db refactoring continues

This commit is contained in:
Chumva 2019-01-28 18:09:35 +02:00
parent 430ceb10ae
commit 256df2c866
9 changed files with 348 additions and 281 deletions

View file

@ -13,6 +13,7 @@ import android.os.*
import android.util.Log
import android.widget.Toast
import net.osmand.PlatformUtil
import net.osmand.telegram.helpers.LocationMessages
import net.osmand.telegram.helpers.LocationMessages.LocationMessage
import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener
import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener
@ -277,7 +278,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
app().showLocationHelper.startShowMessagesTask(chatId, *messages)
messages.forEach {
val locationMessage = OsmandLocationUtils.parseMessage(it, app().telegramHelper, LocationMessage.STATUS_PREPARED)
val locationMessage = OsmandLocationUtils.parseMessage(it, app().telegramHelper, LocationMessages.STATUS_PREPARED)
if (locationMessage != null) {
app().locationMessages.addIngoingMessage(locationMessage)
}

View file

@ -845,13 +845,15 @@ class TelegramSettings(private val app: TelegramApplication) {
var updateTextMessageId = 1
var currentMessageLimit = -1L
var currentMapMessageId = -1L
var oldMapMessageId = -1L
var currentTextMessageId = -1L
var oldTextMessageId = -1L
var userSetLivePeriod = -1L
var userSetLivePeriodStart = -1L
var lastSuccessfulSendTimeMs = -1L
var lastSendTextMessageTime = -1
var lastSendMapMessageTime = -1
var bufferedMessages = 0
var pendingTdLib = 0
var pendingTextMessage = false
var pendingMapMessage = false
var shouldSendViaBotMessage = false

View file

@ -11,7 +11,7 @@ import kotlin.collections.ArrayList
class LocationMessages(val app: TelegramApplication) {
// todo - bufferedMessages is for prepared/pending messages only. On app start we read prepared/pending messages to bufferedMessages. After status changed to sent/error - remove message from buffered.
private var bufferedMessages = emptyList<LocationMessage>()
private var bufferedMessages = emptyList<BufferMessage>()
private val dbHelper: SQLiteHelper
@ -20,27 +20,23 @@ class LocationMessages(val app: TelegramApplication) {
readBufferedMessages()
}
fun getPreparedMessages(): List<LocationMessage> {
val currentUserId = app.telegramHelper.getCurrentUserId()
return bufferedMessages.filter { it.userId == currentUserId && it.status == LocationMessage.STATUS_PREPARED }.sortedBy { it.date }
fun getPreparedMessages(): List<BufferMessage> {
return bufferedMessages.sortedBy { it.date }
}
// todo - drop method. add collected / sent messages count to ShareChatInfo
fun getOutgoingMessages(chatId: Long): List<LocationMessage> {
val currentUserId = app.telegramHelper.getCurrentUserId()
return bufferedMessages.filter { it.userId == currentUserId && it.chatId == chatId }.sortedBy { it.date }
}
// // todo - drop method. add collected / sent messages count to ShareChatInfo
// fun getOutgoingMessages(chatId: Long): List<LocationMessage> {
// val currentUserId = app.telegramHelper.getCurrentUserId()
// return bufferedMessages.filter { it.userId == currentUserId && it.chatId == chatId }.sortedBy { it.date }
// }
// todo - drop it or read from db
fun getSentMessages(chatId: Long, date:Long): List<LocationMessage> {
val currentUserId = app.telegramHelper.getCurrentUserId()
return bufferedMessages.filter { it.userId == currentUserId && it.chatId == chatId && it.date > date }.sortedBy { it.date }
}
// todo - drop it or read from db
fun getSentMessages(chatId: Long): List<LocationMessage> {
val currentUserId = app.telegramHelper.getCurrentUserId()
return bufferedMessages.filter { it.userId == currentUserId && it.chatId == chatId && it.status == LocationMessage.STATUS_SENT }.sortedBy { it.date }
fun updateBufferedMessageStatus(oldMessage: BufferMessage, status: Int){
val messages = mutableListOf(*this.bufferedMessages.toTypedArray())
messages.remove(oldMessage)
val newMessage = BufferMessage(oldMessage.chatId,oldMessage.lat,oldMessage.lon,oldMessage.altitude,oldMessage.speed,oldMessage.hdop,oldMessage.bearing,oldMessage.date,oldMessage.type,status)
messages.add(newMessage)
this.bufferedMessages = messages
// dbHelper.addBufferedMessage(newMessage)
}
// todo - read from db by date (Victor's suggestion - filter by one day only. Need to be changed in UI also.
@ -48,73 +44,78 @@ class LocationMessages(val app: TelegramApplication) {
return emptyList()
}
fun addOutgoingMessage(message: LocationMessage) {
fun addBufferedMessage(message: BufferMessage) {
val messages = mutableListOf(*this.bufferedMessages.toTypedArray())
messages.add(message)
this.bufferedMessages = messages
dbHelper.addOutgoingMessage(message)
dbHelper.addBufferedMessage(message)
}
fun addIngoingMessage(message: LocationMessage) {
dbHelper.addIngoingMessage(message)
}
fun clearOutgoingMessages() {
dbHelper.clearOutgoingMessages()
fun clearBufferedMessages() {
dbHelper.clearBufferedMessages()
bufferedMessages = emptyList()
}
// todo - both methods should be refactored since we have two tables now for outgoing/ingoing messages. Data should be read from db.
fun collectRecordedDataForUser(userId: Int, chatId: Long, start: Long, end: Long): List<LocationMessage> {
return if (chatId == 0L) {
bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter { it.userId == userId && it.date in (start + 1)..(end - 1) }
} else {
bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter {
it.chatId == chatId && it.userId == userId && it.status == LocationMessage.STATUS_SENT && it.date in (start + 1)..(end - 1) }
}
}
fun collectRecordedDataForUsers(start: Long, end: Long, ignoredUsersIds: ArrayList<Int>): List<LocationMessage> {
return bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter {
it.date in (start + 1)..(end - 1) && !ignoredUsersIds.contains(it.userId)
}
}
// fun collectRecordedDataForUser(userId: Int, chatId: Long, start: Long, end: Long): List<LocationMessage> {
// return if (chatId == 0L) {
// bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter { it.userId == userId && it.date in (start + 1)..(end - 1) }
// } else {
// bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter {
// it.chatId == chatId && it.userId == userId&&it.date in (start + 1)..(end - 1) }
// }
// }
//
// fun collectRecordedDataForUsers(start: Long, end: Long, ignoredUsersIds: ArrayList<Int>): List<LocationMessage> {
// return bufferedMessages.sortedWith(compareBy({ it.userId }, { it.chatId })).filter {
// it.date in (start + 1)..(end - 1) && !ignoredUsersIds.contains(it.userId)
// }
// }
private fun readBufferedMessages() {
this.bufferedMessages = dbHelper.getOutgoingMessages();
this.bufferedMessages = dbHelper.getBufferedMessages()
}
private class SQLiteHelper(context: Context) :
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(OUTGOING_TABLE_CREATE)
db.execSQL("CREATE INDEX $DATE_INDEX ON $OUTGOING_TABLE_NAME (\"$COL_DATE\" DESC);")
db.execSQL(INGOING_TABLE_CREATE)
db.execSQL("CREATE INDEX $DATE_INDEX ON $INGOING_TABLE_NAME (\"$COL_DATE\" DESC);")
db.execSQL(TIMELINE_TABLE_CREATE)
db.execSQL("CREATE INDEX $DATE_INDEX ON $TIMELINE_TABLE_NAME (\"$COL_TIME\" DESC);")
db.execSQL(BUFFER_TABLE_CREATE)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL(OUTGOING_TABLE_DELETE)
db.execSQL(INGOING_TABLE_DELETE)
db.execSQL(TIMELINE_TABLE_DELETE)
db.execSQL(BUFFER_TABLE_DELETE)
onCreate(db)
}
internal fun addOutgoingMessage(message: LocationMessage) {
writableDatabase?.execSQL(OUTGOING_TABLE_INSERT,
arrayOf(message.userId, message.chatId, message.lat, message.lon, message.altitude, message.speed,
message.hdop, message.bearing, message.date, message.type, message.status, message.messageId))
internal fun addBufferedMessage(message: BufferMessage) {
writableDatabase?.execSQL(BUFFER_TABLE_INSERT,
arrayOf(message.chatId, message.lat, message.lon, message.altitude, message.speed,
message.hdop, message.bearing, message.date, message.type))
}
internal fun addIngoingMessage(message: LocationMessage) {
writableDatabase?.execSQL(INGOING_TABLE_INSERT,
writableDatabase?.execSQL(TIMELINE_TABLE_INSERT,
arrayOf(message.userId, message.chatId, message.lat, message.lon, message.altitude, message.speed,
message.hdop, message.bearing, message.date, message.type, message.status, message.messageId))
message.hdop, message.bearing, message.date, message.type))
}
internal fun addOutgoingMessage(message: LocationMessage) {
writableDatabase?.execSQL(TIMELINE_TABLE_INSERT,
arrayOf(message.userId, message.chatId, message.lat, message.lon, message.altitude, message.speed,
message.hdop, message.bearing, message.date, message.type))
}
internal fun getOutgoingMessages(): List<LocationMessage> {
val res = arrayListOf<LocationMessage>()
readableDatabase?.rawQuery(OUTGOING_TABLE_SELECT, null)?.apply {
readableDatabase?.rawQuery(TIMELINE_TABLE_SELECT, null)?.apply {
if (moveToFirst()) {
do {
res.add(readLocationMessage(this@apply))
@ -125,6 +126,19 @@ class LocationMessages(val app: TelegramApplication) {
return res
}
internal fun getBufferedMessages(): List<BufferMessage> {
val res = arrayListOf<BufferMessage>()
readableDatabase?.rawQuery(BUFFER_TABLE_SELECT, null)?.apply {
if (moveToFirst()) {
do {
res.add(readBufferMessage(this@apply))
} while (moveToNext())
}
close()
}
return res
}
internal fun readLocationMessage(cursor: Cursor): LocationMessage {
val userId = cursor.getInt(0)
val chatId = cursor.getLong(1)
@ -136,27 +150,40 @@ class LocationMessages(val app: TelegramApplication) {
val bearing = cursor.getDouble(7)
val date = cursor.getLong(8)
val type = cursor.getInt(9)
val status = cursor.getInt(10)
val messageId = cursor.getLong(11)
return LocationMessage(userId, chatId, lat, lon, altitude, speed, hdop, bearing, date, type, status, messageId)
return LocationMessage(userId, chatId, lat, lon, altitude, speed, hdop, bearing, date, type)
}
internal fun clearOutgoingMessages() {
writableDatabase?.execSQL(OUTGOING_TABLE_CLEAR)
internal fun readBufferMessage(cursor: Cursor): BufferMessage {
val chatId = cursor.getLong(1)
val lat = cursor.getDouble(2)
val lon = cursor.getDouble(3)
val altitude = cursor.getDouble(4)
val speed = cursor.getDouble(5)
val hdop = cursor.getDouble(6)
val bearing = cursor.getDouble(7)
val date = cursor.getLong(8)
val type = cursor.getInt(9)
val status = cursor.getInt(10)
return BufferMessage(chatId, lat, lon, altitude, speed, hdop, bearing, date, type,status)
}
internal fun clearBufferedMessages() {
writableDatabase?.execSQL(BUFFER_TABLE_CLEAR)
}
companion object {
private const val DATABASE_NAME = "location_messages"
private const val DATABASE_VERSION = 3
private const val DATABASE_VERSION = 4
private const val OUTGOING_TABLE_NAME = "outgoing"
private const val INGOING_TABLE_NAME = "ingoing"
private const val TIMELINE_TABLE_NAME = "timeline"
private const val BUFFER_TABLE_NAME = "buffer"
private const val COL_USER_ID = "user_id"
private const val COL_CHAT_ID = "chat_id"
private const val COL_DATE = "date"
private const val COL_TIME = "time"
private const val COL_LAT = "lat"
private const val COL_LON = "lon"
private const val COL_ALTITUDE = "altitude"
@ -169,38 +196,49 @@ class LocationMessages(val app: TelegramApplication) {
private const val DATE_INDEX = "date_index"
// Outgoing messages table
private const val OUTGOING_TABLE_INSERT =
("INSERT INTO $OUTGOING_TABLE_NAME ($COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_DATE, $COL_TYPE, $COL_MESSAGE_STATUS, $COL_MESSAGE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
// Timeline messages table
private const val TIMELINE_TABLE_INSERT =
("INSERT INTO $TIMELINE_TABLE_NAME ($COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
private const val OUTGOING_TABLE_CREATE =
("CREATE TABLE IF NOT EXISTS $OUTGOING_TABLE_NAME ($COL_USER_ID long, $COL_CHAT_ID long,$COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_DATE long, $COL_TYPE int, $COL_MESSAGE_STATUS int, $COL_MESSAGE_ID long )")
private const val TIMELINE_TABLE_CREATE =
("CREATE TABLE IF NOT EXISTS $TIMELINE_TABLE_NAME ($COL_USER_ID long, $COL_CHAT_ID long,$COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_TIME long, $COL_TYPE int, $COL_MESSAGE_ID long )")
private const val OUTGOING_TABLE_SELECT =
"SELECT $COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_DATE, $COL_TYPE, $COL_MESSAGE_STATUS, $COL_MESSAGE_ID FROM $OUTGOING_TABLE_NAME"
private const val TIMELINE_TABLE_SELECT =
"SELECT $COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_ID FROM $TIMELINE_TABLE_NAME"
private const val OUTGOING_TABLE_CLEAR = "DELETE FROM $OUTGOING_TABLE_NAME"
private const val TIMELINE_TABLE_CLEAR = "DELETE FROM $TIMELINE_TABLE_NAME"
private const val OUTGOING_TABLE_DELETE = "DROP TABLE IF EXISTS $OUTGOING_TABLE_NAME"
private const val TIMELINE_TABLE_DELETE = "DROP TABLE IF EXISTS $TIMELINE_TABLE_NAME"
// Ingoing messages table
private const val INGOING_TABLE_INSERT =
("INSERT INTO $INGOING_TABLE_NAME ($COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_DATE, $COL_TYPE, $COL_MESSAGE_STATUS, $COL_MESSAGE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
// Buffer messages table
private const val BUFFER_TABLE_INSERT =
("INSERT INTO $BUFFER_TABLE_NAME ($COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_STATUS) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
private const val INGOING_TABLE_CREATE =
("CREATE TABLE IF NOT EXISTS $INGOING_TABLE_NAME ($COL_USER_ID long, $COL_CHAT_ID long,$COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_DATE long, $COL_TYPE int, $COL_MESSAGE_STATUS int, $COL_MESSAGE_ID long )")
private const val BUFFER_TABLE_CREATE =
("CREATE TABLE IF NOT EXISTS $BUFFER_TABLE_NAME ($COL_CHAT_ID long, $COL_LAT double, $COL_LON double, $COL_ALTITUDE double, $COL_SPEED float, $COL_HDOP double, $COL_BEARING double, $COL_TIME long, $COL_TYPE int, $COL_MESSAGE_STATUS int)")
private const val INGOING_TABLE_SELECT =
"SELECT $COL_USER_ID, $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_DATE, $COL_TYPE, $COL_MESSAGE_STATUS, $COL_MESSAGE_ID FROM $INGOING_TABLE_NAME"
private const val BUFFER_TABLE_SELECT =
"SELECT $COL_CHAT_ID, $COL_LAT, $COL_LON, $COL_ALTITUDE, $COL_SPEED, $COL_HDOP, $COL_BEARING, $COL_TIME, $COL_TYPE, $COL_MESSAGE_STATUS FROM $BUFFER_TABLE_NAME"
private const val INGOING_TABLE_CLEAR = "DELETE FROM $INGOING_TABLE_NAME"
private const val BUFFER_TABLE_CLEAR = "DELETE FROM $BUFFER_TABLE_NAME"
private const val INGOING_TABLE_DELETE = "DROP TABLE IF EXISTS $INGOING_TABLE_NAME"
private const val BUFFER_TABLE_DELETE = "DROP TABLE IF EXISTS $BUFFER_TABLE_NAME"
}
}
data class LocationMessage(
val userId: Int,
val chatId: Long,
val lat: Double,
val lon: Double,
val altitude: Double,
val speed: Double,
val hdop: Double,
val bearing: Double,
val date: Long,
val type: Int)
data class BufferMessage (
val chatId: Long,
val lat: Double,
val lon: Double,
@ -211,20 +249,18 @@ class LocationMessages(val app: TelegramApplication) {
val date: Long,
val type: Int,
// todo - status and messageId should be updated in db right away. Make them val instead of var.
var status: Int,
var messageId: Long) {
val status: Int)
companion object {
companion object {
const val STATUS_PREPARED = 0
const val STATUS_PENDING = 1
const val STATUS_SENT = 2
const val STATUS_ERROR = 3
const val STATUS_PREPARED = 0
const val STATUS_PENDING = 1
const val STATUS_SENT = 2
const val STATUS_ERROR = 3
const val TYPE_USER_MAP = 0
const val TYPE_USER_TEXT = 1
const val TYPE_BOT_MAP = 2
const val TYPE_BOT_TEXT = 3
}
const val TYPE_USER_MAP = 0
const val TYPE_USER_TEXT = 1
const val TYPE_BOT_MAP = 2
const val TYPE_BOT_TEXT = 3
}
}

View file

@ -3,7 +3,7 @@ package net.osmand.telegram.helpers
import net.osmand.Location
import net.osmand.PlatformUtil
import net.osmand.telegram.*
import net.osmand.telegram.helpers.LocationMessages.LocationMessage
import net.osmand.telegram.helpers.LocationMessages.BufferMessage
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import net.osmand.telegram.utils.AndroidNetworkUtils
import net.osmand.telegram.utils.BASE_URL
@ -50,8 +50,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
if (location != null) {
if (app.settings.getChatsShareInfo().isNotEmpty()) {
addNewLocationMessages(location, app.telegramHelper.getCurrentUserId())
shareMessages()
shareLocationMessages(location, app.telegramHelper.getCurrentUserId())
}
lastLocationMessageSentTime = System.currentTimeMillis()
}
@ -132,58 +131,45 @@ class ShareLocationHelper(private val app: TelegramApplication) {
refreshNotification()
}
private fun addNewLocationMessages(location: Location, userId: Int) {
private fun shareLocationMessages(location: Location, userId: Int) {
val chatsShareInfo = app.settings.getChatsShareInfo()
val latitude = location.latitude
val longitude = location.longitude
val isBot = app.settings.currentSharingMode != userId.toString()
val sharingMode = app.settings.currentSharingMode
val isBot = sharingMode != userId.toString()
val types = mutableListOf<Int>()
var bufferedMessagesFull = false
when (app.settings.shareTypeValue) {
SHARE_TYPE_MAP -> {
types.add(if (isBot) LocationMessage.TYPE_BOT_MAP else LocationMessage.TYPE_USER_MAP)
types.add(if (isBot) LocationMessages.TYPE_BOT_MAP else LocationMessages.TYPE_USER_MAP)
}
SHARE_TYPE_TEXT -> {
types.add(if (isBot) LocationMessage.TYPE_BOT_TEXT else LocationMessage.TYPE_USER_TEXT)
types.add(if (isBot) LocationMessages.TYPE_BOT_TEXT else LocationMessages.TYPE_USER_TEXT)
}
SHARE_TYPE_MAP_AND_TEXT -> {
types.add(if (isBot) LocationMessage.TYPE_BOT_MAP else LocationMessage.TYPE_USER_MAP)
types.add(if (isBot) LocationMessage.TYPE_BOT_TEXT else LocationMessage.TYPE_USER_TEXT)
types.add(if (isBot) LocationMessages.TYPE_BOT_MAP else LocationMessages.TYPE_USER_MAP)
types.add(if (isBot) LocationMessages.TYPE_BOT_TEXT else LocationMessages.TYPE_USER_TEXT)
}
}
chatsShareInfo.values.forEach { shareInfo ->
types.forEach {
val message = LocationMessage(userId, shareInfo.chatId, latitude, longitude, location.altitude, location.speed.toDouble(),
location.accuracy.toDouble(), location.bearing.toDouble(), location.time, it, LocationMessage.STATUS_PREPARED, shareInfo.currentMapMessageId)
app.locationMessages.addOutgoingMessage(message)
if (shareInfo.pendingTdLib >= 10) {
bufferedMessagesFull = true
}
}
}
types.forEach {
val message = BufferMessage(shareInfo.chatId, latitude, longitude, location.altitude, location.speed.toDouble(),
location.accuracy.toDouble(), location.bearing.toDouble(), location.time, it, LocationMessages.STATUS_PREPARED)
private fun shareMessages() {
var bufferedMessagesFull = false
app.locationMessages.getPreparedMessages().forEach {
val shareChatInfo = app.settings.getChatsShareInfo()[it.chatId]
if (shareChatInfo != null) {
bufferedMessagesFull = shareChatInfo.bufferedMessages < 10
if (bufferedMessagesFull) {
when {
it.type == LocationMessage.TYPE_USER_TEXT -> {
shareChatInfo.bufferedMessages++
it.status = LocationMessage.STATUS_PENDING
app.telegramHelper.sendLiveLocationText(shareChatInfo, it)
}
it.type == LocationMessage.TYPE_USER_MAP -> {
shareChatInfo.bufferedMessages++
it.status = LocationMessage.STATUS_PENDING
app.telegramHelper.sendLiveLocationMap(shareChatInfo, it)
}
it.type == LocationMessage.TYPE_BOT_TEXT -> {
sendLocationToBot(it, app.settings.currentSharingMode, shareChatInfo, SHARE_TYPE_TEXT)
}
it.type == LocationMessage.TYPE_BOT_MAP -> {
sendLocationToBot(it, app.settings.currentSharingMode, shareChatInfo, SHARE_TYPE_MAP)
}
when (app.settings.shareTypeValue) {
SHARE_TYPE_MAP -> {
prepareMapMessage(shareInfo, message, isBot, sharingMode)
}
SHARE_TYPE_TEXT -> {
prepareTextMessage(shareInfo, message, isBot, sharingMode)
}
SHARE_TYPE_MAP_AND_TEXT -> {
prepareMapMessage(shareInfo, message, isBot, sharingMode)
prepareTextMessage(shareInfo, message, isBot, sharingMode)
}
}
}
@ -193,6 +179,58 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
}
private fun prepareTextMessage(shareInfo: TelegramSettings.ShareChatInfo,message: BufferMessage,isBot:Boolean, sharingMode: String) {
if (shareInfo.currentTextMessageId == -1L) {
if (shareInfo.pendingTextMessage) {
app.locationMessages.addBufferedMessage(message)
} else {
if (isBot) {
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_TEXT)
} else {
shareInfo.pendingTdLib++
app.telegramHelper.sendNewTextLocation(shareInfo, message)
}
}
} else {
if (isBot) {
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_TEXT)
} else {
if (shareInfo.pendingTdLib < 10) {
shareInfo.pendingTdLib++
app.telegramHelper.editTextLocation(shareInfo, message)
} else {
app.locationMessages.addBufferedMessage(message)
}
}
}
}
private fun prepareMapMessage(shareInfo: TelegramSettings.ShareChatInfo,message: BufferMessage,isBot:Boolean, sharingMode: String) {
if (shareInfo.currentMapMessageId == -1L) {
if (shareInfo.pendingMapMessage) {
app.locationMessages.addBufferedMessage(message)
} else {
if (isBot) {
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_MAP)
} else {
shareInfo.pendingTdLib++
app.telegramHelper.sendNewMapLocation(shareInfo, message)
}
}
} else {
if (isBot) {
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_MAP)
} else {
if (shareInfo.pendingTdLib < 10) {
shareInfo.pendingTdLib++
app.telegramHelper.editMapLocation(shareInfo, message)
} else {
app.locationMessages.addBufferedMessage(message)
}
}
}
}
private fun checkNetworkType(){
if (app.isInternetConnectionAvailable) {
val networkType = when {
@ -204,9 +242,9 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
}
private fun sendLocationToBot(locationMessage: LocationMessage, sharingMode: String, shareInfo: TelegramSettings.ShareChatInfo, shareType: String) {
private fun sendLocationToBot(locationMessage: BufferMessage, sharingMode: String, shareInfo: TelegramSettings.ShareChatInfo, shareType: String) {
if (app.isInternetConnectionAvailable) {
locationMessage.status = LocationMessage.STATUS_PENDING
// locationMessage.status = LocationMessage.STATUS_PENDING
val url = getDeviceSharingUrl(locationMessage, sharingMode)
AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false,
object : AndroidNetworkUtils.OnRequestResultListener {
@ -216,7 +254,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
val osmandBotId = app.telegramHelper.getOsmandBot()?.id ?: -1
val device = app.settings.getCurrentSharingDevice()
locationMessage.status = if (success) LocationMessage.STATUS_SENT else LocationMessage.STATUS_ERROR
// locationMessage.status = if (success) LocationMessage.STATUS_SENT else LocationMessage.STATUS_ERROR
if (success && shareInfo.shouldSendViaBotMessage && osmandBotId != -1 && device != null) {
app.telegramHelper.sendViaBotLocationMessage(osmandBotId, shareInfo, TdApi.Location(locationMessage.lat, locationMessage.lon), device, shareType)
shareInfo.shouldSendViaBotMessage = false
@ -226,7 +264,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
}
private fun getDeviceSharingUrl(loc: LocationMessage, sharingMode: String): String {
private fun getDeviceSharingUrl(loc: BufferMessage, sharingMode: String): String {
val url = "$BASE_URL/device/$sharingMode/send?lat=${loc.lat}&lon=${loc.lon}"
val builder = StringBuilder(url)
if (loc.bearing != 0.0) {

View file

@ -761,27 +761,6 @@ class TelegramHelper private constructor() {
}
}
/**
* @chatId Id of the chat
* @livePeriod Period for which the location can be updated, in seconds; should be between 60 and 86400 for a live location and 0 otherwise.
* @latitude Latitude of the location
* @longitude Longitude of the location
*/
fun sendLiveLocationMessage(chatsShareInfo:Map<Long, TelegramSettings.ShareChatInfo>, latitude: Double, longitude: Double): Boolean {
if (!requestingActiveLiveLocationMessages && haveAuthorization) {
if (needRefreshActiveLiveLocationMessages) {
getActiveLiveLocationMessages {
sendLiveLocationImpl(chatsShareInfo, latitude, longitude)
}
needRefreshActiveLiveLocationMessages = false
} else {
sendLiveLocationImpl(chatsShareInfo, latitude, longitude)
}
return true
}
return false
}
fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) {
if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) {
client?.send(
@ -831,7 +810,7 @@ class TelegramHelper private constructor() {
private fun recreateLiveLocationMessage(
shareInfo: TelegramSettings.ShareChatInfo,
content: TdApi.InputMessageContent,locationMessage: LocationMessages.LocationMessage?
content: TdApi.InputMessageContent,locationMessage: LocationMessages.BufferMessage?
) {
if (shareInfo.chatId != -1L) {
val array = LongArray(1)
@ -861,106 +840,68 @@ class TelegramHelper private constructor() {
needRefreshActiveLiveLocationMessages = true
}
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent, locationMessage: LocationMessages.LocationMessage?) {
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent, locationMessage: LocationMessages.BufferMessage?) {
needRefreshActiveLiveLocationMessages = true
log.debug("sendNewLiveLocationMessage")
client?.send(
TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
handleMapLocationMessageUpdate(obj, shareInfo, locationMessage)
}
}
private fun sendLiveLocationImpl(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, latitude: Double, longitude: Double) {
val location = TdApi.Location(latitude, longitude)
chatsShareInfo.forEach { (chatId, shareInfo) ->
if (shareInfo.getChatLiveMessageExpireTime() <= 0) {
return@forEach
}
val livePeriod =
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
} else {
shareInfo.livePeriod.toInt()
}
val content = TdApi.InputMessageLocation(location, livePeriod)
val msgId = shareInfo.currentMapMessageId
val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendMapMessageTime)
log.debug("sendLiveLocationImpl - $msgId pendingMapMessage ${shareInfo.pendingMapMessage}")
if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousMapMessage) {
recreateLiveLocationMessage(shareInfo, content, null)
shareInfo.shouldDeletePreviousMapMessage = false
shareInfo.currentMapMessageId = -1
} else {
log.debug("EditMessageLiveLocation - $msgId")
client?.send(
TdApi.EditMessageLiveLocation(chatId, msgId, null, location)) { obj ->
handleMapLocationMessageUpdate(obj, shareInfo, null)
}
}
} else if (!shareInfo.pendingMapMessage || shareInfo.pendingMapMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) {
sendNewLiveLocationMessage(shareInfo, content, null)
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
if (locationMessage?.type == LocationMessages.TYPE_USER_TEXT || locationMessage?.type == LocationMessages.TYPE_BOT_TEXT) {
handleTextLocationMessageUpdate(obj, shareInfo, locationMessage)
} else if (locationMessage?.type == LocationMessages.TYPE_USER_MAP || locationMessage?.type == LocationMessages.TYPE_BOT_MAP) {
handleMapLocationMessageUpdate(obj, shareInfo, locationMessage)
}
}
}
fun sendLiveLocationMap(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.LocationMessage) {
fun sendNewTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) {
shareInfo.updateTextMessageId = 1
val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location)
if (!shareInfo.pendingTextMessage) {
shareInfo.pendingTextMessage = true
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
handleTextLocationMessageUpdate(obj, shareInfo, location)
}
}
}
fun editTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) {
val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location)
if (shareInfo.currentTextMessageId!=-1L) {
client?.send(TdApi.EditMessageText(shareInfo.chatId, shareInfo.currentTextMessageId, null, content)) { obj ->
handleTextLocationMessageUpdate(obj, shareInfo, location)
}
}
}
fun sendNewMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) {
needRefreshActiveLiveLocationMessages = true
val location = TdApi.Location(locationMessage.lat, locationMessage.lon)
val livePeriod = if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
val livePeriod =
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
} else {
shareInfo.livePeriod.toInt()
}
val content = TdApi.InputMessageLocation(location, livePeriod)
val msgId = shareInfo.currentMapMessageId
val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendMapMessageTime)
log.debug("sendLiveLocationImpl - $msgId pendingMapMessage ${shareInfo.pendingMapMessage}")
if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousMapMessage) {
recreateLiveLocationMessage(shareInfo, content, locationMessage)
shareInfo.shouldDeletePreviousMapMessage = false
shareInfo.currentMapMessageId = -1
} else {
log.debug("EditMessageLiveLocation - $msgId")
client?.send(
TdApi.EditMessageLiveLocation(shareInfo.chatId, msgId, null, location)) { obj ->
handleMapLocationMessageUpdate(obj, shareInfo, locationMessage)
}
}
} else if (!shareInfo.pendingMapMessage || shareInfo.pendingMapMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) {
sendNewLiveLocationMessage(shareInfo, content, locationMessage)
}
}
fun sendLiveLocationText(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.LocationMessage) {
val msgId = shareInfo.currentTextMessageId
if (msgId == -1L) {
shareInfo.updateTextMessageId = 1
}
val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location)
val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendTextMessageTime)
log.debug("sendLiveLocationText - $msgId pendingMapMessage ${shareInfo.pendingTextMessage}")
if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousTextMessage) {
recreateLiveLocationMessage(shareInfo, content, location)
shareInfo.shouldDeletePreviousTextMessage = false
} else {
client?.send(TdApi.EditMessageText(shareInfo.chatId, msgId, null, content)) { obj ->
handleTextLocationMessageUpdate(obj, shareInfo, location)
}
}
} else {
if (!shareInfo.pendingTextMessage) {
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, false, null, content)) { obj ->
handleTextLocationMessageUpdate(obj, shareInfo, location)
}
} else if(timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC){
if (!shareInfo.pendingMapMessage) {
shareInfo.pendingMapMessage = true
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
handleMapLocationMessageUpdate(obj, shareInfo, locationMessage)
}
}
}
private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.LocationMessage?) {
fun editMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) {
needRefreshActiveLiveLocationMessages = true
val location = TdApi.Location(locationMessage.lat, locationMessage.lon)
if (shareInfo.currentMapMessageId!=-1L) {
client?.send(
TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, location)) { obj ->
handleMapLocationMessageUpdate(obj, shareInfo, locationMessage)
}
}
}
private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage?) {
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
@ -980,7 +921,8 @@ class TelegramHelper private constructor() {
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
shareInfo.hasSharingError = true
needRefreshActiveLiveLocationMessages = true
location?.status = LocationMessages.LocationMessage.STATUS_ERROR
shareInfo.pendingMapMessage = false
// location?.status = LocationMessages.LocationMessage.STATUS_ERROR
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(-1, "Map location message ${obj.id} failed to send")
}
@ -988,14 +930,15 @@ class TelegramHelper private constructor() {
obj.sendingState?.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR -> {
shareInfo.pendingMapMessage = true
shareInfo.lastSendMapMessageTime = obj.date
location?.status = LocationMessages.LocationMessage.STATUS_PENDING
// location?.status = LocationMessages.LocationMessage.STATUS_PENDING
log.debug("handleMapLocationMessageUpdate - MessageSendingStatePending")
}
else -> {
shareInfo.hasSharingError = false
shareInfo.bufferedMessages--
location?.messageId = obj.id
location?.status = LocationMessages.LocationMessage.STATUS_SENT
shareInfo.pendingTdLib--
shareInfo.pendingMapMessage = false
// location?.messageId = obj.id
// location?.status = LocationMessages.LocationMessage.STATUS_SENT
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(obj))
}
@ -1006,7 +949,7 @@ class TelegramHelper private constructor() {
}
}
private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.LocationMessage?) {
private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage?) {
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
@ -1024,9 +967,10 @@ class TelegramHelper private constructor() {
when {
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
shareInfo.hasSharingError = true
shareInfo.bufferedMessages--
shareInfo.pendingTdLib--
shareInfo.pendingTextMessage = false
needRefreshActiveLiveLocationMessages = true
location?.status = LocationMessages.LocationMessage.STATUS_ERROR
// location?.status = LocationMessages.LocationMessage.STATUS_ERROR
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(-1, "Text location message ${obj.id} failed to send")
}
@ -1034,14 +978,15 @@ class TelegramHelper private constructor() {
obj.sendingState?.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR -> {
shareInfo.pendingTextMessage = true
shareInfo.lastSendTextMessageTime = obj.date
location?.status = LocationMessages.LocationMessage.STATUS_PENDING
// location?.status = LocationMessages.LocationMessage.STATUS_PENDING
log.debug("handleTextLocationMessageUpdate - MessageSendingStatePending")
}
else -> {
shareInfo.hasSharingError = false
shareInfo.bufferedMessages--
location?.messageId = obj.id
location?.status = LocationMessages.LocationMessage.STATUS_SENT
shareInfo.pendingTdLib--
shareInfo.pendingTextMessage = false
// location?.messageId = obj.id
// location?.status = LocationMessages.LocationMessage.STATUS_SENT
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(obj))
}

View file

@ -19,6 +19,7 @@ import android.widget.*
import net.osmand.PlatformUtil
import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.helpers.LocationMessages
import net.osmand.telegram.helpers.LocationMessages.LocationMessage
import net.osmand.telegram.helpers.OsmandAidlHelper
import net.osmand.telegram.helpers.TelegramHelper
@ -293,7 +294,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
}
if (app.telegramService == null) {
messages.forEach {
val locationMessage = OsmandLocationUtils.parseMessage(it, telegramHelper, LocationMessage.STATUS_PREPARED)
val locationMessage = OsmandLocationUtils.parseMessage(it, telegramHelper, LocationMessages.STATUS_PREPARED)
if (locationMessage != null) {
app.locationMessages.addIngoingMessage(locationMessage)
}
@ -349,8 +350,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
fun logoutTelegram(silent: Boolean = false) {
if (telegramHelper.getTelegramAuthorizationState() == TelegramHelper.TelegramAuthorizationState.READY) {
if (app.isInternetConnectionAvailable) {
// todo - why delete messages on logout?
app.locationMessages.clearOutgoingMessages()
app.locationMessages.clearBufferedMessages()
settings.clear()
telegramHelper.logout()
} else {

View file

@ -190,22 +190,22 @@ class TimelineTabFragment : Fragment() {
val res = mutableListOf<ListItem>()
val ignoredUsersIds = ArrayList<Int>()
val currentUserId = telegramHelper.getCurrentUser()?.id
if (currentUserId != null) {
val locationMessages = app.locationMessages.collectRecordedDataForUser(currentUserId, 0, start, end)
// todo - why do we need convert to gpx on update? Is locationMessages not enough to display info?
OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages, false).forEach {
TelegramUiHelper.gpxToChatItem(telegramHelper, it, true)?.also { chatItem ->
res.add(chatItem)
}
}
ignoredUsersIds.add(currentUserId)
}
val locationMessages = app.locationMessages.collectRecordedDataForUsers(start, end, ignoredUsersIds)
OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages).forEach {
TelegramUiHelper.gpxToChatItem(telegramHelper, it,false)?.also { chatItem ->
res.add(chatItem)
}
}
// if (currentUserId != null) {
// val locationMessages = app.locationMessages.collectRecordedDataForUser(currentUserId, 0, start, end)
// // todo - why do we need convert to gpx on update? Is locationMessages not enough to display info?
// OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages, false).forEach {
// TelegramUiHelper.gpxToChatItem(telegramHelper, it, true)?.also { chatItem ->
// res.add(chatItem)
// }
// }
// ignoredUsersIds.add(currentUserId)
// }
// val locationMessages = app.locationMessages.collectRecordedDataForUsers(start, end, ignoredUsersIds)
// OsmandLocationUtils.convertLocationMessagesToGpxFiles(locationMessages).forEach {
// TelegramUiHelper.gpxToChatItem(telegramHelper, it,false)?.also { chatItem ->
// res.add(chatItem)
// }
// }
adapter.items = sortAdapterItems(res)
}

View file

@ -207,14 +207,14 @@ class UserGpxInfoFragment : BaseDialogFragment() {
}
private fun updateGpxInfo() {
gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(
app.locationMessages.collectRecordedDataForUser(
gpxFile.userId,
gpxFile.chatId,
startCalendar.timeInMillis,
endCalendar.timeInMillis
)
).first()
// gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(
// app.locationMessages.collectRecordedDataForUser(
// gpxFile.userId,
// gpxFile.chatId,
// startCalendar.timeInMillis,
// endCalendar.timeInMillis
// )
// ).first()
updateGPXStatisticRow()
updateDateAndTimeButtons()
updateGPXMap()

View file

@ -3,7 +3,9 @@ package net.osmand.telegram.utils
import android.os.AsyncTask
import net.osmand.Location
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.helpers.LocationMessages
import net.osmand.telegram.helpers.LocationMessages.LocationMessage
import net.osmand.telegram.helpers.LocationMessages.BufferMessage
import net.osmand.telegram.helpers.TelegramHelper
import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.util.GeoPointParserUtil
@ -108,7 +110,7 @@ object OsmandLocationUtils {
if (parsedMessageContent != null) {
locationMessage = LocationMessage(helper.getSenderMessageId(message), message.chatId, parsedMessageContent.lat,
parsedMessageContent.lon, parsedMessageContent.altitude, parsedMessageContent.speed, parsedMessageContent.hdop,
parsedMessageContent.bearing, parsedMessageContent.lastUpdated * 1000L, messageType, status, message.id)
parsedMessageContent.bearing, parsedMessageContent.lastUpdated * 1000L, messageType)
}
return locationMessage
}
@ -121,6 +123,10 @@ object OsmandLocationUtils {
return String.format(Locale.US, "%.5f, %.5f", sig.lat, sig.lon)
}
fun formatLocation(sig: BufferMessage): String {
return String.format(Locale.US, "%.5f, %.5f", sig.lat, sig.lon)
}
fun formatFullTime(ti: Long): String {
val dt = Date(ti)
return UTC_DATE_FORMAT.format(dt) + " " + UTC_TIME_FORMAT.format(dt) + " UTC"
@ -313,6 +319,45 @@ object OsmandLocationUtils {
return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true)
}
fun getTextMessageContent(updateId: Int, location: BufferMessage): TdApi.InputMessageText {
val entities = mutableListOf<TdApi.TextEntity>()
val builder = StringBuilder()
val locationMessage = formatLocation(location)
val firstSpace = USER_TEXT_LOCATION_TITLE.indexOf(' ')
val secondSpace = USER_TEXT_LOCATION_TITLE.indexOf(' ', firstSpace + 1)
entities.add(TdApi.TextEntity(builder.length + firstSpace + 1, secondSpace - firstSpace, TdApi.TextEntityTypeTextUrl(SHARING_LINK)))
builder.append("$USER_TEXT_LOCATION_TITLE\n")
entities.add(TdApi.TextEntity(builder.lastIndex, LOCATION_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(LOCATION_PREFIX)
entities.add(TdApi.TextEntity(builder.length, locationMessage.length,
TdApi.TextEntityTypeTextUrl("$BASE_SHARING_URL?lat=${location.lat}&lon=${location.lon}")))
builder.append("$locationMessage\n")
if (location.altitude != 0.0) {
entities.add(TdApi.TextEntity(builder.lastIndex, ALTITUDE_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$ALTITUDE_PREFIX%.1f m\n", location.altitude))
}
if (location.speed > 0) {
entities.add(TdApi.TextEntity(builder.lastIndex, SPEED_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$SPEED_PREFIX%.1f m/s\n", location.speed))
}
if (location.hdop != 0.0 && location.speed == 0.0) {
entities.add(TdApi.TextEntity(builder.lastIndex, HDOP_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$HDOP_PREFIX%d m\n", location.hdop.toInt()))
}
if (updateId == 0) {
builder.append(String.format("$UPDATED_PREFIX%s\n", formatFullTime(location.date)))
} else {
builder.append(String.format("$UPDATED_PREFIX%s (%d)\n", formatFullTime(location.date), updateId))
}
val textMessage = builder.toString().trim()
return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true)
}
fun convertLocationMessagesToGpxFiles(items: List<LocationMessage>, newGpxPerChat: Boolean = true): List<GPXUtilities.GPXFile> {
val dataTracks = ArrayList<GPXUtilities.GPXFile>()