Merge pull request #6475 from osmandapp/TelegramDataBase
Telegram database refactoring
This commit is contained in:
commit
3a61240951
21 changed files with 1577 additions and 1221 deletions
|
@ -135,7 +135,7 @@ dependencies {
|
|||
implementation project(path: ':OsmAnd-java', configuration: 'android')
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation( "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") {
|
||||
implementation( "org.jetbrains.kotlin:kotlin-stdlib:1.2.71") {
|
||||
exclude group: 'org.jetbrains', module: 'annotations'
|
||||
}
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
android:layout_height="1dp"
|
||||
android:background="@color/app_bar_divider" />
|
||||
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:id="@+id/date_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/list_header_height"
|
||||
|
@ -106,13 +106,12 @@
|
|||
android:paddingRight="@dimen/content_padding_standard">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/date_start_btn"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/date_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dialog_button_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="@dimen/content_padding_half"
|
||||
android:layout_marginRight="@dimen/content_padding_half"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/btn_round_border"
|
||||
android:drawablePadding="@dimen/content_padding_half"
|
||||
android:ellipsize="end"
|
||||
|
@ -120,38 +119,12 @@
|
|||
android:maxLines="1"
|
||||
android:paddingLeft="@dimen/image_button_padding"
|
||||
android:paddingRight="@dimen/image_button_padding"
|
||||
android:text="@string/start_date"
|
||||
android:text="@string/shared_string_date"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/text_button_text_size"
|
||||
app:typeface="@string/font_roboto_medium" />
|
||||
|
||||
<View
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@color/app_bar_divider" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/date_end_btn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dialog_button_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="@dimen/content_padding_half"
|
||||
android:layout_marginStart="@dimen/content_padding_half"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/btn_round_border"
|
||||
android:drawablePadding="@dimen/content_padding_half"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start|center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="@dimen/image_button_padding"
|
||||
android:paddingRight="@dimen/image_button_padding"
|
||||
android:text="@string/end_date"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/text_button_text_size"
|
||||
app:typeface="@string/font_roboto_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
|
|
|
@ -171,6 +171,63 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/gps_points"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_regular"
|
||||
android:text="@string/gps_points" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/gps_points_collected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_mono_bold" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_regular"
|
||||
android:text="@string/shared_string_collected" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/gps_points_sent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_mono_bold" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
android:text="@string/shared_string_sent"
|
||||
app:typeface="@string/font_roboto_regular" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<resources>
|
||||
<string name="points_size">%1$d points</string>
|
||||
<string name="shared_string_date">Date</string>
|
||||
<string name="shared_string_collected">Collected</string>
|
||||
<string name="gps_points">Gps points</string>
|
||||
<string name="shared_string_sent">Sent</string>
|
||||
<string name="monitoring_is_enabled">Monitoring is enabled</string>
|
||||
<string name="monitoring_is_disabled">Monitoring is disabled</string>
|
||||
<string name="time_on_the_move">time on the move</string>
|
||||
|
|
|
@ -24,8 +24,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
|||
lateinit var notificationHelper: NotificationHelper private set
|
||||
lateinit var osmandAidlHelper: OsmandAidlHelper private set
|
||||
lateinit var locationProvider: TelegramLocationProvider private set
|
||||
lateinit var messagesDbHelper: MessagesDbHelper private set
|
||||
lateinit var savingTracksDbHelper: SavingTracksDbHelper private set
|
||||
lateinit var locationMessages: LocationMessages private set
|
||||
|
||||
var telegramService: TelegramService? = null
|
||||
|
||||
|
@ -68,8 +67,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
|||
showLocationHelper = ShowLocationHelper(this)
|
||||
notificationHelper = NotificationHelper(this)
|
||||
locationProvider = TelegramLocationProvider(this)
|
||||
messagesDbHelper = MessagesDbHelper(this)
|
||||
savingTracksDbHelper = SavingTracksDbHelper(this)
|
||||
locationMessages = LocationMessages(this)
|
||||
|
||||
if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) {
|
||||
shareLocationHelper.startSharingLocation()
|
||||
|
@ -96,6 +94,13 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
|||
return ni != null && ni.type == ConnectivityManager.TYPE_WIFI
|
||||
}
|
||||
|
||||
val isMobileConnected: Boolean
|
||||
get() {
|
||||
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val ni = mgr.activeNetworkInfo
|
||||
return ni != null && ni.type == ConnectivityManager.TYPE_MOBILE
|
||||
}
|
||||
|
||||
private val isInternetConnected: Boolean
|
||||
get() {
|
||||
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
|
|
@ -13,10 +13,11 @@ import android.os.*
|
|||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import net.osmand.PlatformUtil
|
||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener
|
||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener
|
||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener
|
||||
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
import java.util.*
|
||||
|
||||
|
@ -274,6 +275,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
|||
|
||||
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
|
||||
app().showLocationHelper.startShowMessagesTask(chatId, *messages)
|
||||
messages.forEach {
|
||||
app().locationMessages.addNewLocationMessage(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {
|
||||
|
@ -287,6 +291,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
|||
override fun onUpdateMessages(messages: List<TdApi.Message>) {
|
||||
messages.forEach {
|
||||
app().settings.updateShareInfo(it)
|
||||
app().shareLocationHelper.checkAndSendBufferMessagesToChat(it.chatId)
|
||||
if (it.sendingState == null && (it.content is TdApi.MessageLocation || it.content is TdApi.MessageText)) {
|
||||
app().locationMessages.addNewLocationMessage(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.support.annotation.DrawableRes
|
|||
import android.support.annotation.StringRes
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import net.osmand.PlatformUtil
|
||||
import net.osmand.telegram.helpers.OsmandAidlHelper
|
||||
import net.osmand.telegram.helpers.TelegramHelper
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
|
@ -87,6 +88,8 @@ private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes
|
|||
|
||||
class TelegramSettings(private val app: TelegramApplication) {
|
||||
|
||||
private val log = PlatformUtil.getLog(TelegramSettings::class.java)
|
||||
|
||||
private var shareChatsInfo = ConcurrentHashMap<Long, ShareChatInfo>()
|
||||
private var hiddenOnMapChats: Set<Long> = emptySet()
|
||||
private var shareDevices: Set<DeviceBot> = emptySet()
|
||||
|
@ -242,21 +245,56 @@ class TelegramSettings(private val app: TelegramApplication) {
|
|||
}
|
||||
|
||||
fun updateShareInfo(message: TdApi.Message) {
|
||||
val shareChatInfo = shareChatsInfo[message.chatId]
|
||||
val shareInfo = shareChatsInfo[message.chatId]
|
||||
val content = message.content
|
||||
if (shareChatInfo != null) {
|
||||
if (shareInfo != null) {
|
||||
when (content) {
|
||||
is TdApi.MessageLocation -> {
|
||||
shareChatInfo.currentMapMessageId = message.id
|
||||
shareChatInfo.pendingMapMessage = false
|
||||
val state = message.sendingState
|
||||
if (state != null) {
|
||||
if (state.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR) {
|
||||
shareInfo.pendingMapMessage = true
|
||||
log.debug("updateShareInfo MAP ${message.id} MessageSendingStatePending")
|
||||
shareInfo.oldMapMessageId = message.id
|
||||
} else if (state.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) {
|
||||
shareInfo.hasSharingError = true
|
||||
shareInfo.pendingMapMessage = false
|
||||
log.debug("updateShareInfo MAP ${message.id} MessageSendingStateFailed")
|
||||
}
|
||||
} else {
|
||||
shareInfo.currentMapMessageId = message.id
|
||||
shareInfo.pendingMapMessage = false
|
||||
shareInfo.pendingTdLib--
|
||||
shareInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L
|
||||
if (shareTypeValue == SHARE_TYPE_MAP) {
|
||||
shareInfo.sentMessages++
|
||||
}
|
||||
log.debug("updateShareInfo MAP ${message.id} SUCCESS pendingTdLib: ${shareInfo.pendingTdLib}")
|
||||
}
|
||||
}
|
||||
is TdApi.MessageText -> {
|
||||
shareChatInfo.currentTextMessageId = message.id
|
||||
shareChatInfo.updateTextMessageId++
|
||||
shareChatInfo.pendingTextMessage = false
|
||||
val state = message.sendingState
|
||||
if (state != null) {
|
||||
if (state.constructor == TdApi.MessageSendingStatePending.CONSTRUCTOR) {
|
||||
log.debug("updateShareInfo TEXT ${message.id} MessageSendingStatePending")
|
||||
shareInfo.pendingTextMessage = true
|
||||
shareInfo.oldTextMessageId = message.id
|
||||
} else if (state.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) {
|
||||
log.debug("updateShareInfo TEXT ${message.id} MessageSendingStateFailed")
|
||||
shareInfo.hasSharingError = true
|
||||
shareInfo.pendingTextMessage = false
|
||||
}
|
||||
} else {
|
||||
shareInfo.currentTextMessageId = message.id
|
||||
shareInfo.updateTextMessageId++
|
||||
shareInfo.pendingTextMessage = false
|
||||
shareInfo.pendingTdLib--
|
||||
shareInfo.sentMessages++
|
||||
shareInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L
|
||||
log.debug("updateShareInfo TEXT ${message.id} SUCCESS pendingTdLib: ${shareInfo.pendingTdLib}")
|
||||
}
|
||||
}
|
||||
}
|
||||
shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,6 +578,11 @@ class TelegramSettings(private val app: TelegramApplication) {
|
|||
obj.put(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastSuccessfulSendTimeMs)
|
||||
obj.put(ShareChatInfo.LAST_SEND_MAP_TIME_KEY, chatInfo.lastSendMapMessageTime)
|
||||
obj.put(ShareChatInfo.LAST_SEND_TEXT_TIME_KEY, chatInfo.lastSendTextMessageTime)
|
||||
obj.put(ShareChatInfo.PENDING_TEXT_MESSAGE_KEY, chatInfo.pendingTextMessage)
|
||||
obj.put(ShareChatInfo.PENDING_MAP_MESSAGE_KEY, chatInfo.pendingMapMessage)
|
||||
obj.put(ShareChatInfo.COLLECTED_MESSAGES_KEY, chatInfo.collectedMessages)
|
||||
obj.put(ShareChatInfo.SENT_MESSAGES_KEY, chatInfo.sentMessages)
|
||||
obj.put(ShareChatInfo.PENDING_TDLIB_KEY, chatInfo.pendingTdLib)
|
||||
jArray.put(obj)
|
||||
}
|
||||
jArray
|
||||
|
@ -566,6 +609,11 @@ class TelegramSettings(private val app: TelegramApplication) {
|
|||
lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY)
|
||||
lastSendMapMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_MAP_TIME_KEY)
|
||||
lastSendTextMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_TEXT_TIME_KEY)
|
||||
pendingTextMessage = obj.optBoolean(ShareChatInfo.PENDING_TEXT_MESSAGE_KEY)
|
||||
pendingMapMessage = obj.optBoolean(ShareChatInfo.PENDING_MAP_MESSAGE_KEY)
|
||||
collectedMessages = obj.optInt(ShareChatInfo.COLLECTED_MESSAGES_KEY)
|
||||
sentMessages = obj.optInt(ShareChatInfo.SENT_MESSAGES_KEY)
|
||||
pendingTdLib = obj.optInt(ShareChatInfo.PENDING_TDLIB_KEY)
|
||||
}
|
||||
shareChatsInfo[shareInfo.chatId] = shareInfo
|
||||
}
|
||||
|
@ -845,12 +893,17 @@ 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 collectedMessages = 0
|
||||
var sentMessages = 0
|
||||
var pendingTdLib = 0
|
||||
var pendingTextMessage = false
|
||||
var pendingMapMessage = false
|
||||
var shouldSendViaBotMessage = false
|
||||
|
@ -887,6 +940,11 @@ class TelegramSettings(private val app: TelegramApplication) {
|
|||
internal const val LAST_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime"
|
||||
internal const val LAST_SEND_MAP_TIME_KEY = "lastSendMapMessageTime"
|
||||
internal const val LAST_SEND_TEXT_TIME_KEY = "lastSendTextMessageTime"
|
||||
internal const val PENDING_TEXT_MESSAGE_KEY = "pendingTextMessage"
|
||||
internal const val PENDING_MAP_MESSAGE_KEY = "pendingMapMessage"
|
||||
internal const val COLLECTED_MESSAGES_KEY = "collectedMessages"
|
||||
internal const val SENT_MESSAGES_KEY = "sentMessages"
|
||||
internal const val PENDING_TDLIB_KEY = "sentMessages"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
package net.osmand.telegram.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import net.osmand.PlatformUtil
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
|
||||
class LocationMessages(val app: TelegramApplication) {
|
||||
|
||||
private val log = PlatformUtil.getLog(LocationMessages::class.java)
|
||||
|
||||
// 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<BufferMessage>()
|
||||
|
||||
private var lastMessages = emptyList<LocationMessage>()
|
||||
|
||||
private val dbHelper: SQLiteHelper
|
||||
|
||||
init {
|
||||
dbHelper = SQLiteHelper(app)
|
||||
readBufferedMessages()
|
||||
}
|
||||
|
||||
fun getBufferedMessages(): List<BufferMessage> {
|
||||
return bufferedMessages.sortedBy { it.time }
|
||||
}
|
||||
|
||||
fun getBufferedMessagesForChat(chatId: Long): List<BufferMessage> {
|
||||
return bufferedMessages.filter { it.chatId==chatId }.sortedBy { it.time }
|
||||
}
|
||||
|
||||
// todo - read from db by date (Victor's suggestion - filter by one day only. Need to be changed in UI also.
|
||||
fun getIngoingMessages(currentUserId: Int, start: Long, end: Long): List<LocationMessage> {
|
||||
return dbHelper.getIngoingMessages(currentUserId, start, end)
|
||||
}
|
||||
|
||||
fun getMessagesForUserInChat(userId: Int, chatId: Long, start: Long, end: Long): List<LocationMessage> {
|
||||
return dbHelper.getMessagesForUserInChat(userId, chatId, start, end)
|
||||
}
|
||||
|
||||
fun getMessagesForUser(userId: Int, start: Long, end: Long): List<LocationMessage> {
|
||||
return dbHelper.getMessagesForUser(userId, start, end)
|
||||
}
|
||||
|
||||
fun addBufferedMessage(message: BufferMessage) {
|
||||
log.debug("addBufferedMessage $message")
|
||||
val messages = mutableListOf(*this.bufferedMessages.toTypedArray())
|
||||
messages.add(message)
|
||||
this.bufferedMessages = messages
|
||||
dbHelper.addBufferedMessage(message)
|
||||
}
|
||||
|
||||
fun addNewLocationMessage(message: TdApi.Message) {
|
||||
log.debug("addNewLocationMessage ${message.id}")
|
||||
val type = OsmandLocationUtils.getMessageType(message, app.telegramHelper)
|
||||
|
||||
val previousMessage = lastMessages.firstOrNull { it.chatId == message.chatId && it.userId == message.senderUserId && it.type == type }
|
||||
val locationMessage = OsmandLocationUtils.parseMessage(message, app.telegramHelper, previousMessage)
|
||||
if (locationMessage != null) {
|
||||
dbHelper.addLocationMessage(locationMessage)
|
||||
val messages = mutableListOf(*this.lastMessages.toTypedArray())
|
||||
messages.remove(previousMessage)
|
||||
messages.add(locationMessage)
|
||||
this.lastMessages = messages
|
||||
}
|
||||
}
|
||||
|
||||
fun clearBufferedMessages() {
|
||||
log.debug("clearBufferedMessages")
|
||||
dbHelper.clearBufferedMessages()
|
||||
bufferedMessages = emptyList()
|
||||
}
|
||||
|
||||
fun removeBufferedMessage(message: BufferMessage) {
|
||||
log.debug("removeBufferedMessage $message")
|
||||
val messages = mutableListOf(*this.bufferedMessages.toTypedArray())
|
||||
messages.remove(message)
|
||||
this.bufferedMessages = messages
|
||||
dbHelper.removeBufferedMessage(message)
|
||||
}
|
||||
|
||||
private fun readBufferedMessages() {
|
||||
this.bufferedMessages = dbHelper.getBufferedMessages()
|
||||
}
|
||||
|
||||
private fun readLastMessages() {
|
||||
this.lastMessages = dbHelper.getLastMessages()
|
||||
}
|
||||
|
||||
private class SQLiteHelper(context: Context) :
|
||||
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
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(TIMELINE_TABLE_DELETE)
|
||||
db.execSQL(BUFFER_TABLE_DELETE)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
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.time, message.type))
|
||||
}
|
||||
|
||||
internal fun addLocationMessage(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.time, message.type, message.messageId, message.distanceFromPrev))
|
||||
}
|
||||
|
||||
internal fun getMessagesForUser(userId: Int, start: Long, end: Long): List<LocationMessage> {
|
||||
val res = arrayListOf<LocationMessage>()
|
||||
readableDatabase?.rawQuery(
|
||||
"$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_TIME ASC ",
|
||||
arrayOf(userId.toString()))?.apply {
|
||||
if (moveToFirst()) {
|
||||
do {
|
||||
res.add(readLocationMessage(this@apply))
|
||||
} while (moveToNext())
|
||||
}
|
||||
close()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
internal fun getPreviousMessage(userId: Int, chatId: Long): LocationMessage? {
|
||||
var res:LocationMessage? = null
|
||||
readableDatabase?.rawQuery(
|
||||
"$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_CHAT_ID = ? ORDER BY $COL_TIME DESC LIMIT 1",
|
||||
arrayOf(userId.toString(), chatId.toString()))?.apply {
|
||||
if (moveToFirst()) {
|
||||
res = readLocationMessage(this@apply)
|
||||
}
|
||||
close()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
internal fun getIngoingMessages(currentUserId: Int, start: Long, end: Long): List<LocationMessage> {
|
||||
val res = arrayListOf<LocationMessage>()
|
||||
readableDatabase?.rawQuery(
|
||||
"$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID != ? AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_USER_ID ASC, $COL_CHAT_ID ASC, $COL_TIME ASC ",
|
||||
arrayOf(currentUserId.toString()))?.apply {
|
||||
if (moveToFirst()) {
|
||||
do {
|
||||
res.add(readLocationMessage(this@apply))
|
||||
} while (moveToNext())
|
||||
}
|
||||
close()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
internal fun getMessagesForUserInChat(userId: Int, chatId: Long, start: Long, end: Long): List<LocationMessage> {
|
||||
val res = arrayListOf<LocationMessage>()
|
||||
readableDatabase?.rawQuery(
|
||||
"$TIMELINE_TABLE_SELECT WHERE $COL_USER_ID = ? AND $COL_CHAT_ID = ? AND $COL_TIME BETWEEN $start AND $end ORDER BY $COL_TIME ASC ",
|
||||
arrayOf(userId.toString(), chatId.toString()))?.apply {
|
||||
if (moveToFirst()) {
|
||||
do {
|
||||
res.add(readLocationMessage(this@apply))
|
||||
} while (moveToNext())
|
||||
}
|
||||
close()
|
||||
}
|
||||
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 getLastMessages(): List<LocationMessage> {
|
||||
val res = arrayListOf<LocationMessage>()
|
||||
readableDatabase?.rawQuery(TIMELINE_TABLE_SELECT, null)?.apply {
|
||||
if (moveToFirst()) {
|
||||
do {
|
||||
res.add(readLocationMessage(this@apply))
|
||||
} while (moveToNext())
|
||||
}
|
||||
close()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
internal fun readLocationMessage(cursor: Cursor): LocationMessage {
|
||||
val userId = cursor.getInt(0)
|
||||
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 messageId = cursor.getLong(10)
|
||||
val distanceFromPrev = cursor.getDouble(11)
|
||||
|
||||
return LocationMessage(userId, chatId, lat, lon, altitude, speed, hdop, bearing, date, type, messageId, distanceFromPrev)
|
||||
}
|
||||
|
||||
internal fun readBufferMessage(cursor: Cursor): BufferMessage {
|
||||
val chatId = cursor.getLong(0)
|
||||
val lat = cursor.getDouble(1)
|
||||
val lon = cursor.getDouble(2)
|
||||
val altitude = cursor.getDouble(3)
|
||||
val speed = cursor.getDouble(4)
|
||||
val hdop = cursor.getDouble(5)
|
||||
val bearing = cursor.getDouble(6)
|
||||
val date = cursor.getLong(7)
|
||||
val type = cursor.getInt(8)
|
||||
|
||||
return BufferMessage(chatId, lat, lon, altitude, speed, hdop, bearing, date, type)
|
||||
}
|
||||
|
||||
internal fun clearBufferedMessages() {
|
||||
writableDatabase?.execSQL(BUFFER_TABLE_CLEAR)
|
||||
}
|
||||
|
||||
internal fun removeBufferedMessage(message: BufferMessage) {
|
||||
|
||||
writableDatabase?.execSQL(
|
||||
BUFFER_TABLE_REMOVE,
|
||||
arrayOf(
|
||||
message.chatId,
|
||||
message.lat,
|
||||
message.lon,
|
||||
message.altitude,
|
||||
message.speed,
|
||||
message.hdop,
|
||||
message.bearing,
|
||||
message.time,
|
||||
message.type
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DATABASE_NAME = "location_messages"
|
||||
private const val DATABASE_VERSION = 5
|
||||
|
||||
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_TIME = "time"
|
||||
private const val COL_LAT = "lat"
|
||||
private const val COL_LON = "lon"
|
||||
private const val COL_ALTITUDE = "altitude"
|
||||
private const val COL_SPEED = "speed"
|
||||
private const val COL_HDOP = "hdop"
|
||||
private const val COL_BEARING = "bearing"
|
||||
private const val COL_TYPE = "type" // 0 = user map message, 1 = user text message, 2 = bot map message, 3 = bot text message
|
||||
private const val COL_MESSAGE_ID = "message_id"
|
||||
private const val COL_DISTANCE_FROM_PREV = "distance_from_prev"
|
||||
|
||||
private const val DATE_INDEX = "date_index"
|
||||
|
||||
// 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, $COL_DISTANCE_FROM_PREV) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
|
||||
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, $COL_DISTANCE_FROM_PREV double )")
|
||||
|
||||
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, $COL_DISTANCE_FROM_PREV FROM $TIMELINE_TABLE_NAME"
|
||||
|
||||
private const val TIMELINE_TABLE_CLEAR = "DELETE FROM $TIMELINE_TABLE_NAME"
|
||||
|
||||
private const val TIMELINE_TABLE_DELETE = "DROP TABLE IF EXISTS $TIMELINE_TABLE_NAME"
|
||||
|
||||
// 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) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
|
||||
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)")
|
||||
|
||||
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 FROM $BUFFER_TABLE_NAME"
|
||||
|
||||
private const val BUFFER_TABLE_CLEAR = "DELETE FROM $BUFFER_TABLE_NAME"
|
||||
|
||||
private const val BUFFER_TABLE_REMOVE = "DELETE FROM $BUFFER_TABLE_NAME WHERE $COL_CHAT_ID = ? AND $COL_LAT = ? AND $COL_LON = ? AND $COL_ALTITUDE = ? AND $COL_SPEED = ? AND $COL_HDOP = ? AND $COL_BEARING = ? AND $COL_TIME = ? AND $COL_TYPE = ?"
|
||||
|
||||
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 time: Long,
|
||||
val type: Int,
|
||||
val messageId: Long,
|
||||
val distanceFromPrev: Double)
|
||||
|
||||
data class BufferMessage (
|
||||
val chatId: Long,
|
||||
val lat: Double,
|
||||
val lon: Double,
|
||||
val altitude: Double,
|
||||
val speed: Double,
|
||||
val hdop: Double,
|
||||
val bearing: Double,
|
||||
val time: Long,
|
||||
val type: Int)
|
||||
|
||||
companion object {
|
||||
|
||||
const val TYPE_USER_MAP = 0
|
||||
const val TYPE_USER_TEXT = 1
|
||||
const val TYPE_USER_BOTH = 2
|
||||
const val TYPE_BOT_MAP = 3
|
||||
const val TYPE_BOT_TEXT = 4
|
||||
const val TYPE_BOT_BOTH = 5
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package net.osmand.telegram.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
|
||||
class MessagesDbHelper(val app: TelegramApplication) {
|
||||
|
||||
private val messages = HashSet<Message>()
|
||||
|
||||
private val sqliteHelper: SQLiteHelper
|
||||
|
||||
init {
|
||||
sqliteHelper = SQLiteHelper(app)
|
||||
sqliteHelper.getMessages().forEach {
|
||||
app.telegramHelper.loadMessage(it.chatId, it.messageId)
|
||||
}
|
||||
app.telegramHelper.addIncomingMessagesListener(object :
|
||||
TelegramHelper.TelegramIncomingMessagesListener {
|
||||
|
||||
override fun onReceiveChatLocationMessages(
|
||||
chatId: Long, vararg messages: TdApi.Message
|
||||
) {
|
||||
messages.forEach { addMessage(chatId, it.id) }
|
||||
}
|
||||
|
||||
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {
|
||||
messages.forEach { removeMessage(chatId, it.id) }
|
||||
}
|
||||
|
||||
override fun updateLocationMessages() {}
|
||||
})
|
||||
}
|
||||
|
||||
fun saveMessages() {
|
||||
clearMessages()
|
||||
synchronized(messages) {
|
||||
sqliteHelper.addMessages(messages)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMessages() {
|
||||
sqliteHelper.clearMessages()
|
||||
}
|
||||
|
||||
private fun addMessage(chatId: Long, messageId: Long) {
|
||||
synchronized(messages) {
|
||||
messages.add(Message(chatId, messageId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeMessage(chatId: Long, messageId: Long) {
|
||||
synchronized(messages) {
|
||||
messages.remove(Message(chatId, messageId))
|
||||
}
|
||||
}
|
||||
|
||||
private class SQLiteHelper(context: Context) :
|
||||
SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(MESSAGES_TABLE_CREATE)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL(MESSAGES_TABLE_DELETE)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
internal fun addMessages(messages: Set<Message>) {
|
||||
messages.forEach {
|
||||
writableDatabase?.execSQL(MESSAGES_TABLE_INSERT, arrayOf(it.chatId, it.messageId))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getMessages(): Set<Message> {
|
||||
val res = HashSet<Message>()
|
||||
readableDatabase?.rawQuery(MESSAGES_TABLE_SELECT, null)?.apply {
|
||||
if (moveToFirst()) {
|
||||
do {
|
||||
res.add(Message(getLong(0), getLong(1)))
|
||||
} while (moveToNext())
|
||||
}
|
||||
close()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
internal fun clearMessages() {
|
||||
writableDatabase?.execSQL(MESSAGES_TABLE_CLEAR)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DB_NAME = "messages.db"
|
||||
private const val DB_VERSION = 1
|
||||
|
||||
private const val MESSAGES_TABLE_NAME = "messages"
|
||||
private const val MESSAGES_COL_CHAT_ID = "chat_id"
|
||||
private const val MESSAGES_COL_MESSAGE_ID = "message_id"
|
||||
|
||||
private const val MESSAGES_TABLE_CREATE =
|
||||
"CREATE TABLE IF NOT EXISTS $MESSAGES_TABLE_NAME (" +
|
||||
"$MESSAGES_COL_CHAT_ID LONG, " +
|
||||
"$MESSAGES_COL_MESSAGE_ID LONG)"
|
||||
|
||||
private const val MESSAGES_TABLE_DELETE = "DROP TABLE IF EXISTS $MESSAGES_TABLE_NAME"
|
||||
|
||||
private const val MESSAGES_TABLE_SELECT =
|
||||
"SELECT $MESSAGES_COL_CHAT_ID, $MESSAGES_COL_MESSAGE_ID FROM $MESSAGES_TABLE_NAME"
|
||||
|
||||
private const val MESSAGES_TABLE_CLEAR = "DELETE FROM $MESSAGES_TABLE_NAME"
|
||||
|
||||
private const val MESSAGES_TABLE_INSERT = "INSERT INTO $MESSAGES_TABLE_NAME (" +
|
||||
"$MESSAGES_COL_CHAT_ID, $MESSAGES_COL_MESSAGE_ID) VALUES (?, ?)"
|
||||
}
|
||||
}
|
||||
|
||||
private data class Message(val chatId: Long, val messageId: Long)
|
||||
}
|
|
@ -1,476 +0,0 @@
|
|||
package net.osmand.telegram.helpers;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.telegram.TelegramApplication;
|
||||
import net.osmand.telegram.utils.GPXUtilities;
|
||||
import net.osmand.telegram.utils.GPXUtilities.GPXFile;
|
||||
import net.osmand.telegram.utils.GPXUtilities.Track;
|
||||
import net.osmand.telegram.utils.GPXUtilities.TrkSegment;
|
||||
import net.osmand.telegram.utils.GPXUtilities.WptPt;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.drinkless.td.libcore.telegram.TdApi;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class SavingTracksDbHelper extends SQLiteOpenHelper {
|
||||
|
||||
private final static String DATABASE_NAME = "tracks";
|
||||
private final static int DATABASE_VERSION = 3;
|
||||
|
||||
private final static String TRACK_NAME = "track"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_USER_ID = "user_id"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_CHAT_ID = "chat_id"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_DATE = "date"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_LAT = "lat"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_LON = "lon"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_ALTITUDE = "altitude"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_SPEED = "speed"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_HDOP = "hdop"; //$NON-NLS-1$
|
||||
private final static String TRACK_COL_TEXT_INFO = "text_info"; // 1 = true, 0 = false //$NON-NLS-1$
|
||||
|
||||
private final static String INSERT_SCRIPT = "INSERT INTO " + TRACK_NAME + " (" + TRACK_COL_USER_ID + ", " + TRACK_COL_CHAT_ID + ", " + TRACK_COL_LAT + ", " + TRACK_COL_LON + ", "
|
||||
+ TRACK_COL_ALTITUDE + ", " + TRACK_COL_SPEED + ", " + TRACK_COL_HDOP + ", " + TRACK_COL_DATE + ", " + TRACK_COL_TEXT_INFO + ")"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
private final static String CREATE_SCRIPT = "CREATE TABLE " + TRACK_NAME + " (" + TRACK_COL_USER_ID + " long," + TRACK_COL_CHAT_ID + " long," + TRACK_COL_LAT + " double, " + TRACK_COL_LON + " double, " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$
|
||||
+ TRACK_COL_ALTITUDE + " double, " + TRACK_COL_SPEED + " double, " //$NON-NLS-1$ //$NON-NLS-2$
|
||||
+ TRACK_COL_HDOP + " double, " + TRACK_COL_DATE + " long, " + TRACK_COL_TEXT_INFO + " int )";
|
||||
|
||||
private final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class);
|
||||
|
||||
private final TelegramApplication app;
|
||||
|
||||
public SavingTracksDbHelper(TelegramApplication app) {
|
||||
super(app, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.app = app;
|
||||
|
||||
app.getTelegramHelper().addIncomingMessagesListener(new TelegramHelper.TelegramIncomingMessagesListener() {
|
||||
|
||||
@Override
|
||||
public void onReceiveChatLocationMessages(long chatId, @NotNull TdApi.Message... messages) {
|
||||
for (TdApi.Message message : messages) {
|
||||
updateLocationMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteChatLocationMessages(long chatId, @NotNull List<? extends TdApi.Message> messages) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLocationMessages() {
|
||||
|
||||
}
|
||||
});
|
||||
app.getTelegramHelper().addOutgoingMessagesListener(new TelegramHelper.TelegramOutgoingMessagesListener() {
|
||||
|
||||
@Override
|
||||
public void onUpdateMessages(@NotNull List<? extends TdApi.Message> messages) {
|
||||
for (TdApi.Message message : messages) {
|
||||
updateLocationMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteMessages(long chatId, @NotNull List<Long> messages) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendLiveLocationError(int code, @NotNull String message) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_SCRIPT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion < 3) {
|
||||
db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_TEXT_INFO + " int");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveUserDataToGpx(SaveGpxListener listener, File dir, int userId, long chatId, long start, long end) {
|
||||
GPXFile gpxFile = collectRecordedDataForUserAndChat(userId, chatId, start, end);
|
||||
if (gpxFile != null && !gpxFile.isEmpty()) {
|
||||
SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, userId);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveGpx(SaveGpxListener listener, File dir, GPXFile gpxFile) {
|
||||
if (gpxFile != null && !gpxFile.isEmpty()) {
|
||||
SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLocationMessage(TdApi.Message message) {
|
||||
log.debug(message);
|
||||
TdApi.MessageContent content = message.content;
|
||||
int senderId = app.getTelegramHelper().getSenderMessageId(message);
|
||||
if (content instanceof TdApi.MessageLocation) {
|
||||
long lastTextMessageUpdate = getLastTextTrackPointTimeForUser(message.senderUserId);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (lastTextMessageUpdate == 0 || currentTime - lastTextMessageUpdate < 10 * 1000) {
|
||||
log.debug("Add map message " + message.senderUserId);
|
||||
TdApi.MessageLocation messageLocation = (TdApi.MessageLocation) content;
|
||||
insertData(senderId, message.chatId, messageLocation.location.latitude,
|
||||
messageLocation.location.longitude, 0.0, 0.0, 0.0,
|
||||
Math.max(message.date, message.editDate), 0);
|
||||
} else {
|
||||
log.debug("Skip map message");
|
||||
}
|
||||
} else if (content instanceof TelegramHelper.MessageLocation) {
|
||||
log.debug("Add text message " + message.senderUserId);
|
||||
TelegramHelper.MessageLocation messageLocation = (TelegramHelper.MessageLocation) content;
|
||||
insertData(senderId, message.chatId, messageLocation.getLat(), messageLocation.getLon(),
|
||||
messageLocation.getAltitude(), messageLocation.getSpeed(), messageLocation.getHdop(),
|
||||
messageLocation.getLastUpdated() * 1000L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertData(int userId, long chatId, double lat, double lon, double alt, double speed, double hdop, long time, int textMessage) {
|
||||
execWithClose(INSERT_SCRIPT, new Object[]{userId, chatId, lat, lon, alt, speed, hdop, time, textMessage});
|
||||
}
|
||||
|
||||
private synchronized void execWithClose(String script, Object[] objects) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
try {
|
||||
if (db != null) {
|
||||
db.execSQL(script, objects);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getLastTextTrackPointTimeForUser(int userId) {
|
||||
long res = 0;
|
||||
try {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
if (db != null) {
|
||||
try {
|
||||
Cursor query = db.rawQuery("SELECT " + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ? AND "
|
||||
+ TRACK_COL_TEXT_INFO + " = ?" + " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(1)});
|
||||
if (query.moveToFirst()) {
|
||||
res = query.getLong(0);
|
||||
}
|
||||
query.close();
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public GPXFile collectRecordedDataForUserAndChat(int userId, long chatId, long start, long end) {
|
||||
GPXFile gpxFile = null;
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
if (db != null && db.isOpen()) {
|
||||
try {
|
||||
gpxFile = collectDBTracksForUser(db, userId, chatId, start, end);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
return gpxFile;
|
||||
}
|
||||
|
||||
public GPXFile collectRecordedDataForUser(int userId, long chatId, long start, long end) {
|
||||
GPXFile gpxFile = null;
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
if (db != null && db.isOpen()) {
|
||||
try {
|
||||
if (chatId == 0) {
|
||||
gpxFile = collectDBTracksForUser(db, userId, start, end);
|
||||
} else {
|
||||
gpxFile = collectDBTracksForUser(db, userId, chatId, start, end);
|
||||
}
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
return gpxFile;
|
||||
}
|
||||
|
||||
public ArrayList<GPXFile> collectRecordedDataForUsers(long start, long end, ArrayList<Integer> ignoredUsersIds) {
|
||||
ArrayList<GPXFile> data = new ArrayList<>();
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
if (db != null && db.isOpen()) {
|
||||
try {
|
||||
collectDBTracksForUsers(db, data, start, end, ignoredUsersIds);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long chatId, long start, long end) {
|
||||
Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + ","
|
||||
+ TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + ","
|
||||
+ TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?"
|
||||
+ " AND " + TRACK_COL_CHAT_ID + " = ?" + " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end
|
||||
+ " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(chatId)});
|
||||
|
||||
GPXFile gpxFile = null;
|
||||
long previousTime = 0;
|
||||
TrkSegment segment = null;
|
||||
Track track = null;
|
||||
if (query.moveToFirst()) {
|
||||
gpxFile = new GPXFile();
|
||||
gpxFile.chatId = chatId;
|
||||
gpxFile.userId = userId;
|
||||
do {
|
||||
long time = query.getLong(7);
|
||||
WptPt pt = new WptPt();
|
||||
pt.userId = query.getInt(0);
|
||||
pt.chatId = query.getLong(1);
|
||||
pt.lat = query.getDouble(2);
|
||||
pt.lon = query.getDouble(3);
|
||||
pt.ele = query.getDouble(4);
|
||||
pt.speed = query.getDouble(5);
|
||||
pt.hdop = query.getDouble(6);
|
||||
pt.time = time;
|
||||
long currentInterval = Math.abs(time - previousTime);
|
||||
|
||||
if (track != null) {
|
||||
if (currentInterval < 30 * 60 * 1000) {
|
||||
// 30 minute - same segment
|
||||
segment.points.add(pt);
|
||||
} else {
|
||||
segment = new TrkSegment();
|
||||
segment.points.add(pt);
|
||||
track.segments.add(segment);
|
||||
}
|
||||
} else {
|
||||
track = new Track();
|
||||
segment = new TrkSegment();
|
||||
track.segments.add(segment);
|
||||
segment.points.add(pt);
|
||||
|
||||
gpxFile.tracks.add(track);
|
||||
}
|
||||
previousTime = time;
|
||||
} while (query.moveToNext());
|
||||
}
|
||||
query.close();
|
||||
return gpxFile;
|
||||
}
|
||||
|
||||
private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long start, long end) {
|
||||
Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + ","
|
||||
+ TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + ","
|
||||
+ TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?"
|
||||
+ " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end
|
||||
+ " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)});
|
||||
|
||||
GPXFile gpxFile = null;
|
||||
long previousTime = 0;
|
||||
TrkSegment segment = null;
|
||||
Track track = null;
|
||||
if (query.moveToFirst()) {
|
||||
gpxFile = new GPXFile();
|
||||
gpxFile.userId = userId;
|
||||
do {
|
||||
long time = query.getLong(7);
|
||||
WptPt pt = new WptPt();
|
||||
pt.userId = query.getInt(0);
|
||||
pt.chatId = query.getLong(1);
|
||||
pt.lat = query.getDouble(2);
|
||||
pt.lon = query.getDouble(3);
|
||||
pt.ele = query.getDouble(4);
|
||||
pt.speed = query.getDouble(5);
|
||||
pt.hdop = query.getDouble(6);
|
||||
pt.time = time;
|
||||
long currentInterval = Math.abs(time - previousTime);
|
||||
|
||||
if (track != null) {
|
||||
if (currentInterval < 30 * 60 * 1000) {
|
||||
// 30 minute - same segment
|
||||
segment.points.add(pt);
|
||||
} else {
|
||||
segment = new TrkSegment();
|
||||
segment.points.add(pt);
|
||||
track.segments.add(segment);
|
||||
}
|
||||
} else {
|
||||
track = new Track();
|
||||
segment = new TrkSegment();
|
||||
track.segments.add(segment);
|
||||
segment.points.add(pt);
|
||||
|
||||
gpxFile.tracks.add(track);
|
||||
}
|
||||
previousTime = time;
|
||||
} while (query.moveToNext());
|
||||
}
|
||||
query.close();
|
||||
return gpxFile;
|
||||
}
|
||||
|
||||
private void collectDBTracksForUsers(SQLiteDatabase db, ArrayList<GPXFile> dataTracks, long start, long end, ArrayList<Integer> ignoredUsersIds) {
|
||||
Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + ","
|
||||
+ TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + ","
|
||||
+ TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_DATE
|
||||
+ " BETWEEN " + start + " AND " + end + " ORDER BY " + TRACK_COL_USER_ID + " ASC, "
|
||||
+ TRACK_COL_CHAT_ID + " ASC, " + TRACK_COL_DATE + " ASC ", null);
|
||||
|
||||
long previousTime = 0;
|
||||
long previousChatId = 0;
|
||||
int previousUserId = 0;
|
||||
TrkSegment segment = null;
|
||||
Track track = null;
|
||||
GPXFile gpx = new GPXFile();
|
||||
if (query.moveToFirst()) {
|
||||
do {
|
||||
int userId = query.getInt(0);
|
||||
if (ignoredUsersIds.contains(userId)) {
|
||||
continue;
|
||||
}
|
||||
int chatId = query.getInt(1);
|
||||
long time = query.getLong(7);
|
||||
if (previousUserId != userId || previousChatId != chatId) {
|
||||
gpx = new GPXFile();
|
||||
gpx.chatId = chatId;
|
||||
gpx.userId = userId;
|
||||
previousTime = 0;
|
||||
track = null;
|
||||
segment = null;
|
||||
dataTracks.add(gpx);
|
||||
}
|
||||
|
||||
WptPt pt = new WptPt();
|
||||
pt.userId = userId;
|
||||
pt.chatId = chatId;
|
||||
pt.lat = query.getDouble(2);
|
||||
pt.lon = query.getDouble(3);
|
||||
pt.ele = query.getDouble(4);
|
||||
pt.speed = query.getDouble(5);
|
||||
pt.hdop = query.getDouble(6);
|
||||
pt.time = time;
|
||||
long currentInterval = Math.abs(time - previousTime);
|
||||
if (track != null) {
|
||||
if (currentInterval < 30 * 60 * 1000) {
|
||||
// 30 minute - same segment
|
||||
segment.points.add(pt);
|
||||
} else {
|
||||
segment = new TrkSegment();
|
||||
segment.points.add(pt);
|
||||
track.segments.add(segment);
|
||||
}
|
||||
} else {
|
||||
track = new Track();
|
||||
segment = new TrkSegment();
|
||||
track.segments.add(segment);
|
||||
segment.points.add(pt);
|
||||
|
||||
gpx.tracks.add(track);
|
||||
}
|
||||
previousTime = time;
|
||||
previousUserId = userId;
|
||||
previousChatId = chatId;
|
||||
} while (query.moveToNext());
|
||||
}
|
||||
query.close();
|
||||
}
|
||||
|
||||
private static class SaveGPXTrackToFileTask extends AsyncTask<Void, Void, List<String>> {
|
||||
|
||||
private TelegramApplication app;
|
||||
private SaveGpxListener listener;
|
||||
|
||||
private final GPXFile gpxFile;
|
||||
private File dir;
|
||||
private int userId;
|
||||
|
||||
SaveGPXTrackToFileTask(TelegramApplication app, SaveGpxListener listener, GPXFile gpxFile, File dir, int userId) {
|
||||
this.gpxFile = gpxFile;
|
||||
this.listener = listener;
|
||||
this.app = app;
|
||||
this.dir = dir;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> doInBackground(Void... params) {
|
||||
List<String> warnings = new ArrayList<String>();
|
||||
dir.mkdirs();
|
||||
if (dir.getParentFile().canWrite()) {
|
||||
if (dir.exists()) {
|
||||
|
||||
// save file
|
||||
File fout = new File(dir, userId + ".gpx"); //$NON-NLS-1$
|
||||
if (!gpxFile.isEmpty()) {
|
||||
WptPt pt = gpxFile.findPointToShow();
|
||||
|
||||
TdApi.User user = app.getTelegramHelper().getUser(pt.userId);
|
||||
String fileName;
|
||||
if (user != null) {
|
||||
fileName = TelegramUiHelper.INSTANCE.getUserName(user)
|
||||
+ "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
|
||||
} else {
|
||||
fileName = userId + "_" + new SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(new Date(pt.time)); //$NON-NLS-1$
|
||||
}
|
||||
fout = new File(dir, fileName + ".gpx"); //$NON-NLS-1$
|
||||
int ind = 1;
|
||||
while (fout.exists()) {
|
||||
fout = new File(dir, fileName + "_" + (++ind) + ".gpx"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
String warn = GPXUtilities.writeGpxFile(fout, gpxFile, app);
|
||||
if (warn != null) {
|
||||
warnings.add(warn);
|
||||
return warnings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<String> warnings) {
|
||||
if (listener != null) {
|
||||
if (warnings != null && warnings.isEmpty()) {
|
||||
listener.onSavingGpxFinish(gpxFile.path);
|
||||
} else {
|
||||
listener.onSavingGpxError(warnings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface SaveGpxListener {
|
||||
|
||||
void onSavingGpxFinish(String path);
|
||||
|
||||
void onSavingGpxError(List<String> warnings);
|
||||
}
|
||||
}
|
|
@ -3,6 +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.BufferMessage
|
||||
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
|
||||
import net.osmand.telegram.utils.AndroidNetworkUtils
|
||||
import net.osmand.telegram.utils.BASE_URL
|
||||
|
@ -48,36 +49,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
lastLocation = location
|
||||
|
||||
if (location != null) {
|
||||
val chatsShareInfo = app.settings.getChatsShareInfo()
|
||||
if (chatsShareInfo.isNotEmpty()) {
|
||||
val latitude = location.latitude
|
||||
val longitude = location.longitude
|
||||
val user = app.telegramHelper.getCurrentUser()
|
||||
val sharingMode = app.settings.currentSharingMode
|
||||
|
||||
if (user != null && sharingMode == user.id.toString()) {
|
||||
when (app.settings.shareTypeValue) {
|
||||
SHARE_TYPE_MAP -> app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, latitude, longitude)
|
||||
SHARE_TYPE_TEXT -> app.telegramHelper.sendLiveLocationText(chatsShareInfo, location)
|
||||
SHARE_TYPE_MAP_AND_TEXT -> {
|
||||
app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, latitude, longitude)
|
||||
app.telegramHelper.sendLiveLocationText(chatsShareInfo, location)
|
||||
}
|
||||
}
|
||||
} else if (sharingMode.isNotEmpty()) {
|
||||
val url = getDeviceSharingUrl(location,sharingMode)
|
||||
AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false,
|
||||
object : AndroidNetworkUtils.OnRequestResultListener {
|
||||
override fun onResult(result: String?) {
|
||||
updateShareInfoSuccessfulSendTime(result, chatsShareInfo)
|
||||
|
||||
val osmandBot = app.telegramHelper.getOsmandBot()
|
||||
if (osmandBot != null) {
|
||||
checkAndSendViaBotMessages(chatsShareInfo, TdApi.Location(latitude, longitude), osmandBot)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (app.settings.getChatsShareInfo().isNotEmpty()) {
|
||||
shareLocationMessages(location, app.telegramHelper.getCurrentUserId())
|
||||
}
|
||||
lastLocationMessageSentTime = System.currentTimeMillis()
|
||||
}
|
||||
|
@ -122,6 +95,37 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
}
|
||||
}
|
||||
|
||||
fun checkAndSendBufferMessagesToChat(chatId: Long) {
|
||||
val shareInfo = app.settings.getChatsShareInfo()[chatId]
|
||||
if (shareInfo != null && shareInfo.pendingTdLib < 10) {
|
||||
app.locationMessages.getBufferedMessagesForChat(shareInfo.chatId).forEach {
|
||||
if (it.type == LocationMessages.TYPE_USER_TEXT && !shareInfo.pendingTextMessage && shareInfo.currentTextMessageId != -1L) {
|
||||
app.telegramHelper.editTextLocation(shareInfo, it)
|
||||
app.locationMessages.removeBufferedMessage(it)
|
||||
} else if (it.type == LocationMessages.TYPE_USER_MAP && !shareInfo.pendingMapMessage && shareInfo.currentMapMessageId != -1L) {
|
||||
app.telegramHelper.editMapLocation(shareInfo, it)
|
||||
app.locationMessages.removeBufferedMessage(it)
|
||||
} else if (it.type == LocationMessages.TYPE_USER_BOTH) {
|
||||
var messageSent = false
|
||||
if (!shareInfo.pendingMapMessage && shareInfo.currentMapMessageId != -1L) {
|
||||
app.telegramHelper.editMapLocation(shareInfo, it)
|
||||
messageSent = true
|
||||
}
|
||||
if (!shareInfo.pendingTextMessage && shareInfo.currentTextMessageId != -1L) {
|
||||
app.telegramHelper.editTextLocation(shareInfo, it)
|
||||
messageSent = true
|
||||
}
|
||||
if (messageSent) {
|
||||
app.locationMessages.removeBufferedMessage(it)
|
||||
}
|
||||
}
|
||||
if (shareInfo.pendingTdLib >= 10) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startSharingLocation() {
|
||||
if (!sharingLocation) {
|
||||
sharingLocation = true
|
||||
|
@ -129,6 +133,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
app.startMyLocationService()
|
||||
|
||||
refreshNotification()
|
||||
|
||||
checkAndSendBufferMessages()
|
||||
} else {
|
||||
app.forceUpdateMyLocation()
|
||||
}
|
||||
|
@ -158,25 +164,193 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
refreshNotification()
|
||||
}
|
||||
|
||||
private fun getDeviceSharingUrl(loc: Location, sharingMode: String): String {
|
||||
val url = "$BASE_URL/device/$sharingMode/send?lat=${loc.latitude}&lon=${loc.longitude}"
|
||||
private fun checkAndSendBufferMessages(){
|
||||
log.debug("checkAndSendBufferMessages")
|
||||
app.settings.getChatsShareInfo().forEach loop@{ (chatId, shareInfo) ->
|
||||
if (shareInfo.pendingTdLib < 10) {
|
||||
app.locationMessages.getBufferedMessagesForChat(chatId).forEach {
|
||||
if (it.type == LocationMessages.TYPE_USER_TEXT && !shareInfo.pendingTextMessage && shareInfo.currentTextMessageId != -1L) {
|
||||
app.telegramHelper.editTextLocation(shareInfo, it)
|
||||
app.locationMessages.removeBufferedMessage(it)
|
||||
} else if (it.type == LocationMessages.TYPE_USER_MAP && !shareInfo.pendingMapMessage && shareInfo.currentMapMessageId != -1L) {
|
||||
app.telegramHelper.editMapLocation(shareInfo, it)
|
||||
app.locationMessages.removeBufferedMessage(it)
|
||||
}
|
||||
if (shareInfo.pendingTdLib >= 10) {
|
||||
return@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareLocationMessages(location: Location, userId: Int) {
|
||||
val chatsShareInfo = app.settings.getChatsShareInfo()
|
||||
val latitude = location.latitude
|
||||
val longitude = location.longitude
|
||||
val sharingMode = app.settings.currentSharingMode
|
||||
val isBot = sharingMode != userId.toString()
|
||||
var bufferedMessagesFull = false
|
||||
val type = when (app.settings.shareTypeValue) {
|
||||
SHARE_TYPE_MAP -> {
|
||||
if (isBot) LocationMessages.TYPE_BOT_MAP else LocationMessages.TYPE_USER_MAP
|
||||
}
|
||||
SHARE_TYPE_TEXT -> {
|
||||
if (isBot) LocationMessages.TYPE_BOT_TEXT else LocationMessages.TYPE_USER_TEXT
|
||||
}
|
||||
SHARE_TYPE_MAP_AND_TEXT -> {
|
||||
if (isBot) LocationMessages.TYPE_BOT_BOTH else LocationMessages.TYPE_USER_BOTH
|
||||
} else -> -1
|
||||
}
|
||||
chatsShareInfo.values.forEach { shareInfo ->
|
||||
if (shareInfo.pendingTdLib >= 10) {
|
||||
bufferedMessagesFull = true
|
||||
}
|
||||
val message = BufferMessage(
|
||||
shareInfo.chatId, latitude, longitude, location.altitude, location.speed.toDouble(),
|
||||
location.accuracy.toDouble(), location.bearing.toDouble(), System.currentTimeMillis(), type
|
||||
)
|
||||
|
||||
if (type == LocationMessages.TYPE_USER_MAP || type == LocationMessages.TYPE_BOT_MAP) {
|
||||
prepareMapMessage(shareInfo, message, isBot, sharingMode)
|
||||
} else if (type == LocationMessages.TYPE_USER_TEXT || type == LocationMessages.TYPE_BOT_TEXT) {
|
||||
prepareTextMessage(shareInfo, message, isBot, sharingMode)
|
||||
} else if (type == LocationMessages.TYPE_USER_BOTH || type == LocationMessages.TYPE_BOT_BOTH) {
|
||||
prepareMapAndTextMessage(shareInfo, message, isBot, sharingMode)
|
||||
}
|
||||
}
|
||||
if (bufferedMessagesFull) {
|
||||
checkNetworkType()
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareTextMessage(shareInfo: TelegramSettings.ShareChatInfo,message: BufferMessage,isBot:Boolean, sharingMode: String) {
|
||||
log.debug("prepareTextMessage $message")
|
||||
shareInfo.collectedMessages++
|
||||
if (shareInfo.currentTextMessageId == -1L) {
|
||||
if (shareInfo.pendingTextMessage) {
|
||||
app.locationMessages.addBufferedMessage(message)
|
||||
} else {
|
||||
if (isBot) {
|
||||
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_TEXT)
|
||||
} else {
|
||||
app.telegramHelper.sendNewTextLocation(shareInfo, message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isBot) {
|
||||
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_TEXT)
|
||||
} else {
|
||||
if (shareInfo.pendingTdLib < 10) {
|
||||
app.telegramHelper.editTextLocation(shareInfo, message)
|
||||
} else {
|
||||
app.locationMessages.addBufferedMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareMapMessage(shareInfo: TelegramSettings.ShareChatInfo,message: BufferMessage,isBot:Boolean, sharingMode: String) {
|
||||
log.debug("prepareMapMessage $message")
|
||||
shareInfo.collectedMessages++
|
||||
if (shareInfo.currentMapMessageId == -1L) {
|
||||
if (shareInfo.pendingMapMessage) {
|
||||
app.locationMessages.addBufferedMessage(message)
|
||||
} else {
|
||||
if (isBot) {
|
||||
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_MAP)
|
||||
} else {
|
||||
app.telegramHelper.sendNewMapLocation(shareInfo, message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isBot) {
|
||||
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_MAP)
|
||||
} else {
|
||||
if (shareInfo.pendingTdLib < 10) {
|
||||
app.telegramHelper.editMapLocation(shareInfo, message)
|
||||
} else {
|
||||
shareInfo.collectedMessages++
|
||||
app.locationMessages.addBufferedMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareMapAndTextMessage(shareInfo: TelegramSettings.ShareChatInfo, message: BufferMessage, isBot:Boolean, sharingMode: String) {
|
||||
log.debug("prepareMapAndTextMessage $message")
|
||||
shareInfo.collectedMessages++
|
||||
if (shareInfo.pendingMapMessage || shareInfo.pendingTextMessage || shareInfo.pendingTdLib >= 10) {
|
||||
app.locationMessages.addBufferedMessage(message)
|
||||
} else {
|
||||
if (isBot) {
|
||||
sendLocationToBot(message, sharingMode, shareInfo, SHARE_TYPE_MAP_AND_TEXT)
|
||||
} else {
|
||||
if (shareInfo.currentMapMessageId == -1L) {
|
||||
app.telegramHelper.sendNewMapLocation(shareInfo, message)
|
||||
} else {
|
||||
app.telegramHelper.editMapLocation(shareInfo, message)
|
||||
}
|
||||
if (shareInfo.currentTextMessageId == -1L) {
|
||||
app.telegramHelper.sendNewTextLocation(shareInfo, message)
|
||||
} else {
|
||||
app.telegramHelper.editTextLocation(shareInfo, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNetworkType(){
|
||||
if (app.isInternetConnectionAvailable) {
|
||||
val networkType = when {
|
||||
app.isWifiConnected -> TdApi.NetworkTypeWiFi()
|
||||
app.isMobileConnected -> TdApi.NetworkTypeMobile()
|
||||
else -> TdApi.NetworkTypeOther()
|
||||
}
|
||||
app.telegramHelper.networkChange(networkType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendLocationToBot(locationMessage: BufferMessage, sharingMode: String, shareInfo: TelegramSettings.ShareChatInfo, shareType: String) {
|
||||
if (app.isInternetConnectionAvailable) {
|
||||
log.debug("sendLocationToBot $locationMessage")
|
||||
val url = getDeviceSharingUrl(locationMessage, sharingMode)
|
||||
AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false,
|
||||
object : AndroidNetworkUtils.OnRequestResultListener {
|
||||
override fun onResult(result: String?) {
|
||||
val chatsShareInfo = app.settings.getChatsShareInfo()
|
||||
val success = checkResultAndUpdateShareInfoSuccessfulSendTime(result, chatsShareInfo)
|
||||
val osmandBotId = app.telegramHelper.getOsmandBot()?.id ?: -1
|
||||
val device = app.settings.getCurrentSharingDevice()
|
||||
|
||||
if (success && shareInfo.shouldSendViaBotMessage && osmandBotId != -1 && device != null) {
|
||||
app.telegramHelper.sendViaBotLocationMessage(osmandBotId, shareInfo, TdApi.Location(locationMessage.lat, locationMessage.lon), device, shareType)
|
||||
shareInfo.shouldSendViaBotMessage = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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.hasBearing() && loc.bearing != 0.0f) {
|
||||
if (loc.bearing != 0.0) {
|
||||
builder.append("&azi=${loc.bearing}")
|
||||
}
|
||||
if (loc.hasSpeed() && loc.speed != 0.0f) {
|
||||
if (loc.speed != 0.0) {
|
||||
builder.append("&spd=${loc.speed}")
|
||||
}
|
||||
if (loc.hasAltitude() && loc.altitude != 0.0) {
|
||||
if (loc.altitude != 0.0) {
|
||||
builder.append("&alt=${loc.altitude}")
|
||||
}
|
||||
if (loc.hasAccuracy() && loc.accuracy != 0.0f) {
|
||||
builder.append("&hdop=${loc.accuracy}")
|
||||
if (loc.hdop != 0.0) {
|
||||
builder.append("&hdop=${loc.hdop}")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private fun updateShareInfoSuccessfulSendTime(result: String?, chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>) {
|
||||
private fun checkResultAndUpdateShareInfoSuccessfulSendTime(result: String?, chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>):Boolean {
|
||||
if (result != null) {
|
||||
try {
|
||||
val jsonResult = JSONObject(result)
|
||||
|
@ -186,22 +360,12 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
chatsShareInfo.forEach { (_, shareInfo) ->
|
||||
shareInfo.lastSuccessfulSendTimeMs = currentTime
|
||||
}
|
||||
return true
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndSendViaBotMessages(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, location: TdApi.Location, osmandBot: TdApi.User) {
|
||||
val device = app.settings.getCurrentSharingDevice()
|
||||
if (device != null) {
|
||||
chatsShareInfo.forEach { (_, shareInfo) ->
|
||||
if (shareInfo.shouldSendViaBotMessage) {
|
||||
app.telegramHelper.sendViaBotLocationMessage(osmandBot.id, shareInfo, location, device,app.settings.shareTypeValue)
|
||||
shareInfo.shouldSendViaBotMessage = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun refreshNotification() {
|
||||
|
@ -216,4 +380,4 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
|||
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = TelegramHelper.MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC - 1
|
||||
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC + 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,10 +8,11 @@ import net.osmand.aidl.map.ALatLon
|
|||
import net.osmand.aidl.maplayer.point.AMapPoint
|
||||
import net.osmand.telegram.R
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation
|
||||
import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper.ListItem
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
import java.io.File
|
||||
import java.util.concurrent.Executors
|
||||
|
@ -62,7 +63,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
|||
execOsmandApi {
|
||||
val messages = telegramHelper.getMessages()
|
||||
for (message in messages) {
|
||||
val date = telegramHelper.getLastUpdatedTime(message)
|
||||
val date = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
val messageShowingTime = System.currentTimeMillis() / 1000 - date
|
||||
if (messageShowingTime > app.settings.locHistoryTime) {
|
||||
removeMapPoint(message.chatId, message)
|
||||
|
@ -78,9 +79,9 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
|||
val chatId = message.chatId
|
||||
val chatTitle = telegramHelper.getChat(message.chatId)?.title
|
||||
val content = message.content
|
||||
val date = telegramHelper.getLastUpdatedTime(message)
|
||||
val date = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime
|
||||
if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserTextLocation && content.isValid()))) {
|
||||
if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserLocation && content.isValid()))) {
|
||||
var userName = ""
|
||||
var photoPath: String? = null
|
||||
val user = telegramHelper.getUser(message.senderUserId)
|
||||
|
@ -105,7 +106,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
|||
val params = generatePointParams(photoPath, stale)
|
||||
val aLatLon = when (content) {
|
||||
is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude)
|
||||
is MessageUserTextLocation -> ALatLon(content.lat, content.lon)
|
||||
is MessageUserLocation -> ALatLon(content.lat, content.lon)
|
||||
else -> null
|
||||
}
|
||||
if (aLatLon != null) {
|
||||
|
@ -250,7 +251,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
|||
|
||||
private fun removeMapPoint(chatId: Long, message: TdApi.Message) {
|
||||
val content = message.content
|
||||
if (content is TdApi.MessageLocation || content is MessageUserTextLocation) {
|
||||
if (content is TdApi.MessageLocation || content is MessageUserLocation) {
|
||||
osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}")
|
||||
} else if (content is MessageOsmAndBotLocation) {
|
||||
osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.name}")
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
package net.osmand.telegram.helpers
|
||||
|
||||
import android.text.TextUtils
|
||||
import net.osmand.Location
|
||||
import net.osmand.PlatformUtil
|
||||
import net.osmand.telegram.SHARE_TYPE_MAP
|
||||
import net.osmand.telegram.SHARE_TYPE_MAP_AND_TEXT
|
||||
import net.osmand.telegram.SHARE_TYPE_TEXT
|
||||
import net.osmand.telegram.TelegramSettings
|
||||
import net.osmand.telegram.*
|
||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.*
|
||||
import net.osmand.telegram.utils.BASE_SHARING_URL
|
||||
import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_DIR
|
||||
import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_EXT
|
||||
import net.osmand.util.GeoPointParserUtil
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.DEVICE_PREFIX
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.USER_TEXT_LOCATION_TITLE
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.getLastUpdatedTime
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.parseOsmAndBotLocation
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.parseOsmAndBotLocationContent
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.parseTextLocation
|
||||
import org.drinkless.td.libcore.telegram.Client
|
||||
import org.drinkless.td.libcore.telegram.Client.ResultHandler
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executors
|
||||
|
@ -36,32 +38,6 @@ class TelegramHelper private constructor() {
|
|||
private const val IGNORED_ERROR_CODE = 406
|
||||
private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5
|
||||
|
||||
private const val DEVICE_PREFIX = "Device: "
|
||||
private const val LOCATION_PREFIX = "Location: "
|
||||
private const val LAST_LOCATION_PREFIX = "Last location: "
|
||||
private const val UPDATED_PREFIX = "Updated: "
|
||||
private const val USER_TEXT_LOCATION_TITLE = "\uD83D\uDDFA OsmAnd sharing:"
|
||||
|
||||
private const val SHARING_LINK = "https://play.google.com/store/apps/details?id=net.osmand.telegram"
|
||||
|
||||
private const val ALTITUDE_PREFIX = "Altitude: "
|
||||
private const val SPEED_PREFIX = "Speed: "
|
||||
private const val HDOP_PREFIX = "Horizontal precision: "
|
||||
|
||||
private const val NOW = "now"
|
||||
private const val FEW_SECONDS_AGO = "few seconds ago"
|
||||
private const val SECONDS_AGO_SUFFIX = " seconds ago"
|
||||
private const val MINUTES_AGO_SUFFIX = " minutes ago"
|
||||
private const val HOURS_AGO_SUFFIX = " hours ago"
|
||||
private const val UTC_FORMAT_SUFFIX = " UTC"
|
||||
|
||||
private val UTC_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
|
||||
private val UTC_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
// min and max values for the Telegram API
|
||||
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
|
||||
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day
|
||||
|
@ -172,6 +148,8 @@ class TelegramHelper private constructor() {
|
|||
|
||||
fun getCurrentUser() = currentUser
|
||||
|
||||
fun getCurrentUserId() = currentUser?.id ?: -1
|
||||
|
||||
fun getUserMessage(user: TdApi.User) =
|
||||
usersLocationMessages.values.firstOrNull { it.senderUserId == user.id }
|
||||
|
||||
|
@ -216,15 +194,6 @@ class TelegramHelper private constructor() {
|
|||
return chat.type is TdApi.ChatTypeSupergroup || chat.type is TdApi.ChatTypeBasicGroup
|
||||
}
|
||||
|
||||
fun getLastUpdatedTime(message: TdApi.Message): Int {
|
||||
val content = message.content
|
||||
return when (content) {
|
||||
is MessageOsmAndBotLocation -> content.lastUpdated
|
||||
is MessageUserTextLocation -> content.lastUpdated
|
||||
else -> Math.max(message.editDate, message.date)
|
||||
}
|
||||
}
|
||||
|
||||
fun isPrivateChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypePrivate
|
||||
|
||||
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
|
||||
|
@ -324,7 +293,7 @@ class TelegramHelper private constructor() {
|
|||
try {
|
||||
log.debug("Loading native tdlib...")
|
||||
System.loadLibrary("tdjni")
|
||||
Client.setLogVerbosityLevel(0)
|
||||
Client.setLogVerbosityLevel(1)
|
||||
libraryLoaded = true
|
||||
} catch (e: Throwable) {
|
||||
log.error("Failed to load tdlib", e)
|
||||
|
@ -349,6 +318,12 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
fun networkChange(networkType: TdApi.NetworkType) {
|
||||
client?.send(TdApi.SetNetworkType(networkType)) { obj ->
|
||||
log.debug(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fun isInit() = client != null && haveAuthorization
|
||||
|
||||
fun getUserPhotoPath(user: TdApi.User?) = when {
|
||||
|
@ -421,7 +396,7 @@ class TelegramHelper private constructor() {
|
|||
|
||||
private fun isUserLocationMessage(message: TdApi.Message): Boolean {
|
||||
val cont = message.content
|
||||
return (cont is MessageUserTextLocation || cont is TdApi.MessageLocation)
|
||||
return (cont is MessageUserLocation || cont is TdApi.MessageLocation)
|
||||
}
|
||||
|
||||
private fun hasLocalUserPhoto(user: TdApi.User): Boolean {
|
||||
|
@ -713,10 +688,6 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
fun loadMessage(chatId: Long, messageId: Long) {
|
||||
requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage)
|
||||
}
|
||||
|
||||
private fun requestMessage(chatId: Long, messageId: Long, onComplete: (TdApi.Message) -> Unit) {
|
||||
client?.send(TdApi.GetMessage(chatId, messageId)) { obj ->
|
||||
if (obj is TdApi.Message) {
|
||||
|
@ -728,7 +699,10 @@ class TelegramHelper private constructor() {
|
|||
private fun addNewMessage(message: TdApi.Message) {
|
||||
lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, Math.max(message.date, message.editDate))
|
||||
if (message.isAppropriate()) {
|
||||
log.debug("addNewMessage: $message")
|
||||
if (message.isOutgoing) {
|
||||
return
|
||||
}
|
||||
log.debug("addNewMessage: ${message.id}")
|
||||
val fromBot = isOsmAndBot(message.senderUserId)
|
||||
val viaBot = isOsmAndBot(message.viaBotUserId)
|
||||
val oldContent = message.content
|
||||
|
@ -741,21 +715,15 @@ class TelegramHelper private constructor() {
|
|||
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
|
||||
message.content = parseOsmAndBotLocation(message)
|
||||
}
|
||||
if (message.isOutgoing) {
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(message))
|
||||
}
|
||||
} else {
|
||||
removeOldMessages(message, fromBot, viaBot)
|
||||
val oldMessage = usersLocationMessages.values.firstOrNull { getSenderMessageId(it) == getSenderMessageId(message) && !fromBot && !viaBot }
|
||||
val hasNewerMessage = oldMessage != null && (Math.max(message.editDate, message.date) < Math.max(oldMessage.editDate, oldMessage.date))
|
||||
if (!hasNewerMessage) {
|
||||
usersLocationMessages[message.id] = message
|
||||
}
|
||||
incomingMessagesListeners.forEach {
|
||||
if (!hasNewerMessage || it is SavingTracksDbHelper) {
|
||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||
}
|
||||
removeOldMessages(message, fromBot, viaBot)
|
||||
val oldMessage = usersLocationMessages.values.firstOrNull { getSenderMessageId(it) == getSenderMessageId(message) && !fromBot && !viaBot }
|
||||
val hasNewerMessage = oldMessage != null && (Math.max(message.editDate, message.date) < Math.max(oldMessage.editDate, oldMessage.date))
|
||||
if (!hasNewerMessage) {
|
||||
usersLocationMessages[message.id] = message
|
||||
}
|
||||
incomingMessagesListeners.forEach {
|
||||
if (!hasNewerMessage || it is TelegramService) {
|
||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -779,34 +747,13 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
} else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)
|
||||
&& Math.max(newMessage.editDate, newMessage.date) > Math.max(message.editDate, message.date)) {
|
||||
&& Math.max(newMessage.editDate, newMessage.date) >= Math.max(message.editDate, message.date)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(
|
||||
|
@ -854,10 +801,7 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun recreateLiveLocationMessage(
|
||||
shareInfo: TelegramSettings.ShareChatInfo,
|
||||
content: TdApi.InputMessageContent
|
||||
) {
|
||||
private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
|
||||
if (shareInfo.chatId != -1L) {
|
||||
val array = LongArray(1)
|
||||
if (content is TdApi.InputMessageLocation) {
|
||||
|
@ -889,71 +833,67 @@ class TelegramHelper private constructor() {
|
|||
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
|
||||
needRefreshActiveLiveLocationMessages = true
|
||||
log.debug("sendNewLiveLocationMessage")
|
||||
client?.send(
|
||||
TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
shareInfo.shouldDeletePreviousMapMessage = false
|
||||
shareInfo.currentMapMessageId = -1
|
||||
} else {
|
||||
log.debug("EditMessageLiveLocation - $msgId")
|
||||
client?.send(
|
||||
TdApi.EditMessageLiveLocation(chatId, msgId, null, location)) { obj ->
|
||||
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
} else if (!shareInfo.pendingMapMessage || shareInfo.pendingMapMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) {
|
||||
sendNewLiveLocationMessage(shareInfo, content)
|
||||
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||
if (content is TdApi.InputMessageText) {
|
||||
handleTextLocationMessageUpdate(obj, shareInfo)
|
||||
} else if (content is TdApi.InputMessageLocation) {
|
||||
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendLiveLocationText(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, location: Location) {
|
||||
chatsShareInfo.forEach { (chatId, shareInfo) ->
|
||||
if (shareInfo.getChatLiveMessageExpireTime() <= 0) {
|
||||
return@forEach
|
||||
fun sendNewTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) {
|
||||
shareInfo.updateTextMessageId = 1
|
||||
val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location)
|
||||
if (!shareInfo.pendingTextMessage) {
|
||||
shareInfo.pendingTextMessage = true
|
||||
shareInfo.pendingTdLib++
|
||||
log.error("sendNewTextLocation ${shareInfo.pendingTdLib}")
|
||||
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||
handleTextLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
val msgId = shareInfo.currentTextMessageId
|
||||
if (msgId == -1L) {
|
||||
shareInfo.updateTextMessageId = 1
|
||||
}
|
||||
}
|
||||
|
||||
fun editTextLocation(shareInfo: TelegramSettings.ShareChatInfo, location: LocationMessages.BufferMessage) {
|
||||
val content = OsmandLocationUtils.getTextMessageContent(shareInfo.updateTextMessageId, location)
|
||||
if (shareInfo.currentTextMessageId!=-1L) {
|
||||
shareInfo.pendingTdLib++
|
||||
log.info("editTextLocation ${shareInfo.currentTextMessageId} pendingTdLib: ${shareInfo.pendingTdLib}")
|
||||
client?.send(TdApi.EditMessageText(shareInfo.chatId, shareInfo.currentTextMessageId, null, content)) { obj ->
|
||||
handleTextLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
val content = 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)
|
||||
shareInfo.shouldDeletePreviousTextMessage = false
|
||||
} else {
|
||||
client?.send(TdApi.EditMessageText(chatId, msgId, null, content)) { obj ->
|
||||
handleTextLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
} else if (!shareInfo.pendingTextMessage || shareInfo.pendingTextMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) {
|
||||
client?.send(TdApi.SendMessage(chatId, 0, false, false, null, content)) { obj ->
|
||||
handleTextLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
|
||||
} else {
|
||||
shareInfo.livePeriod.toInt()
|
||||
}
|
||||
val content = TdApi.InputMessageLocation(location, livePeriod)
|
||||
if (!shareInfo.pendingMapMessage) {
|
||||
shareInfo.pendingMapMessage = true
|
||||
shareInfo.pendingTdLib++
|
||||
log.error("sendNewMapLocation ${shareInfo.pendingTdLib}")
|
||||
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun editMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) {
|
||||
needRefreshActiveLiveLocationMessages = true
|
||||
val location = TdApi.Location(locationMessage.lat, locationMessage.lon)
|
||||
if (shareInfo.currentMapMessageId!=-1L) {
|
||||
shareInfo.pendingTdLib++
|
||||
log.info("editMapLocation ${shareInfo.currentMapMessageId} pendingTdLib: ${shareInfo.pendingTdLib}")
|
||||
client?.send(TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, location)) { obj ->
|
||||
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -961,6 +901,7 @@ class TelegramHelper private constructor() {
|
|||
private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
||||
when (obj.constructor) {
|
||||
TdApi.Error.CONSTRUCTOR -> {
|
||||
log.debug("handleMapLocationMessageUpdate - ERROR $obj")
|
||||
val error = obj as TdApi.Error
|
||||
needRefreshActiveLiveLocationMessages = true
|
||||
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
||||
|
@ -978,6 +919,8 @@ class TelegramHelper private constructor() {
|
|||
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
||||
shareInfo.hasSharingError = true
|
||||
needRefreshActiveLiveLocationMessages = true
|
||||
shareInfo.pendingMapMessage = false
|
||||
log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onSendLiveLocationError(-1, "Map location message ${obj.id} failed to send")
|
||||
}
|
||||
|
@ -986,9 +929,14 @@ class TelegramHelper private constructor() {
|
|||
shareInfo.pendingMapMessage = true
|
||||
shareInfo.lastSendMapMessageTime = obj.date
|
||||
log.debug("handleMapLocationMessageUpdate - MessageSendingStatePending")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(obj))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
shareInfo.hasSharingError = false
|
||||
shareInfo.pendingMapMessage = false
|
||||
log.debug("handleMapLocationMessageUpdate - MessageSendingStateSuccess")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(obj))
|
||||
}
|
||||
|
@ -1002,6 +950,7 @@ class TelegramHelper private constructor() {
|
|||
private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
||||
when (obj.constructor) {
|
||||
TdApi.Error.CONSTRUCTOR -> {
|
||||
log.debug("handleTextLocationMessageUpdate - ERROR")
|
||||
val error = obj as TdApi.Error
|
||||
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
||||
shareInfo.shouldDeletePreviousTextMessage = true
|
||||
|
@ -1017,7 +966,9 @@ class TelegramHelper private constructor() {
|
|||
when {
|
||||
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
||||
shareInfo.hasSharingError = true
|
||||
shareInfo.pendingTextMessage = false
|
||||
needRefreshActiveLiveLocationMessages = true
|
||||
log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onSendLiveLocationError(-1, "Text location message ${obj.id} failed to send")
|
||||
}
|
||||
|
@ -1026,9 +977,14 @@ class TelegramHelper private constructor() {
|
|||
shareInfo.pendingTextMessage = true
|
||||
shareInfo.lastSendTextMessageTime = obj.date
|
||||
log.debug("handleTextLocationMessageUpdate - MessageSendingStatePending")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(obj))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
shareInfo.hasSharingError = false
|
||||
shareInfo.pendingTextMessage = false
|
||||
log.debug("handleTextLocationMessageUpdate - MessageSendingStateSuccess")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(obj))
|
||||
}
|
||||
|
@ -1039,54 +995,6 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun formatLocation(sig: Location): String {
|
||||
return String.format(Locale.US, "%.5f, %.5f", sig.latitude, sig.longitude)
|
||||
}
|
||||
|
||||
private fun formatFullTime(ti: Long): String {
|
||||
val dt = Date(ti)
|
||||
return UTC_DATE_FORMAT.format(dt) + " " + UTC_TIME_FORMAT.format(dt) + " UTC"
|
||||
}
|
||||
|
||||
private fun getTextMessageContent(updateId: Int, location: Location): 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.latitude}&lon=${location.longitude}")))
|
||||
builder.append("$locationMessage\n")
|
||||
|
||||
if (location.hasAltitude() && 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.hasSpeed() && 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.hasAccuracy() && location.accuracy != 0.0f && location.speed == 0.0f) {
|
||||
entities.add(TdApi.TextEntity(builder.lastIndex, HDOP_PREFIX.length, TdApi.TextEntityTypeBold()))
|
||||
builder.append(String.format(Locale.US, "$HDOP_PREFIX%d m\n", location.accuracy.toInt()))
|
||||
}
|
||||
if (updateId == 0) {
|
||||
builder.append(String.format("$UPDATED_PREFIX%s\n", formatFullTime(location.time)))
|
||||
} else {
|
||||
builder.append(String.format("$UPDATED_PREFIX%s (%d)\n", formatFullTime(location.time), updateId))
|
||||
}
|
||||
val textMessage = builder.toString().trim()
|
||||
|
||||
return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true)
|
||||
}
|
||||
|
||||
fun logout(): Boolean {
|
||||
return if (libraryLoaded) {
|
||||
haveAuthorization = false
|
||||
|
@ -1215,188 +1123,6 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun parseOsmAndBotLocation(message: TdApi.Message): MessageOsmAndBotLocation {
|
||||
val messageLocation = message.content as TdApi.MessageLocation
|
||||
return MessageOsmAndBotLocation().apply {
|
||||
name = getOsmAndBotDeviceName(message)
|
||||
lat = messageLocation.location.latitude
|
||||
lon = messageLocation.location.longitude
|
||||
lastUpdated = getLastUpdatedTime(message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseOsmAndBotLocationContent(oldContent:MessageOsmAndBotLocation, content: TdApi.MessageContent): MessageOsmAndBotLocation {
|
||||
val messageLocation = content as TdApi.MessageLocation
|
||||
return MessageOsmAndBotLocation().apply {
|
||||
name = oldContent.name
|
||||
lat = messageLocation.location.latitude
|
||||
lon = messageLocation.location.longitude
|
||||
lastUpdated = (System.currentTimeMillis() / 1000).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseTextLocation(text: TdApi.FormattedText, botLocation: Boolean = true): MessageLocation {
|
||||
val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserTextLocation()
|
||||
|
||||
var locationNA = false
|
||||
for (s in text.text.lines()) {
|
||||
when {
|
||||
s.startsWith(DEVICE_PREFIX) -> {
|
||||
if (res is MessageOsmAndBotLocation) {
|
||||
res.name = s.removePrefix(DEVICE_PREFIX)
|
||||
}
|
||||
}
|
||||
s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> {
|
||||
var locStr: String
|
||||
var parse = true
|
||||
if (s.startsWith(LAST_LOCATION_PREFIX)) {
|
||||
locStr = s.removePrefix(LAST_LOCATION_PREFIX)
|
||||
if (!locationNA) {
|
||||
parse = false
|
||||
}
|
||||
} else {
|
||||
locStr = s.removePrefix(LOCATION_PREFIX)
|
||||
if (locStr.trim() == "n/a") {
|
||||
locationNA = true
|
||||
parse = false
|
||||
}
|
||||
}
|
||||
if (parse) {
|
||||
try {
|
||||
val urlTextEntity = text.entities.firstOrNull { it.type is TdApi.TextEntityTypeTextUrl }
|
||||
if (urlTextEntity != null && urlTextEntity.offset == text.text.indexOf(locStr)) {
|
||||
val url = (urlTextEntity.type as TdApi.TextEntityTypeTextUrl).url
|
||||
val point: GeoPointParserUtil.GeoParsedPoint? = GeoPointParserUtil.parse(url)
|
||||
if (point != null) {
|
||||
res.lat = point.latitude
|
||||
res.lon = point.longitude
|
||||
}
|
||||
} else {
|
||||
val (latS, lonS) = locStr.split(" ")
|
||||
res.lat = latS.dropLast(1).toDouble()
|
||||
res.lon = lonS.toDouble()
|
||||
|
||||
val timeIndex = locStr.indexOf("(")
|
||||
if (timeIndex != -1) {
|
||||
val updatedS = locStr.substring(timeIndex, locStr.length)
|
||||
res.lastUpdated = (parseTime(updatedS.removePrefix("(").removeSuffix(")")) / 1000).toInt()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
s.startsWith(ALTITUDE_PREFIX) -> {
|
||||
val altStr = s.removePrefix(ALTITUDE_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.altitude = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(SPEED_PREFIX) -> {
|
||||
val altStr = s.removePrefix(SPEED_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.speed = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(HDOP_PREFIX) -> {
|
||||
val altStr = s.removePrefix(HDOP_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.hdop = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(UPDATED_PREFIX) -> {
|
||||
if (res.lastUpdated == 0) {
|
||||
val updatedStr = s.removePrefix(UPDATED_PREFIX)
|
||||
val endIndex = updatedStr.indexOf("(")
|
||||
val updatedS = updatedStr.substring(0, if (endIndex != -1) endIndex else updatedStr.length)
|
||||
val parsedTime = (parseTime(updatedS.trim()) / 1000).toInt()
|
||||
val currentTime = (System.currentTimeMillis() / 1000) - 1
|
||||
res.lastUpdated = if (parsedTime < currentTime) parsedTime else currentTime.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
private fun parseTime(timeS: String): Long {
|
||||
try {
|
||||
when {
|
||||
timeS.endsWith(FEW_SECONDS_AGO) -> return System.currentTimeMillis() - 5000
|
||||
|
||||
timeS.endsWith(SECONDS_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX)
|
||||
return System.currentTimeMillis() - locStr.toLong() * 1000
|
||||
}
|
||||
timeS.endsWith(MINUTES_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX)
|
||||
val minutes = locStr.toLong()
|
||||
return System.currentTimeMillis() - minutes * 60 * 1000
|
||||
}
|
||||
timeS.endsWith(HOURS_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX)
|
||||
val hours = locStr.toLong()
|
||||
return (System.currentTimeMillis() - hours * 60 * 60 * 1000)
|
||||
}
|
||||
timeS.endsWith(UTC_FORMAT_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX)
|
||||
val (latS, lonS) = locStr.split(" ")
|
||||
val date = UTC_DATE_FORMAT.parse(latS)
|
||||
val time = UTC_TIME_FORMAT.parse(lonS)
|
||||
val res = date.time + time.time
|
||||
return res
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
abstract class MessageLocation : TdApi.MessageContent() {
|
||||
|
||||
var lat: Double = Double.NaN
|
||||
internal set
|
||||
var lon: Double = Double.NaN
|
||||
internal set
|
||||
var lastUpdated: Int = 0
|
||||
internal set
|
||||
var speed: Double = 0.0
|
||||
internal set
|
||||
var altitude: Double = 0.0
|
||||
internal set
|
||||
var hdop: Double = 0.0
|
||||
internal set
|
||||
|
||||
override fun getConstructor() = -1
|
||||
|
||||
abstract fun isValid(): Boolean
|
||||
}
|
||||
|
||||
class MessageOsmAndBotLocation : MessageLocation() {
|
||||
|
||||
var name: String = ""
|
||||
internal set
|
||||
|
||||
override fun isValid() = name != "" && lat != Double.NaN && lon != Double.NaN
|
||||
}
|
||||
|
||||
class MessageUserTextLocation : MessageLocation() {
|
||||
|
||||
override fun isValid() = lat != Double.NaN && lon != Double.NaN
|
||||
|
||||
}
|
||||
|
||||
class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long, internal val isChannel: Boolean) : Comparable<OrderedChat> {
|
||||
|
||||
override fun compareTo(other: OrderedChat): Int {
|
||||
|
@ -1583,26 +1309,10 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
}
|
||||
TdApi.UpdateMessageEdited.CONSTRUCTOR -> {
|
||||
val updateMessageEdited = obj as TdApi.UpdateMessageEdited
|
||||
val message = usersLocationMessages[updateMessageEdited.messageId]
|
||||
if (message == null) {
|
||||
updateMessageEdited.apply {
|
||||
requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage)
|
||||
}
|
||||
} else {
|
||||
synchronized(message) {
|
||||
message.editDate = updateMessageEdited.editDate
|
||||
lastTelegramUpdateTime = Math.max(message.date, message.editDate)
|
||||
}
|
||||
incomingMessagesListeners.forEach {
|
||||
it.updateLocationMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
TdApi.UpdateMessageContent.CONSTRUCTOR -> {
|
||||
val updateMessageContent = obj as TdApi.UpdateMessageContent
|
||||
val message = usersLocationMessages[updateMessageContent.messageId]
|
||||
log.debug("UpdateMessageContent " + updateMessageContent.messageId)
|
||||
if (message == null) {
|
||||
updateMessageContent.apply {
|
||||
requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage)
|
||||
|
@ -1622,7 +1332,6 @@ class TelegramHelper private constructor() {
|
|||
newContent
|
||||
}
|
||||
}
|
||||
log.debug("UpdateMessageContent " + message.senderUserId)
|
||||
incomingMessagesListeners.forEach {
|
||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||
}
|
||||
|
@ -1630,6 +1339,7 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
TdApi.UpdateNewMessage.CONSTRUCTOR -> {
|
||||
addNewMessage((obj as TdApi.UpdateNewMessage).message)
|
||||
log.debug("UpdateNewMessage " + obj.message.id)
|
||||
}
|
||||
TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> {
|
||||
val updateChat = obj as TdApi.UpdateMessageMentionRead
|
||||
|
@ -1736,9 +1446,9 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> {
|
||||
val udateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded
|
||||
val message = udateMessageSendSucceeded.message
|
||||
log.debug("UpdateMessageSendSucceeded: $message")
|
||||
val updateSucceeded = obj as TdApi.UpdateMessageSendSucceeded
|
||||
val message = updateSucceeded.message
|
||||
log.debug("UpdateMessageSendSucceeded: ${message.id} oldId: ${updateSucceeded.oldMessageId}")
|
||||
outgoingMessagesListeners.forEach {
|
||||
it.onUpdateMessages(listOf(message))
|
||||
}
|
||||
|
|
|
@ -6,9 +6,10 @@ import android.widget.ImageView
|
|||
import net.osmand.data.LatLon
|
||||
import net.osmand.telegram.R
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation
|
||||
import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation
|
||||
import net.osmand.telegram.utils.GPXUtilities
|
||||
import net.osmand.telegram.utils.GPXUtilities.GPXFile
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
|
||||
object TelegramUiHelper {
|
||||
|
@ -65,11 +66,11 @@ object TelegramUiHelper {
|
|||
val user = helper.getUser(userId)
|
||||
val message = messages.firstOrNull { it.viaBotUserId == 0 }
|
||||
if (message != null) {
|
||||
res.lastUpdated = helper.getLastUpdatedTime(message)
|
||||
res.lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
val content = message.content
|
||||
if (content is TdApi.MessageLocation) {
|
||||
res.latLon = LatLon(content.location.latitude, content.location.longitude)
|
||||
} else if (content is MessageUserTextLocation) {
|
||||
} else if (content is MessageUserLocation) {
|
||||
res.latLon = LatLon(content.lat, content.lon)
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +111,7 @@ object TelegramUiHelper {
|
|||
val content = message.content
|
||||
return when (content) {
|
||||
is MessageOsmAndBotLocation -> botMessageToLocationItem(chat, content)
|
||||
is MessageUserTextLocation -> locationMessageToLocationItem(helper, chat, message)
|
||||
is MessageUserLocation -> locationMessageToLocationItem(helper, chat, message)
|
||||
is TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message)
|
||||
else -> null
|
||||
}
|
||||
|
@ -125,12 +126,12 @@ object TelegramUiHelper {
|
|||
return when (content) {
|
||||
is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content)
|
||||
is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message)
|
||||
is MessageUserTextLocation -> locationMessageToChatItem(helper, chat, message)
|
||||
is MessageUserLocation -> locationMessageToChatItem(helper, chat, message)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun gpxToChatItem(helper: TelegramHelper, gpx: GPXUtilities.GPXFile, simpleUserItem: Boolean): GpxChatItem? {
|
||||
fun gpxToChatItem(helper: TelegramHelper, gpx: GPXFile, simpleUserItem: Boolean): GpxChatItem? {
|
||||
return if (simpleUserItem) gpxToUserGpxChatItem(helper, gpx) else gpxToGpxChatItem(helper, gpx)
|
||||
}
|
||||
|
||||
|
@ -165,14 +166,14 @@ object TelegramUiHelper {
|
|||
name = TelegramUiHelper.getUserName(user)
|
||||
latLon = when (content) {
|
||||
is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude)
|
||||
is MessageUserTextLocation -> LatLon(content.lat, content.lon)
|
||||
is MessageUserLocation -> LatLon(content.lat, content.lon)
|
||||
else -> null
|
||||
}
|
||||
photoPath = helper.getUserPhotoPath(user)
|
||||
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
|
||||
placeholderId = R.drawable.img_user_picture
|
||||
userId = message.senderUserId
|
||||
lastUpdated = helper.getLastUpdatedTime(message)
|
||||
lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,7 +212,7 @@ object TelegramUiHelper {
|
|||
name = TelegramUiHelper.getUserName(user)
|
||||
latLon = when (content) {
|
||||
is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude)
|
||||
is MessageUserTextLocation -> LatLon(content.lat, content.lon)
|
||||
is MessageUserLocation -> LatLon(content.lat, content.lon)
|
||||
else -> null
|
||||
}
|
||||
if (helper.isGroup(chat)) {
|
||||
|
@ -225,13 +226,13 @@ object TelegramUiHelper {
|
|||
userId = message.senderUserId
|
||||
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
|
||||
chatWithBot = helper.isBot(userId)
|
||||
lastUpdated = helper.getLastUpdatedTime(message)
|
||||
lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun gpxToGpxChatItem(
|
||||
helper: TelegramHelper,
|
||||
gpx: GPXUtilities.GPXFile
|
||||
gpx: GPXFile
|
||||
): GpxChatItem? {
|
||||
val user = helper.getUser(gpx.userId) ?: return null
|
||||
val chat = helper.getChat(gpx.chatId) ?: return null
|
||||
|
@ -255,9 +256,36 @@ object TelegramUiHelper {
|
|||
}
|
||||
}
|
||||
|
||||
fun locationMessagesToChatItem(
|
||||
helper: TelegramHelper,
|
||||
messages: List<LocationMessages.LocationMessage>
|
||||
): LocationMessagesChatItem? {
|
||||
val message = messages.firstOrNull()
|
||||
val user = helper.getUser(message?.userId ?: -1) ?: return null
|
||||
val chat = helper.getChat(message?.chatId ?: -1) ?: return null
|
||||
return LocationMessagesChatItem().apply {
|
||||
chatId = chat.id
|
||||
chatTitle = chat.title
|
||||
locationMessages = messages
|
||||
name = TelegramUiHelper.getUserName(user)
|
||||
if (helper.isGroup(chat)) {
|
||||
photoPath = helper.getUserPhotoPath(user)
|
||||
groupPhotoPath = chat.photo?.small?.local?.path
|
||||
} else {
|
||||
photoPath = user.profilePhoto?.small?.local?.path
|
||||
}
|
||||
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
|
||||
placeholderId = R.drawable.img_user_picture
|
||||
userId = user.id
|
||||
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
|
||||
chatWithBot = helper.isBot(userId)
|
||||
lastUpdated = (messages.maxBy { it.time }?.time ?: -1).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun gpxToUserGpxChatItem(
|
||||
helper: TelegramHelper,
|
||||
gpx: GPXUtilities.GPXFile
|
||||
gpx: GPXFile
|
||||
): GpxChatItem? {
|
||||
val user = helper.getUser(gpx.userId) ?: return null
|
||||
return GpxChatItem().apply {
|
||||
|
@ -322,7 +350,25 @@ object TelegramUiHelper {
|
|||
|
||||
class GpxChatItem : ListItem() {
|
||||
|
||||
var gpxFile: GPXUtilities.GPXFile? = null
|
||||
var gpxFile: GPXFile? = null
|
||||
internal set
|
||||
var groupPhotoPath: String? = null
|
||||
internal set
|
||||
var privateChat: Boolean = false
|
||||
internal set
|
||||
var chatWithBot: Boolean = false
|
||||
internal set
|
||||
|
||||
override fun canBeOpenedOnMap() = latLon != null
|
||||
|
||||
override fun getMapPointId() = "${chatId}_$userId"
|
||||
|
||||
override fun getVisibleName() = chatTitle
|
||||
}
|
||||
|
||||
class LocationMessagesChatItem : ListItem() {
|
||||
|
||||
var locationMessages: List<LocationMessages.LocationMessage> = emptyList()
|
||||
internal set
|
||||
var groupPhotoPath: String? = null
|
||||
internal set
|
||||
|
|
|
@ -19,7 +19,6 @@ import net.osmand.telegram.TelegramApplication
|
|||
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
||||
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
|
||||
import net.osmand.telegram.TelegramSettings
|
||||
import net.osmand.telegram.helpers.SavingTracksDbHelper
|
||||
import net.osmand.telegram.helpers.TelegramHelper.*
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem
|
||||
|
|
|
@ -19,6 +19,8 @@ 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
|
||||
import net.osmand.telegram.helpers.TelegramHelper.*
|
||||
|
@ -183,7 +185,6 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
|||
override fun onStop() {
|
||||
super.onStop()
|
||||
settings.save()
|
||||
app.messagesDbHelper.saveMessages()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -291,6 +292,11 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
|||
if (!app.showLocationHelper.showingLocation && settings.hasAnyChatToShowOnMap()) {
|
||||
app.showLocationHelper.startShowingLocation()
|
||||
}
|
||||
if (app.telegramService == null) {
|
||||
messages.forEach {
|
||||
app.locationMessages.addNewLocationMessage(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {}
|
||||
|
@ -341,7 +347,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
|||
fun logoutTelegram(silent: Boolean = false) {
|
||||
if (telegramHelper.getTelegramAuthorizationState() == TelegramHelper.TelegramAuthorizationState.READY) {
|
||||
if (app.isInternetConnectionAvailable) {
|
||||
app.messagesDbHelper.clearMessages()
|
||||
app.locationMessages.clearBufferedMessages()
|
||||
settings.clear()
|
||||
telegramHelper.logout()
|
||||
} else {
|
||||
|
|
|
@ -231,7 +231,6 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateCurrentUserPhoto()
|
||||
telegramHelper.getActiveLiveLocationMessages(null)
|
||||
updateContent()
|
||||
updateEnable = true
|
||||
startHandler()
|
||||
|
@ -599,6 +598,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
}
|
||||
|
||||
holder.title?.text = title
|
||||
holder.icon?.setOnClickListener {
|
||||
app.forceUpdateMyLocation()
|
||||
}
|
||||
if (holder is ChatViewHolder) {
|
||||
holder.description?.visibility = View.GONE
|
||||
if (live) {
|
||||
|
@ -714,6 +716,17 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true)
|
||||
)})"
|
||||
}
|
||||
holder.gpsPointsCollected?.apply {
|
||||
if (shareInfo != null) {
|
||||
val bufferedMessages = shareInfo.pendingTdLib + app.locationMessages.getBufferedMessagesForChat(shareInfo.chatId).size
|
||||
text = "$bufferedMessages"
|
||||
}
|
||||
}
|
||||
holder.gpsPointsSent?.apply {
|
||||
if (shareInfo != null) {
|
||||
text = "${shareInfo.sentMessages}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,10 +764,12 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
val stopSharingDescr: TextView? = view.findViewById(R.id.stop_in)
|
||||
val stopSharingFirstPart: TextView? = view.findViewById(R.id.ending_in_first_part)
|
||||
val stopSharingSecondPart: TextView? = view.findViewById(R.id.ending_in_second_part)
|
||||
val gpsPointsCollected: TextView? = view.findViewById(R.id.gps_points_collected)
|
||||
val gpsPointsSent: TextView? = view.findViewById(R.id.gps_points_sent)
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionButtonsListener {
|
||||
fun switchButtonsVisibility(visible: Boolean)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import net.osmand.telegram.helpers.TelegramUiHelper
|
|||
import net.osmand.telegram.ui.SetTimeDialogFragment.SetTimeListAdapter.ChatViewHolder
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.OsmandFormatter
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import net.osmand.telegram.utils.UiUtils
|
||||
import net.osmand.util.MapUtils
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
|
@ -328,7 +329,7 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
|||
val message = telegramHelper.getChatMessages(itemId).firstOrNull()
|
||||
val content = message?.content
|
||||
if (message != null && content is TdApi.MessageLocation && (location != null && content.location != null)) {
|
||||
val lastUpdated = telegramHelper.getLastUpdatedTime(message)
|
||||
val lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||
holder.description?.visibility = View.VISIBLE
|
||||
holder.description?.text = OsmandFormatter.getListItemLiveTimeDescr(app, lastUpdated)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.DatePickerDialog
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
|
@ -17,11 +18,11 @@ import android.widget.TextView
|
|||
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.TelegramUiHelper
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper.ListItem
|
||||
import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.GPXUtilities
|
||||
import net.osmand.telegram.utils.OsmandFormatter
|
||||
import java.util.*
|
||||
|
||||
|
@ -38,13 +39,14 @@ class TimelineTabFragment : Fragment() {
|
|||
|
||||
private lateinit var adapter: LiveNowListAdapter
|
||||
|
||||
private lateinit var dateStartBtn: TextView
|
||||
private lateinit var dateEndBtn: TextView
|
||||
private lateinit var dateBtn: TextView
|
||||
private lateinit var mainView: View
|
||||
|
||||
private var start = 0L
|
||||
private var end = 0L
|
||||
|
||||
private var updateEnable: Boolean = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -74,43 +76,50 @@ class TimelineTabFragment : Fragment() {
|
|||
monitoringTv.setText(if (monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled)
|
||||
}
|
||||
|
||||
dateStartBtn = mainView.findViewById<TextView>(R.id.date_start_btn).apply {
|
||||
dateBtn = mainView.findViewById<TextView>(R.id.date_btn).apply {
|
||||
setOnClickListener {
|
||||
selectStartDate()
|
||||
selectDate()
|
||||
}
|
||||
setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_start), null, null, null)
|
||||
setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light))
|
||||
}
|
||||
dateEndBtn = mainView.findViewById<TextView>(R.id.date_end_btn).apply {
|
||||
setOnClickListener {
|
||||
selectEndDate()
|
||||
}
|
||||
setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_add), null, null, null)
|
||||
}
|
||||
|
||||
setupBtnTextColor(dateStartBtn)
|
||||
setupBtnTextColor(dateEndBtn)
|
||||
|
||||
return mainView
|
||||
}
|
||||
|
||||
private fun setupBtnTextColor(textView: TextView) {
|
||||
textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light))
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateEnable = true
|
||||
startHandler()
|
||||
}
|
||||
|
||||
private fun selectStartDate() {
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
updateEnable = false
|
||||
}
|
||||
|
||||
private fun selectDate() {
|
||||
val dateFromDialog =
|
||||
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||
val from = Calendar.getInstance()
|
||||
from.set(Calendar.YEAR, year)
|
||||
from.set(Calendar.MONTH, monthOfYear)
|
||||
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||
|
||||
from.set(Calendar.HOUR_OF_DAY, 0)
|
||||
from.clear(Calendar.MINUTE)
|
||||
from.clear(Calendar.SECOND)
|
||||
from.clear(Calendar.MILLISECOND)
|
||||
start = from.timeInMillis
|
||||
|
||||
from.set(Calendar.HOUR_OF_DAY, 23)
|
||||
from.set(Calendar.MINUTE, 59)
|
||||
from.set(Calendar.SECOND, 59)
|
||||
from.set(Calendar.MILLISECOND, 999)
|
||||
end = from.timeInMillis
|
||||
|
||||
updateList()
|
||||
updateDateButtons()
|
||||
updateDateButton()
|
||||
}
|
||||
val startCalendar = Calendar.getInstance()
|
||||
startCalendar.timeInMillis = start
|
||||
|
@ -121,34 +130,8 @@ class TimelineTabFragment : Fragment() {
|
|||
).show()
|
||||
}
|
||||
|
||||
private fun selectEndDate() {
|
||||
val dateFromDialog =
|
||||
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||
val from = Calendar.getInstance()
|
||||
from.set(Calendar.YEAR, year)
|
||||
from.set(Calendar.MONTH, monthOfYear)
|
||||
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||
from.set(Calendar.HOUR_OF_DAY, 23)
|
||||
from.set(Calendar.MINUTE, 59)
|
||||
from.set(Calendar.SECOND, 59)
|
||||
from.set(Calendar.MILLISECOND, 999)
|
||||
end = from.timeInMillis
|
||||
updateList()
|
||||
updateDateButtons()
|
||||
}
|
||||
val endCalendar = Calendar.getInstance()
|
||||
endCalendar.timeInMillis = end
|
||||
DatePickerDialog(context, dateFromDialog,
|
||||
endCalendar.get(Calendar.YEAR),
|
||||
endCalendar.get(Calendar.MONTH),
|
||||
endCalendar.get(Calendar.DAY_OF_MONTH)
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun updateDateButtons() {
|
||||
dateStartBtn.text = OsmandFormatter.getFormattedDate(start / 1000)
|
||||
dateEndBtn.text = OsmandFormatter.getFormattedDate(end / 1000)
|
||||
dateEndBtn.setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_end), null, null, null)
|
||||
private fun updateDateButton() {
|
||||
dateBtn.text = OsmandFormatter.getFormattedDate(start / 1000)
|
||||
}
|
||||
|
||||
private fun getPressedStateIcon(@DrawableRes iconId: Int): Drawable? {
|
||||
|
@ -162,32 +145,35 @@ class TimelineTabFragment : Fragment() {
|
|||
return normal
|
||||
}
|
||||
|
||||
private fun startHandler() {
|
||||
val updateAdapter = Handler()
|
||||
updateAdapter.postDelayed({
|
||||
if (updateEnable) {
|
||||
updateList()
|
||||
startHandler()
|
||||
}
|
||||
}, ADAPTER_UPDATE_INTERVAL_MIL)
|
||||
}
|
||||
|
||||
private fun updateList() {
|
||||
val res = mutableListOf<ListItem>()
|
||||
val s = System.currentTimeMillis()
|
||||
log.debug("updateList $s")
|
||||
val ignoredUsersIds = ArrayList<Int>()
|
||||
val currentUserId = telegramHelper.getCurrentUser()?.id
|
||||
if (currentUserId != null) {
|
||||
val currentUserGpx:GPXUtilities.GPXFile? = app.savingTracksDbHelper.collectRecordedDataForUser(currentUserId, 0, start, end)
|
||||
if (currentUserGpx != null) {
|
||||
TelegramUiHelper.gpxToChatItem(telegramHelper, currentUserGpx, true)?.also {
|
||||
res.add(it)
|
||||
val outgoingMessages = app.locationMessages.getMessagesForUser(currentUserId, start, end)
|
||||
TelegramUiHelper.locationMessagesToChatItem(telegramHelper, outgoingMessages)?.also { chatItem ->
|
||||
res.add(chatItem)
|
||||
}
|
||||
}
|
||||
ignoredUsersIds.add(currentUserId)
|
||||
}
|
||||
val gpxFiles = app.savingTracksDbHelper.collectRecordedDataForUsers(start, end, ignoredUsersIds)
|
||||
val e = System.currentTimeMillis()
|
||||
|
||||
gpxFiles.forEach {
|
||||
TelegramUiHelper.gpxToChatItem(telegramHelper, it,false)?.also { chatItem ->
|
||||
res.add(chatItem)
|
||||
val ingoingMessages = app.locationMessages.getIngoingMessages(currentUserId, start, end)
|
||||
val emm = ingoingMessages.distinctBy { Pair(it.userId, it.chatId) }
|
||||
emm.forEach { message ->
|
||||
TelegramUiHelper.locationMessagesToChatItem(telegramHelper,
|
||||
ingoingMessages.filter { it.chatId == message.chatId && it.userId == message.userId })?.also { chatItem ->
|
||||
res.add(chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adapter.items = sortAdapterItems(res)
|
||||
log.debug("updateList $s dif: ${e - s}")
|
||||
}
|
||||
|
||||
private fun sortAdapterItems(list: MutableList<ListItem>): MutableList<ListItem> {
|
||||
|
@ -225,24 +211,17 @@ class TimelineTabFragment : Fragment() {
|
|||
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||
holder.lastTelegramUpdateTime?.visibility = View.GONE
|
||||
|
||||
if (item is TelegramUiHelper.GpxChatItem) {
|
||||
val gpx = item.gpxFile
|
||||
val groupDescrRowVisible = (!item.privateChat || item.chatWithBot) && item.userId != currentUserId
|
||||
if (groupDescrRowVisible) {
|
||||
holder.groupDescrContainer?.visibility = View.VISIBLE
|
||||
holder.groupTitle?.text = item.getVisibleName()
|
||||
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
|
||||
} else {
|
||||
holder.groupDescrContainer?.visibility = View.GONE
|
||||
}
|
||||
if (item is TelegramUiHelper.LocationMessagesChatItem) {
|
||||
val distance = OsmandFormatter.getFormattedDistance(getDistance(item.locationMessages),app)
|
||||
val name = if ((!item.privateChat || item.chatWithBot) && item.userId != currentUserId) item.getVisibleName() else ""
|
||||
holder.groupDescrContainer?.visibility = View.VISIBLE
|
||||
holder.groupTitle?.text = "$distance (${getString(R.string.points_size, item.locationMessages.size)}) $name"
|
||||
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
|
||||
holder.userRow?.setOnClickListener {
|
||||
if (gpx != null) {
|
||||
childFragmentManager.also {
|
||||
UserGpxInfoFragment.showInstance(it, gpx, start, end)
|
||||
}
|
||||
childFragmentManager.also {
|
||||
UserGpxInfoFragment.showInstance(it, item.userId, item.chatId, start, end)
|
||||
}
|
||||
}
|
||||
|
||||
holder.imageButton?.visibility = View.GONE
|
||||
holder.showOnMapRow?.visibility = View.GONE
|
||||
holder.bottomDivider?.visibility = if (lastItem) View.GONE else View.VISIBLE
|
||||
|
@ -250,6 +229,14 @@ class TimelineTabFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getDistance(messages: List<LocationMessages.LocationMessage>): Float {
|
||||
var dist = 0.0
|
||||
messages.forEach {
|
||||
dist += it.distanceFromPrev
|
||||
}
|
||||
return dist.toFloat()
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
@ -269,4 +256,8 @@ class TimelineTabFragment : Fragment() {
|
|||
val bottomDivider: View? = view.findViewById(R.id.bottom_divider)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ADAPTER_UPDATE_INTERVAL_MIL = 15 * 1000L // 15 sec
|
||||
}
|
||||
}
|
|
@ -21,11 +21,11 @@ import net.osmand.PlatformUtil
|
|||
import net.osmand.aidl.gpx.AGpxBitmap
|
||||
import net.osmand.telegram.R
|
||||
import net.osmand.telegram.helpers.OsmandAidlHelper
|
||||
import net.osmand.telegram.helpers.SavingTracksDbHelper
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.GPXUtilities
|
||||
import net.osmand.telegram.utils.OsmandFormatter
|
||||
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||
import net.osmand.util.Algorithms
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -37,7 +37,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
|
||||
private val uiUtils get() = app.uiUtils
|
||||
|
||||
private lateinit var gpxFile: GPXUtilities.GPXFile
|
||||
private var gpxFile = GPXUtilities.GPXFile()
|
||||
|
||||
private lateinit var dateStartBtn: TextView
|
||||
private lateinit var timeStartBtn: TextView
|
||||
|
@ -52,6 +52,9 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
private var startCalendar = Calendar.getInstance()
|
||||
private var endCalendar = Calendar.getInstance()
|
||||
|
||||
private var userId = -1
|
||||
private var chatId = -1L
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup?,
|
||||
|
@ -62,9 +65,6 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
|
||||
readFromBundle(savedInstanceState ?: arguments)
|
||||
|
||||
val userId = gpxFile.userId
|
||||
val chatId = gpxFile.chatId
|
||||
|
||||
val user = app.telegramHelper.getUser(userId)
|
||||
if (user != null) {
|
||||
mainView.findViewById<TextView>(R.id.title).text = TelegramUiHelper.getUserName(user)
|
||||
|
@ -80,8 +80,6 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
}
|
||||
})
|
||||
|
||||
updateGPXMap()
|
||||
|
||||
val backBtn = mainView.findViewById<ImageView>(R.id.back_button)
|
||||
backBtn.setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_arrow_back))
|
||||
backBtn.setOnClickListener {
|
||||
|
@ -134,12 +132,13 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
openGpx(gpx.path)
|
||||
} else {
|
||||
saveCurrentGpxToFile(object :
|
||||
SavingTracksDbHelper.SaveGpxListener {
|
||||
OsmandLocationUtils.SaveGpxListener {
|
||||
|
||||
override fun onSavingGpxFinish(path: String) {
|
||||
openGpx(path)
|
||||
}
|
||||
|
||||
override fun onSavingGpxError(warnings: MutableList<String>?) {
|
||||
override fun onSavingGpxError(warnings: List<String>?) {
|
||||
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
})
|
||||
|
@ -156,12 +155,12 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
(activity as MainActivity).shareGpx(gpx.path)
|
||||
} else {
|
||||
saveCurrentGpxToFile(object :
|
||||
SavingTracksDbHelper.SaveGpxListener {
|
||||
OsmandLocationUtils.SaveGpxListener {
|
||||
override fun onSavingGpxFinish(path: String) {
|
||||
(activity as MainActivity).shareGpx(path)
|
||||
}
|
||||
|
||||
override fun onSavingGpxError(warnings: MutableList<String>?) {
|
||||
override fun onSavingGpxError(warnings: List<String>?) {
|
||||
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
})
|
||||
|
@ -169,6 +168,8 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
updateGpxInfo()
|
||||
|
||||
return mainView
|
||||
}
|
||||
|
||||
|
@ -189,12 +190,16 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveCurrentGpxToFile(listener: SavingTracksDbHelper.SaveGpxListener) {
|
||||
app.savingTracksDbHelper.saveGpx(listener, app.getExternalFilesDir(null), gpxFile)
|
||||
private fun saveCurrentGpxToFile(listener: OsmandLocationUtils.SaveGpxListener) {
|
||||
if (!gpxFile.isEmpty) {
|
||||
OsmandLocationUtils.saveGpx(app, listener, app.getExternalFilesDir(null)!!, gpxFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readFromBundle(bundle: Bundle?) {
|
||||
bundle?.also {
|
||||
userId = it.getInt(USER_ID_KEY)
|
||||
chatId = it.getLong(CHAT_ID_KEY)
|
||||
startCalendar.timeInMillis = it.getLong(START_KEY)
|
||||
endCalendar.timeInMillis = it.getLong(END_KEY)
|
||||
}
|
||||
|
@ -205,7 +210,10 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
}
|
||||
|
||||
private fun updateGpxInfo() {
|
||||
gpxFile = app.savingTracksDbHelper.collectRecordedDataForUser(gpxFile.userId, gpxFile.chatId, startCalendar.timeInMillis, endCalendar.timeInMillis)
|
||||
val emm = app.locationMessages.getMessagesForUserInChat(
|
||||
userId, chatId, startCalendar.timeInMillis, endCalendar.timeInMillis)
|
||||
|
||||
gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(emm).firstOrNull()?:GPXUtilities.GPXFile()
|
||||
updateGPXStatisticRow()
|
||||
updateDateAndTimeButtons()
|
||||
updateGPXMap()
|
||||
|
@ -229,7 +237,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
|
||||
private fun updateGPXMap() {
|
||||
saveCurrentGpxToFile(object :
|
||||
SavingTracksDbHelper.SaveGpxListener {
|
||||
OsmandLocationUtils.SaveGpxListener {
|
||||
override fun onSavingGpxFinish(path: String) {
|
||||
val mgr = activity?.getSystemService(Context.WINDOW_SERVICE)
|
||||
if (mgr != null) {
|
||||
|
@ -242,7 +250,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSavingGpxError(warnings: MutableList<String>?) {
|
||||
override fun onSavingGpxError(warnings: List<String>?) {
|
||||
log.debug("onSavingGpxError ${warnings?.firstOrNull()}")
|
||||
}
|
||||
})
|
||||
|
@ -299,18 +307,21 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
|||
private const val TAG = "UserGpxInfoFragment"
|
||||
private const val START_KEY = "start_key"
|
||||
private const val END_KEY = "end_key"
|
||||
private const val USER_ID_KEY = "user_id_key"
|
||||
private const val CHAT_ID_KEY = "chat_id_key"
|
||||
|
||||
private const val GPX_TRACK_COLOR = -65536
|
||||
|
||||
fun showInstance(fm: FragmentManager, gpxFile: GPXUtilities.GPXFile, start: Long, end: Long): Boolean {
|
||||
fun showInstance(fm: FragmentManager,userId:Int,chatId:Long, start: Long, end: Long): Boolean {
|
||||
return try {
|
||||
val fragment = UserGpxInfoFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(USER_ID_KEY, userId)
|
||||
putLong(CHAT_ID_KEY, chatId)
|
||||
putLong(START_KEY, start)
|
||||
putLong(END_KEY, end)
|
||||
}
|
||||
}
|
||||
fragment.gpxFile = gpxFile
|
||||
fragment.show(fm, TAG)
|
||||
true
|
||||
} catch (e: RuntimeException) {
|
||||
|
|
|
@ -0,0 +1,557 @@
|
|||
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.BufferMessage
|
||||
import net.osmand.telegram.helpers.LocationMessages.LocationMessage
|
||||
import net.osmand.telegram.helpers.TelegramHelper
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||
import net.osmand.util.GeoPointParserUtil
|
||||
import net.osmand.util.MapUtils
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
object OsmandLocationUtils {
|
||||
|
||||
const val DEVICE_PREFIX = "Device: "
|
||||
const val LOCATION_PREFIX = "Location: "
|
||||
const val LAST_LOCATION_PREFIX = "Last location: "
|
||||
const val UPDATED_PREFIX = "Updated: "
|
||||
const val USER_TEXT_LOCATION_TITLE = "\uD83D\uDDFA OsmAnd sharing:"
|
||||
|
||||
const val SHARING_LINK = "https://play.google.com/store/apps/details?id=net.osmand.telegram"
|
||||
|
||||
const val ALTITUDE_PREFIX = "Altitude: "
|
||||
const val SPEED_PREFIX = "Speed: "
|
||||
const val HDOP_PREFIX = "Horizontal precision: "
|
||||
|
||||
const val NOW = "now"
|
||||
const val FEW_SECONDS_AGO = "few seconds ago"
|
||||
const val SECONDS_AGO_SUFFIX = " seconds ago"
|
||||
const val MINUTES_AGO_SUFFIX = " minutes ago"
|
||||
const val HOURS_AGO_SUFFIX = " hours ago"
|
||||
const val UTC_FORMAT_SUFFIX = " UTC"
|
||||
|
||||
val UTC_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
|
||||
val UTC_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
|
||||
fun getLastUpdatedTime(message: TdApi.Message): Int {
|
||||
val content = message.content
|
||||
return when (content) {
|
||||
is MessageOsmAndBotLocation -> content.lastUpdated
|
||||
is MessageUserLocation -> content.lastUpdated
|
||||
else -> Math.max(message.editDate, message.date)
|
||||
}
|
||||
}
|
||||
|
||||
fun getOsmAndBotDeviceName(message: TdApi.Message): String {
|
||||
var deviceName = ""
|
||||
if (message.replyMarkup is TdApi.ReplyMarkupInlineKeyboard) {
|
||||
val replyMarkup = message.replyMarkup as TdApi.ReplyMarkupInlineKeyboard
|
||||
try {
|
||||
deviceName = replyMarkup.rows[0][1].text.split("\\s".toRegex())[1]
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
return deviceName
|
||||
}
|
||||
|
||||
fun parseOsmAndBotLocation(message: TdApi.Message): MessageOsmAndBotLocation {
|
||||
val messageLocation = message.content as TdApi.MessageLocation
|
||||
return MessageOsmAndBotLocation().apply {
|
||||
name = getOsmAndBotDeviceName(message)
|
||||
lat = messageLocation.location.latitude
|
||||
lon = messageLocation.location.longitude
|
||||
lastUpdated = getLastUpdatedTime(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseUserMapLocation(message: TdApi.Message): MessageUserLocation {
|
||||
val messageLocation = message.content as TdApi.MessageLocation
|
||||
return MessageUserLocation().apply {
|
||||
lat = messageLocation.location.latitude
|
||||
lon = messageLocation.location.longitude
|
||||
lastUpdated = getLastUpdatedTime(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseMessage(message: TdApi.Message, helper: TelegramHelper, previousMessage: LocationMessage?): LocationMessage? {
|
||||
var locationMessage: LocationMessage? = null
|
||||
val oldContent = message.content
|
||||
val messageType = getMessageType(message,helper)
|
||||
var parsedMessageContent: MessageLocation? = null
|
||||
if (messageType != -1) {
|
||||
parsedMessageContent = when (messageType) {
|
||||
LocationMessages.TYPE_BOT_TEXT -> {
|
||||
parseTextLocation((oldContent as TdApi.MessageText).text)
|
||||
}
|
||||
LocationMessages.TYPE_USER_TEXT -> {
|
||||
if (oldContent is TdApi.MessageText) {
|
||||
parseTextLocation(oldContent.text, false)
|
||||
} else {
|
||||
oldContent as MessageUserLocation
|
||||
}
|
||||
}
|
||||
LocationMessages.TYPE_BOT_MAP -> {
|
||||
parseOsmAndBotLocation(message)
|
||||
}
|
||||
LocationMessages.TYPE_USER_MAP -> {
|
||||
parseUserMapLocation(message)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedMessageContent != null) {
|
||||
val distanceFromPrev = if (previousMessage != null) {
|
||||
MapUtils.getDistance(previousMessage.lat, previousMessage.lon,
|
||||
parsedMessageContent.lat, parsedMessageContent.lon)
|
||||
} else 0.0
|
||||
|
||||
locationMessage = LocationMessage(helper.getSenderMessageId(message), message.chatId, parsedMessageContent.lat,
|
||||
parsedMessageContent.lon, parsedMessageContent.altitude, parsedMessageContent.speed, parsedMessageContent.hdop,
|
||||
parsedMessageContent.bearing, parsedMessageContent.lastUpdated * 1000L, messageType, message.id, distanceFromPrev)
|
||||
}
|
||||
|
||||
return locationMessage
|
||||
}
|
||||
|
||||
fun getMessageType(message: TdApi.Message, helper: TelegramHelper): Int {
|
||||
val fromBot = helper.isOsmAndBot(message.senderUserId)
|
||||
val viaBot = helper.isOsmAndBot(message.viaBotUserId)
|
||||
val oldContent = message.content
|
||||
return if (oldContent is TdApi.MessageText) {
|
||||
if (fromBot || viaBot) {
|
||||
LocationMessages.TYPE_BOT_TEXT
|
||||
} else {
|
||||
LocationMessages.TYPE_USER_TEXT
|
||||
}
|
||||
} else if (oldContent is TdApi.MessageLocation) {
|
||||
if (fromBot || viaBot) {
|
||||
LocationMessages.TYPE_BOT_MAP
|
||||
} else {
|
||||
LocationMessages.TYPE_USER_MAP
|
||||
}
|
||||
} else if (oldContent is MessageLocation) {
|
||||
oldContent.type
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
fun formatLocation(sig: Location): String {
|
||||
return String.format(Locale.US, "%.5f, %.5f", sig.latitude, sig.longitude)
|
||||
}
|
||||
|
||||
fun formatLocation(sig: LocationMessage): String {
|
||||
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"
|
||||
}
|
||||
|
||||
fun parseOsmAndBotLocationContent(oldContent: MessageOsmAndBotLocation, content: TdApi.MessageContent): MessageOsmAndBotLocation {
|
||||
val messageLocation = content as TdApi.MessageLocation
|
||||
return MessageOsmAndBotLocation().apply {
|
||||
name = oldContent.name
|
||||
lat = messageLocation.location.latitude
|
||||
lon = messageLocation.location.longitude
|
||||
lastUpdated = (System.currentTimeMillis() / 1000).toInt()
|
||||
type = LocationMessages.TYPE_BOT_MAP
|
||||
}
|
||||
}
|
||||
|
||||
fun parseTextLocation(text: TdApi.FormattedText, botLocation: Boolean = true): MessageLocation {
|
||||
val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserLocation()
|
||||
res.type = if (botLocation) LocationMessages.TYPE_BOT_TEXT else LocationMessages.TYPE_USER_TEXT
|
||||
var locationNA = false
|
||||
for (s in text.text.lines()) {
|
||||
when {
|
||||
s.startsWith(DEVICE_PREFIX) -> {
|
||||
if (res is MessageOsmAndBotLocation) {
|
||||
res.name = s.removePrefix(DEVICE_PREFIX)
|
||||
}
|
||||
}
|
||||
s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> {
|
||||
var locStr: String
|
||||
var parse = true
|
||||
if (s.startsWith(LAST_LOCATION_PREFIX)) {
|
||||
locStr = s.removePrefix(LAST_LOCATION_PREFIX)
|
||||
if (!locationNA) {
|
||||
parse = false
|
||||
}
|
||||
} else {
|
||||
locStr = s.removePrefix(LOCATION_PREFIX)
|
||||
if (locStr.trim() == "n/a") {
|
||||
locationNA = true
|
||||
parse = false
|
||||
}
|
||||
}
|
||||
if (parse) {
|
||||
try {
|
||||
val urlTextEntity =
|
||||
text.entities.firstOrNull { it.type is TdApi.TextEntityTypeTextUrl }
|
||||
if (urlTextEntity != null && urlTextEntity.offset == text.text.indexOf(
|
||||
locStr
|
||||
)
|
||||
) {
|
||||
val url = (urlTextEntity.type as TdApi.TextEntityTypeTextUrl).url
|
||||
val point: GeoPointParserUtil.GeoParsedPoint? =
|
||||
GeoPointParserUtil.parse(url)
|
||||
if (point != null) {
|
||||
res.lat = point.latitude
|
||||
res.lon = point.longitude
|
||||
}
|
||||
} else {
|
||||
val (latS, lonS) = locStr.split(" ")
|
||||
res.lat = latS.dropLast(1).toDouble()
|
||||
res.lon = lonS.toDouble()
|
||||
|
||||
val timeIndex = locStr.indexOf("(")
|
||||
if (timeIndex != -1) {
|
||||
val updatedS = locStr.substring(timeIndex, locStr.length)
|
||||
res.lastUpdated =
|
||||
(parseTime(updatedS.removePrefix("(").removeSuffix(")")) / 1000).toInt()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
s.startsWith(ALTITUDE_PREFIX) -> {
|
||||
val altStr = s.removePrefix(ALTITUDE_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.altitude = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(SPEED_PREFIX) -> {
|
||||
val altStr = s.removePrefix(SPEED_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.speed = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(HDOP_PREFIX) -> {
|
||||
val altStr = s.removePrefix(HDOP_PREFIX)
|
||||
try {
|
||||
val alt = altStr.split(" ").first()
|
||||
res.hdop = alt.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
s.startsWith(UPDATED_PREFIX) -> {
|
||||
if (res.lastUpdated == 0) {
|
||||
val updatedStr = s.removePrefix(UPDATED_PREFIX)
|
||||
val endIndex = updatedStr.indexOf("(")
|
||||
val updatedS = updatedStr.substring(
|
||||
0,
|
||||
if (endIndex != -1) endIndex else updatedStr.length
|
||||
)
|
||||
val parsedTime = (parseTime(updatedS.trim()) / 1000).toInt()
|
||||
val currentTime = (System.currentTimeMillis() / 1000) - 1
|
||||
res.lastUpdated =
|
||||
if (parsedTime < currentTime) parsedTime else currentTime.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun parseTime(timeS: String): Long {
|
||||
try {
|
||||
when {
|
||||
timeS.endsWith(FEW_SECONDS_AGO) -> return System.currentTimeMillis() - 5000
|
||||
|
||||
timeS.endsWith(SECONDS_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX)
|
||||
return System.currentTimeMillis() - locStr.toLong() * 1000
|
||||
}
|
||||
timeS.endsWith(MINUTES_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX)
|
||||
val minutes = locStr.toLong()
|
||||
return System.currentTimeMillis() - minutes * 60 * 1000
|
||||
}
|
||||
timeS.endsWith(HOURS_AGO_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX)
|
||||
val hours = locStr.toLong()
|
||||
return (System.currentTimeMillis() - hours * 60 * 60 * 1000)
|
||||
}
|
||||
timeS.endsWith(UTC_FORMAT_SUFFIX) -> {
|
||||
val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX)
|
||||
val (latS, lonS) = locStr.split(" ")
|
||||
val date = UTC_DATE_FORMAT.parse(latS)
|
||||
val time = UTC_TIME_FORMAT.parse(lonS)
|
||||
val res = date.time + time.time
|
||||
return res
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun getTextMessageContent(updateId: Int, location: LocationMessage): 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.time)))
|
||||
} else {
|
||||
builder.append(String.format("$UPDATED_PREFIX%s (%d)\n", formatFullTime(location.time), updateId))
|
||||
}
|
||||
val textMessage = builder.toString().trim()
|
||||
|
||||
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.time)))
|
||||
} else {
|
||||
builder.append(String.format("$UPDATED_PREFIX%s (%d)\n", formatFullTime(location.time), 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>()
|
||||
|
||||
var previousTime: Long = -1
|
||||
var previousChatId: Long = -1
|
||||
var previousUserId = -1
|
||||
var segment: GPXUtilities.TrkSegment? = null
|
||||
var track: GPXUtilities.Track? = null
|
||||
var gpx: GPXUtilities.GPXFile? = null
|
||||
|
||||
items.forEach {
|
||||
val userId = it.userId
|
||||
val chatId = it.chatId
|
||||
val time = it.time
|
||||
if (previousUserId != userId || (newGpxPerChat && previousChatId != chatId)) {
|
||||
gpx = GPXUtilities.GPXFile()
|
||||
gpx!!.chatId = chatId
|
||||
gpx!!.userId = userId
|
||||
previousTime = 0
|
||||
track = null
|
||||
segment = null
|
||||
dataTracks.add(gpx!!)
|
||||
}
|
||||
val pt = GPXUtilities.WptPt()
|
||||
pt.userId = userId
|
||||
pt.chatId = chatId
|
||||
pt.lat = it.lat
|
||||
pt.lon = it.lon
|
||||
pt.ele = it.altitude
|
||||
pt.speed = it.speed
|
||||
pt.hdop = it.hdop
|
||||
pt.time = time
|
||||
val currentInterval = Math.abs(time - previousTime)
|
||||
if (track != null) {
|
||||
if (currentInterval < 30 * 60 * 1000) {
|
||||
// 30 minute - same segment
|
||||
segment!!.points.add(pt)
|
||||
} else {
|
||||
segment = GPXUtilities.TrkSegment()
|
||||
segment!!.points.add(pt)
|
||||
track!!.segments.add(segment)
|
||||
}
|
||||
} else {
|
||||
track = GPXUtilities.Track()
|
||||
segment = GPXUtilities.TrkSegment()
|
||||
track!!.segments.add(segment)
|
||||
segment!!.points.add(pt)
|
||||
|
||||
gpx!!.tracks.add(track)
|
||||
}
|
||||
previousTime = time
|
||||
previousUserId = userId
|
||||
previousChatId = chatId
|
||||
}
|
||||
|
||||
return dataTracks
|
||||
}
|
||||
|
||||
fun saveGpx(app: TelegramApplication, listener: SaveGpxListener, dir: File, gpxFile: GPXUtilities.GPXFile) {
|
||||
if (!gpxFile.isEmpty) {
|
||||
val task = SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0)
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MessageLocation : TdApi.MessageContent() {
|
||||
|
||||
var lat: Double = Double.NaN
|
||||
internal set
|
||||
var lon: Double = Double.NaN
|
||||
internal set
|
||||
var lastUpdated: Int = 0
|
||||
internal set
|
||||
var speed: Double = 0.0
|
||||
internal set
|
||||
var altitude: Double = 0.0
|
||||
internal set
|
||||
var hdop: Double = 0.0
|
||||
internal set
|
||||
var bearing: Double = 0.0
|
||||
internal set
|
||||
var type: Int = -1
|
||||
internal set
|
||||
|
||||
override fun getConstructor() = -1
|
||||
|
||||
abstract fun isValid(): Boolean
|
||||
}
|
||||
|
||||
class MessageOsmAndBotLocation : MessageLocation() {
|
||||
|
||||
var name: String = ""
|
||||
internal set
|
||||
|
||||
override fun isValid() = name != "" && lat != Double.NaN && lon != Double.NaN
|
||||
}
|
||||
|
||||
class MessageUserLocation : MessageLocation() {
|
||||
|
||||
override fun isValid() = lat != Double.NaN && lon != Double.NaN
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
) :
|
||||
AsyncTask<Void, Void, List<String>>() {
|
||||
|
||||
override fun doInBackground(vararg params: Void): List<String> {
|
||||
val warnings = ArrayList<String>()
|
||||
dir.mkdirs()
|
||||
if (dir.parentFile.canWrite()) {
|
||||
if (dir.exists()) {
|
||||
// save file
|
||||
var fout = File(dir, "$userId.gpx")
|
||||
if (!gpxFile.isEmpty) {
|
||||
val pt = gpxFile.findPointToShow()
|
||||
|
||||
val user = app.telegramHelper.getUser(pt!!.userId)
|
||||
val fileName: String
|
||||
fileName = if (user != null) {
|
||||
(TelegramUiHelper.getUserName(user) + "_" + SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(Date(pt.time)))
|
||||
} else {
|
||||
userId.toString() + "_" + SimpleDateFormat("yyyy-MM-dd_HH-mm_EEE", Locale.US).format(Date(pt.time))
|
||||
}
|
||||
fout = File(dir, "$fileName.gpx")
|
||||
var ind = 1
|
||||
while (fout.exists()) {
|
||||
fout = File(dir, "${fileName}_${++ind}.gpx")
|
||||
}
|
||||
}
|
||||
val warn = GPXUtilities.writeGpxFile(fout, gpxFile, app)
|
||||
if (warn != null) {
|
||||
warnings.add(warn)
|
||||
return warnings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return warnings
|
||||
}
|
||||
|
||||
override fun onPostExecute(warnings: List<String>?) {
|
||||
if (listener != null) {
|
||||
if (warnings != null && warnings.isEmpty()) {
|
||||
listener.onSavingGpxFinish(gpxFile.path)
|
||||
} else {
|
||||
listener.onSavingGpxError(warnings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SaveGpxListener {
|
||||
|
||||
fun onSavingGpxFinish(path: String)
|
||||
|
||||
fun onSavingGpxError(warnings: List<String>?)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue