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 project(path: ':OsmAnd-java', configuration: 'android')
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
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'
|
exclude group: 'org.jetbrains', module: 'annotations'
|
||||||
}
|
}
|
||||||
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
|
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="@color/app_bar_divider" />
|
android:background="@color/app_bar_divider" />
|
||||||
|
|
||||||
<LinearLayout
|
<FrameLayout
|
||||||
android:id="@+id/date_row"
|
android:id="@+id/date_row"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/list_header_height"
|
android:layout_height="@dimen/list_header_height"
|
||||||
|
@ -106,13 +106,12 @@
|
||||||
android:paddingRight="@dimen/content_padding_standard">
|
android:paddingRight="@dimen/content_padding_standard">
|
||||||
|
|
||||||
<net.osmand.telegram.ui.views.TextViewEx
|
<net.osmand.telegram.ui.views.TextViewEx
|
||||||
android:id="@+id/date_start_btn"
|
android:id="@+id/date_btn"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/dialog_button_height"
|
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_marginEnd="@dimen/content_padding_half"
|
||||||
android:layout_marginRight="@dimen/content_padding_half"
|
android:layout_marginRight="@dimen/content_padding_half"
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/btn_round_border"
|
android:background="@drawable/btn_round_border"
|
||||||
android:drawablePadding="@dimen/content_padding_half"
|
android:drawablePadding="@dimen/content_padding_half"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
@ -120,38 +119,12 @@
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingLeft="@dimen/image_button_padding"
|
android:paddingLeft="@dimen/image_button_padding"
|
||||||
android:paddingRight="@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:textColor="?attr/ctrl_active_color"
|
||||||
android:textSize="@dimen/text_button_text_size"
|
android:textSize="@dimen/text_button_text_size"
|
||||||
app:typeface="@string/font_roboto_medium" />
|
app:typeface="@string/font_roboto_medium" />
|
||||||
|
|
||||||
<View
|
</FrameLayout>
|
||||||
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>
|
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,63 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</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>
|
</LinearLayout>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
<resources>
|
<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_enabled">Monitoring is enabled</string>
|
||||||
<string name="monitoring_is_disabled">Monitoring is disabled</string>
|
<string name="monitoring_is_disabled">Monitoring is disabled</string>
|
||||||
<string name="time_on_the_move">time on the move</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 notificationHelper: NotificationHelper private set
|
||||||
lateinit var osmandAidlHelper: OsmandAidlHelper private set
|
lateinit var osmandAidlHelper: OsmandAidlHelper private set
|
||||||
lateinit var locationProvider: TelegramLocationProvider private set
|
lateinit var locationProvider: TelegramLocationProvider private set
|
||||||
lateinit var messagesDbHelper: MessagesDbHelper private set
|
lateinit var locationMessages: LocationMessages private set
|
||||||
lateinit var savingTracksDbHelper: SavingTracksDbHelper private set
|
|
||||||
|
|
||||||
var telegramService: TelegramService? = null
|
var telegramService: TelegramService? = null
|
||||||
|
|
||||||
|
@ -68,8 +67,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
||||||
showLocationHelper = ShowLocationHelper(this)
|
showLocationHelper = ShowLocationHelper(this)
|
||||||
notificationHelper = NotificationHelper(this)
|
notificationHelper = NotificationHelper(this)
|
||||||
locationProvider = TelegramLocationProvider(this)
|
locationProvider = TelegramLocationProvider(this)
|
||||||
messagesDbHelper = MessagesDbHelper(this)
|
locationMessages = LocationMessages(this)
|
||||||
savingTracksDbHelper = SavingTracksDbHelper(this)
|
|
||||||
|
|
||||||
if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) {
|
if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) {
|
||||||
shareLocationHelper.startSharingLocation()
|
shareLocationHelper.startSharingLocation()
|
||||||
|
@ -96,6 +94,13 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
||||||
return ni != null && ni.type == ConnectivityManager.TYPE_WIFI
|
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
|
private val isInternetConnected: Boolean
|
||||||
get() {
|
get() {
|
||||||
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
|
@ -13,10 +13,11 @@ import android.os.*
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import net.osmand.PlatformUtil
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener
|
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener
|
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.notifications.TelegramNotification.NotificationType
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
import net.osmand.telegram.utils.AndroidUtils
|
||||||
|
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -274,6 +275,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
|
|
||||||
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
|
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
|
||||||
app().showLocationHelper.startShowMessagesTask(chatId, *messages)
|
app().showLocationHelper.startShowMessagesTask(chatId, *messages)
|
||||||
|
messages.forEach {
|
||||||
|
app().locationMessages.addNewLocationMessage(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {
|
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>) {
|
override fun onUpdateMessages(messages: List<TdApi.Message>) {
|
||||||
messages.forEach {
|
messages.forEach {
|
||||||
app().settings.updateShareInfo(it)
|
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.support.annotation.StringRes
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.helpers.OsmandAidlHelper
|
import net.osmand.telegram.helpers.OsmandAidlHelper
|
||||||
import net.osmand.telegram.helpers.TelegramHelper
|
import net.osmand.telegram.helpers.TelegramHelper
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
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) {
|
class TelegramSettings(private val app: TelegramApplication) {
|
||||||
|
|
||||||
|
private val log = PlatformUtil.getLog(TelegramSettings::class.java)
|
||||||
|
|
||||||
private var shareChatsInfo = ConcurrentHashMap<Long, ShareChatInfo>()
|
private var shareChatsInfo = ConcurrentHashMap<Long, ShareChatInfo>()
|
||||||
private var hiddenOnMapChats: Set<Long> = emptySet()
|
private var hiddenOnMapChats: Set<Long> = emptySet()
|
||||||
private var shareDevices: Set<DeviceBot> = emptySet()
|
private var shareDevices: Set<DeviceBot> = emptySet()
|
||||||
|
@ -242,21 +245,56 @@ class TelegramSettings(private val app: TelegramApplication) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShareInfo(message: TdApi.Message) {
|
fun updateShareInfo(message: TdApi.Message) {
|
||||||
val shareChatInfo = shareChatsInfo[message.chatId]
|
val shareInfo = shareChatsInfo[message.chatId]
|
||||||
val content = message.content
|
val content = message.content
|
||||||
if (shareChatInfo != null) {
|
if (shareInfo != null) {
|
||||||
when (content) {
|
when (content) {
|
||||||
is TdApi.MessageLocation -> {
|
is TdApi.MessageLocation -> {
|
||||||
shareChatInfo.currentMapMessageId = message.id
|
val state = message.sendingState
|
||||||
shareChatInfo.pendingMapMessage = false
|
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 -> {
|
is TdApi.MessageText -> {
|
||||||
shareChatInfo.currentTextMessageId = message.id
|
val state = message.sendingState
|
||||||
shareChatInfo.updateTextMessageId++
|
if (state != null) {
|
||||||
shareChatInfo.pendingTextMessage = false
|
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_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastSuccessfulSendTimeMs)
|
||||||
obj.put(ShareChatInfo.LAST_SEND_MAP_TIME_KEY, chatInfo.lastSendMapMessageTime)
|
obj.put(ShareChatInfo.LAST_SEND_MAP_TIME_KEY, chatInfo.lastSendMapMessageTime)
|
||||||
obj.put(ShareChatInfo.LAST_SEND_TEXT_TIME_KEY, chatInfo.lastSendTextMessageTime)
|
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.put(obj)
|
||||||
}
|
}
|
||||||
jArray
|
jArray
|
||||||
|
@ -566,6 +609,11 @@ class TelegramSettings(private val app: TelegramApplication) {
|
||||||
lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY)
|
lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY)
|
||||||
lastSendMapMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_MAP_TIME_KEY)
|
lastSendMapMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_MAP_TIME_KEY)
|
||||||
lastSendTextMessageTime = obj.optInt(ShareChatInfo.LAST_SEND_TEXT_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
|
shareChatsInfo[shareInfo.chatId] = shareInfo
|
||||||
}
|
}
|
||||||
|
@ -845,12 +893,17 @@ class TelegramSettings(private val app: TelegramApplication) {
|
||||||
var updateTextMessageId = 1
|
var updateTextMessageId = 1
|
||||||
var currentMessageLimit = -1L
|
var currentMessageLimit = -1L
|
||||||
var currentMapMessageId = -1L
|
var currentMapMessageId = -1L
|
||||||
|
var oldMapMessageId = -1L
|
||||||
var currentTextMessageId = -1L
|
var currentTextMessageId = -1L
|
||||||
|
var oldTextMessageId = -1L
|
||||||
var userSetLivePeriod = -1L
|
var userSetLivePeriod = -1L
|
||||||
var userSetLivePeriodStart = -1L
|
var userSetLivePeriodStart = -1L
|
||||||
var lastSuccessfulSendTimeMs = -1L
|
var lastSuccessfulSendTimeMs = -1L
|
||||||
var lastSendTextMessageTime = -1
|
var lastSendTextMessageTime = -1
|
||||||
var lastSendMapMessageTime = -1
|
var lastSendMapMessageTime = -1
|
||||||
|
var collectedMessages = 0
|
||||||
|
var sentMessages = 0
|
||||||
|
var pendingTdLib = 0
|
||||||
var pendingTextMessage = false
|
var pendingTextMessage = false
|
||||||
var pendingMapMessage = false
|
var pendingMapMessage = false
|
||||||
var shouldSendViaBotMessage = 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_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime"
|
||||||
internal const val LAST_SEND_MAP_TIME_KEY = "lastSendMapMessageTime"
|
internal const val LAST_SEND_MAP_TIME_KEY = "lastSendMapMessageTime"
|
||||||
internal const val LAST_SEND_TEXT_TIME_KEY = "lastSendTextMessageTime"
|
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.Location
|
||||||
import net.osmand.PlatformUtil
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.*
|
import net.osmand.telegram.*
|
||||||
|
import net.osmand.telegram.helpers.LocationMessages.BufferMessage
|
||||||
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
|
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
|
||||||
import net.osmand.telegram.utils.AndroidNetworkUtils
|
import net.osmand.telegram.utils.AndroidNetworkUtils
|
||||||
import net.osmand.telegram.utils.BASE_URL
|
import net.osmand.telegram.utils.BASE_URL
|
||||||
|
@ -48,36 +49,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
||||||
lastLocation = location
|
lastLocation = location
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
val chatsShareInfo = app.settings.getChatsShareInfo()
|
if (app.settings.getChatsShareInfo().isNotEmpty()) {
|
||||||
if (chatsShareInfo.isNotEmpty()) {
|
shareLocationMessages(location, app.telegramHelper.getCurrentUserId())
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lastLocationMessageSentTime = System.currentTimeMillis()
|
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() {
|
fun startSharingLocation() {
|
||||||
if (!sharingLocation) {
|
if (!sharingLocation) {
|
||||||
sharingLocation = true
|
sharingLocation = true
|
||||||
|
@ -129,6 +133,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
||||||
app.startMyLocationService()
|
app.startMyLocationService()
|
||||||
|
|
||||||
refreshNotification()
|
refreshNotification()
|
||||||
|
|
||||||
|
checkAndSendBufferMessages()
|
||||||
} else {
|
} else {
|
||||||
app.forceUpdateMyLocation()
|
app.forceUpdateMyLocation()
|
||||||
}
|
}
|
||||||
|
@ -158,25 +164,193 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
||||||
refreshNotification()
|
refreshNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDeviceSharingUrl(loc: Location, sharingMode: String): String {
|
private fun checkAndSendBufferMessages(){
|
||||||
val url = "$BASE_URL/device/$sharingMode/send?lat=${loc.latitude}&lon=${loc.longitude}"
|
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)
|
val builder = StringBuilder(url)
|
||||||
if (loc.hasBearing() && loc.bearing != 0.0f) {
|
if (loc.bearing != 0.0) {
|
||||||
builder.append("&azi=${loc.bearing}")
|
builder.append("&azi=${loc.bearing}")
|
||||||
}
|
}
|
||||||
if (loc.hasSpeed() && loc.speed != 0.0f) {
|
if (loc.speed != 0.0) {
|
||||||
builder.append("&spd=${loc.speed}")
|
builder.append("&spd=${loc.speed}")
|
||||||
}
|
}
|
||||||
if (loc.hasAltitude() && loc.altitude != 0.0) {
|
if (loc.altitude != 0.0) {
|
||||||
builder.append("&alt=${loc.altitude}")
|
builder.append("&alt=${loc.altitude}")
|
||||||
}
|
}
|
||||||
if (loc.hasAccuracy() && loc.accuracy != 0.0f) {
|
if (loc.hdop != 0.0) {
|
||||||
builder.append("&hdop=${loc.accuracy}")
|
builder.append("&hdop=${loc.hdop}")
|
||||||
}
|
}
|
||||||
return builder.toString()
|
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) {
|
if (result != null) {
|
||||||
try {
|
try {
|
||||||
val jsonResult = JSONObject(result)
|
val jsonResult = JSONObject(result)
|
||||||
|
@ -186,22 +360,12 @@ class ShareLocationHelper(private val app: TelegramApplication) {
|
||||||
chatsShareInfo.forEach { (_, shareInfo) ->
|
chatsShareInfo.forEach { (_, shareInfo) ->
|
||||||
shareInfo.lastSuccessfulSendTimeMs = currentTime
|
shareInfo.lastSuccessfulSendTimeMs = currentTime
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return false
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshNotification() {
|
private fun refreshNotification() {
|
||||||
|
|
|
@ -8,10 +8,11 @@ import net.osmand.aidl.map.ALatLon
|
||||||
import net.osmand.aidl.maplayer.point.AMapPoint
|
import net.osmand.aidl.maplayer.point.AMapPoint
|
||||||
import net.osmand.telegram.R
|
import net.osmand.telegram.R
|
||||||
import net.osmand.telegram.TelegramApplication
|
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.helpers.TelegramUiHelper.ListItem
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
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 org.drinkless.td.libcore.telegram.TdApi
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
@ -62,7 +63,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
execOsmandApi {
|
execOsmandApi {
|
||||||
val messages = telegramHelper.getMessages()
|
val messages = telegramHelper.getMessages()
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
val date = telegramHelper.getLastUpdatedTime(message)
|
val date = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
val messageShowingTime = System.currentTimeMillis() / 1000 - date
|
val messageShowingTime = System.currentTimeMillis() / 1000 - date
|
||||||
if (messageShowingTime > app.settings.locHistoryTime) {
|
if (messageShowingTime > app.settings.locHistoryTime) {
|
||||||
removeMapPoint(message.chatId, message)
|
removeMapPoint(message.chatId, message)
|
||||||
|
@ -78,9 +79,9 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
val chatId = message.chatId
|
val chatId = message.chatId
|
||||||
val chatTitle = telegramHelper.getChat(message.chatId)?.title
|
val chatTitle = telegramHelper.getChat(message.chatId)?.title
|
||||||
val content = message.content
|
val content = message.content
|
||||||
val date = telegramHelper.getLastUpdatedTime(message)
|
val date = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime
|
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 userName = ""
|
||||||
var photoPath: String? = null
|
var photoPath: String? = null
|
||||||
val user = telegramHelper.getUser(message.senderUserId)
|
val user = telegramHelper.getUser(message.senderUserId)
|
||||||
|
@ -105,7 +106,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
val params = generatePointParams(photoPath, stale)
|
val params = generatePointParams(photoPath, stale)
|
||||||
val aLatLon = when (content) {
|
val aLatLon = when (content) {
|
||||||
is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude)
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
if (aLatLon != null) {
|
if (aLatLon != null) {
|
||||||
|
@ -250,7 +251,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
|
|
||||||
private fun removeMapPoint(chatId: Long, message: TdApi.Message) {
|
private fun removeMapPoint(chatId: Long, message: TdApi.Message) {
|
||||||
val content = message.content
|
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}")
|
osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}")
|
||||||
} else if (content is MessageOsmAndBotLocation) {
|
} else if (content is MessageOsmAndBotLocation) {
|
||||||
osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.name}")
|
osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.name}")
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
package net.osmand.telegram.helpers
|
package net.osmand.telegram.helpers
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import net.osmand.Location
|
|
||||||
import net.osmand.PlatformUtil
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.SHARE_TYPE_MAP
|
import net.osmand.telegram.*
|
||||||
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.helpers.TelegramHelper.TelegramAuthenticationParameterType.*
|
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_DIR
|
||||||
import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_EXT
|
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
|
||||||
import org.drinkless.td.libcore.telegram.Client.ResultHandler
|
import org.drinkless.td.libcore.telegram.Client.ResultHandler
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState
|
import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
@ -36,32 +38,6 @@ class TelegramHelper private constructor() {
|
||||||
private const val IGNORED_ERROR_CODE = 406
|
private const val IGNORED_ERROR_CODE = 406
|
||||||
private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5
|
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
|
// min and max values for the Telegram API
|
||||||
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
|
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
|
||||||
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day
|
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 getCurrentUser() = currentUser
|
||||||
|
|
||||||
|
fun getCurrentUserId() = currentUser?.id ?: -1
|
||||||
|
|
||||||
fun getUserMessage(user: TdApi.User) =
|
fun getUserMessage(user: TdApi.User) =
|
||||||
usersLocationMessages.values.firstOrNull { it.senderUserId == user.id }
|
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
|
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 isPrivateChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypePrivate
|
||||||
|
|
||||||
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
|
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
|
||||||
|
@ -324,7 +293,7 @@ class TelegramHelper private constructor() {
|
||||||
try {
|
try {
|
||||||
log.debug("Loading native tdlib...")
|
log.debug("Loading native tdlib...")
|
||||||
System.loadLibrary("tdjni")
|
System.loadLibrary("tdjni")
|
||||||
Client.setLogVerbosityLevel(0)
|
Client.setLogVerbosityLevel(1)
|
||||||
libraryLoaded = true
|
libraryLoaded = true
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error("Failed to load tdlib", e)
|
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 isInit() = client != null && haveAuthorization
|
||||||
|
|
||||||
fun getUserPhotoPath(user: TdApi.User?) = when {
|
fun getUserPhotoPath(user: TdApi.User?) = when {
|
||||||
|
@ -421,7 +396,7 @@ class TelegramHelper private constructor() {
|
||||||
|
|
||||||
private fun isUserLocationMessage(message: TdApi.Message): Boolean {
|
private fun isUserLocationMessage(message: TdApi.Message): Boolean {
|
||||||
val cont = message.content
|
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 {
|
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) {
|
private fun requestMessage(chatId: Long, messageId: Long, onComplete: (TdApi.Message) -> Unit) {
|
||||||
client?.send(TdApi.GetMessage(chatId, messageId)) { obj ->
|
client?.send(TdApi.GetMessage(chatId, messageId)) { obj ->
|
||||||
if (obj is TdApi.Message) {
|
if (obj is TdApi.Message) {
|
||||||
|
@ -728,7 +699,10 @@ class TelegramHelper private constructor() {
|
||||||
private fun addNewMessage(message: TdApi.Message) {
|
private fun addNewMessage(message: TdApi.Message) {
|
||||||
lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, Math.max(message.date, message.editDate))
|
lastTelegramUpdateTime = Math.max(lastTelegramUpdateTime, Math.max(message.date, message.editDate))
|
||||||
if (message.isAppropriate()) {
|
if (message.isAppropriate()) {
|
||||||
log.debug("addNewMessage: $message")
|
if (message.isOutgoing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.debug("addNewMessage: ${message.id}")
|
||||||
val fromBot = isOsmAndBot(message.senderUserId)
|
val fromBot = isOsmAndBot(message.senderUserId)
|
||||||
val viaBot = isOsmAndBot(message.viaBotUserId)
|
val viaBot = isOsmAndBot(message.viaBotUserId)
|
||||||
val oldContent = message.content
|
val oldContent = message.content
|
||||||
|
@ -741,11 +715,6 @@ class TelegramHelper private constructor() {
|
||||||
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
|
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
|
||||||
message.content = parseOsmAndBotLocation(message)
|
message.content = parseOsmAndBotLocation(message)
|
||||||
}
|
}
|
||||||
if (message.isOutgoing) {
|
|
||||||
outgoingMessagesListeners.forEach {
|
|
||||||
it.onUpdateMessages(listOf(message))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
removeOldMessages(message, fromBot, viaBot)
|
removeOldMessages(message, fromBot, viaBot)
|
||||||
val oldMessage = usersLocationMessages.values.firstOrNull { getSenderMessageId(it) == getSenderMessageId(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))
|
val hasNewerMessage = oldMessage != null && (Math.max(message.editDate, message.date) < Math.max(oldMessage.editDate, oldMessage.date))
|
||||||
|
@ -753,13 +722,12 @@ class TelegramHelper private constructor() {
|
||||||
usersLocationMessages[message.id] = message
|
usersLocationMessages[message.id] = message
|
||||||
}
|
}
|
||||||
incomingMessagesListeners.forEach {
|
incomingMessagesListeners.forEach {
|
||||||
if (!hasNewerMessage || it is SavingTracksDbHelper) {
|
if (!hasNewerMessage || it is TelegramService) {
|
||||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeOldMessages(newMessage: TdApi.Message, fromBot: Boolean, viaBot: Boolean) {
|
private fun removeOldMessages(newMessage: TdApi.Message, fromBot: Boolean, viaBot: Boolean) {
|
||||||
val iterator = usersLocationMessages.entries.iterator()
|
val iterator = usersLocationMessages.entries.iterator()
|
||||||
|
@ -779,34 +747,13 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)
|
} 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()
|
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) {
|
fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) {
|
||||||
if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) {
|
if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) {
|
||||||
client?.send(
|
client?.send(
|
||||||
|
@ -854,10 +801,7 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recreateLiveLocationMessage(
|
private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
|
||||||
shareInfo: TelegramSettings.ShareChatInfo,
|
|
||||||
content: TdApi.InputMessageContent
|
|
||||||
) {
|
|
||||||
if (shareInfo.chatId != -1L) {
|
if (shareInfo.chatId != -1L) {
|
||||||
val array = LongArray(1)
|
val array = LongArray(1)
|
||||||
if (content is TdApi.InputMessageLocation) {
|
if (content is TdApi.InputMessageLocation) {
|
||||||
|
@ -889,18 +833,42 @@ class TelegramHelper private constructor() {
|
||||||
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
|
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
|
||||||
needRefreshActiveLiveLocationMessages = true
|
needRefreshActiveLiveLocationMessages = true
|
||||||
log.debug("sendNewLiveLocationMessage")
|
log.debug("sendNewLiveLocationMessage")
|
||||||
client?.send(
|
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||||
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)
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendNewMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) {
|
||||||
|
needRefreshActiveLiveLocationMessages = true
|
||||||
|
val location = TdApi.Location(locationMessage.lat, locationMessage.lon)
|
||||||
val livePeriod =
|
val livePeriod =
|
||||||
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
|
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
|
||||||
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
|
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
|
||||||
|
@ -908,52 +876,24 @@ class TelegramHelper private constructor() {
|
||||||
shareInfo.livePeriod.toInt()
|
shareInfo.livePeriod.toInt()
|
||||||
}
|
}
|
||||||
val content = TdApi.InputMessageLocation(location, livePeriod)
|
val content = TdApi.InputMessageLocation(location, livePeriod)
|
||||||
val msgId = shareInfo.currentMapMessageId
|
if (!shareInfo.pendingMapMessage) {
|
||||||
val timeAfterLastSendMessage = ((System.currentTimeMillis() / 1000) - shareInfo.lastSendMapMessageTime)
|
shareInfo.pendingMapMessage = true
|
||||||
log.debug("sendLiveLocationImpl - $msgId pendingMapMessage ${shareInfo.pendingMapMessage}")
|
shareInfo.pendingTdLib++
|
||||||
if (msgId != -1L) {
|
log.error("sendNewMapLocation ${shareInfo.pendingTdLib}")
|
||||||
if (shareInfo.shouldDeletePreviousMapMessage) {
|
client?.send(TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
|
||||||
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)
|
handleMapLocationMessageUpdate(obj, shareInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!shareInfo.pendingMapMessage || shareInfo.pendingMapMessage && timeAfterLastSendMessage > SEND_NEW_MESSAGE_INTERVAL_SEC) {
|
|
||||||
sendNewLiveLocationMessage(shareInfo, content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendLiveLocationText(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, location: Location) {
|
fun editMapLocation(shareInfo: TelegramSettings.ShareChatInfo, locationMessage: LocationMessages.BufferMessage) {
|
||||||
chatsShareInfo.forEach { (chatId, shareInfo) ->
|
needRefreshActiveLiveLocationMessages = true
|
||||||
if (shareInfo.getChatLiveMessageExpireTime() <= 0) {
|
val location = TdApi.Location(locationMessage.lat, locationMessage.lon)
|
||||||
return@forEach
|
if (shareInfo.currentMapMessageId!=-1L) {
|
||||||
}
|
shareInfo.pendingTdLib++
|
||||||
val msgId = shareInfo.currentTextMessageId
|
log.info("editMapLocation ${shareInfo.currentMapMessageId} pendingTdLib: ${shareInfo.pendingTdLib}")
|
||||||
if (msgId == -1L) {
|
client?.send(TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, location)) { obj ->
|
||||||
shareInfo.updateTextMessageId = 1
|
handleMapLocationMessageUpdate(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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -961,6 +901,7 @@ class TelegramHelper private constructor() {
|
||||||
private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
private fun handleMapLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
||||||
when (obj.constructor) {
|
when (obj.constructor) {
|
||||||
TdApi.Error.CONSTRUCTOR -> {
|
TdApi.Error.CONSTRUCTOR -> {
|
||||||
|
log.debug("handleMapLocationMessageUpdate - ERROR $obj")
|
||||||
val error = obj as TdApi.Error
|
val error = obj as TdApi.Error
|
||||||
needRefreshActiveLiveLocationMessages = true
|
needRefreshActiveLiveLocationMessages = true
|
||||||
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
||||||
|
@ -978,6 +919,8 @@ class TelegramHelper private constructor() {
|
||||||
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
||||||
shareInfo.hasSharingError = true
|
shareInfo.hasSharingError = true
|
||||||
needRefreshActiveLiveLocationMessages = true
|
needRefreshActiveLiveLocationMessages = true
|
||||||
|
shareInfo.pendingMapMessage = false
|
||||||
|
log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed")
|
||||||
outgoingMessagesListeners.forEach {
|
outgoingMessagesListeners.forEach {
|
||||||
it.onSendLiveLocationError(-1, "Map location message ${obj.id} failed to send")
|
it.onSendLiveLocationError(-1, "Map location message ${obj.id} failed to send")
|
||||||
}
|
}
|
||||||
|
@ -986,9 +929,14 @@ class TelegramHelper private constructor() {
|
||||||
shareInfo.pendingMapMessage = true
|
shareInfo.pendingMapMessage = true
|
||||||
shareInfo.lastSendMapMessageTime = obj.date
|
shareInfo.lastSendMapMessageTime = obj.date
|
||||||
log.debug("handleMapLocationMessageUpdate - MessageSendingStatePending")
|
log.debug("handleMapLocationMessageUpdate - MessageSendingStatePending")
|
||||||
|
outgoingMessagesListeners.forEach {
|
||||||
|
it.onUpdateMessages(listOf(obj))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
shareInfo.hasSharingError = false
|
shareInfo.hasSharingError = false
|
||||||
|
shareInfo.pendingMapMessage = false
|
||||||
|
log.debug("handleMapLocationMessageUpdate - MessageSendingStateSuccess")
|
||||||
outgoingMessagesListeners.forEach {
|
outgoingMessagesListeners.forEach {
|
||||||
it.onUpdateMessages(listOf(obj))
|
it.onUpdateMessages(listOf(obj))
|
||||||
}
|
}
|
||||||
|
@ -1002,6 +950,7 @@ class TelegramHelper private constructor() {
|
||||||
private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
private fun handleTextLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
|
||||||
when (obj.constructor) {
|
when (obj.constructor) {
|
||||||
TdApi.Error.CONSTRUCTOR -> {
|
TdApi.Error.CONSTRUCTOR -> {
|
||||||
|
log.debug("handleTextLocationMessageUpdate - ERROR")
|
||||||
val error = obj as TdApi.Error
|
val error = obj as TdApi.Error
|
||||||
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
|
||||||
shareInfo.shouldDeletePreviousTextMessage = true
|
shareInfo.shouldDeletePreviousTextMessage = true
|
||||||
|
@ -1017,7 +966,9 @@ class TelegramHelper private constructor() {
|
||||||
when {
|
when {
|
||||||
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
|
||||||
shareInfo.hasSharingError = true
|
shareInfo.hasSharingError = true
|
||||||
|
shareInfo.pendingTextMessage = false
|
||||||
needRefreshActiveLiveLocationMessages = true
|
needRefreshActiveLiveLocationMessages = true
|
||||||
|
log.debug("handleTextLocationMessageUpdate - MessageSendingStateFailed")
|
||||||
outgoingMessagesListeners.forEach {
|
outgoingMessagesListeners.forEach {
|
||||||
it.onSendLiveLocationError(-1, "Text location message ${obj.id} failed to send")
|
it.onSendLiveLocationError(-1, "Text location message ${obj.id} failed to send")
|
||||||
}
|
}
|
||||||
|
@ -1026,9 +977,14 @@ class TelegramHelper private constructor() {
|
||||||
shareInfo.pendingTextMessage = true
|
shareInfo.pendingTextMessage = true
|
||||||
shareInfo.lastSendTextMessageTime = obj.date
|
shareInfo.lastSendTextMessageTime = obj.date
|
||||||
log.debug("handleTextLocationMessageUpdate - MessageSendingStatePending")
|
log.debug("handleTextLocationMessageUpdate - MessageSendingStatePending")
|
||||||
|
outgoingMessagesListeners.forEach {
|
||||||
|
it.onUpdateMessages(listOf(obj))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
shareInfo.hasSharingError = false
|
shareInfo.hasSharingError = false
|
||||||
|
shareInfo.pendingTextMessage = false
|
||||||
|
log.debug("handleTextLocationMessageUpdate - MessageSendingStateSuccess")
|
||||||
outgoingMessagesListeners.forEach {
|
outgoingMessagesListeners.forEach {
|
||||||
it.onUpdateMessages(listOf(obj))
|
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 {
|
fun logout(): Boolean {
|
||||||
return if (libraryLoaded) {
|
return if (libraryLoaded) {
|
||||||
haveAuthorization = false
|
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> {
|
class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long, internal val isChannel: Boolean) : Comparable<OrderedChat> {
|
||||||
|
|
||||||
override fun compareTo(other: OrderedChat): Int {
|
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 -> {
|
TdApi.UpdateMessageContent.CONSTRUCTOR -> {
|
||||||
val updateMessageContent = obj as TdApi.UpdateMessageContent
|
val updateMessageContent = obj as TdApi.UpdateMessageContent
|
||||||
val message = usersLocationMessages[updateMessageContent.messageId]
|
val message = usersLocationMessages[updateMessageContent.messageId]
|
||||||
|
log.debug("UpdateMessageContent " + updateMessageContent.messageId)
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
updateMessageContent.apply {
|
updateMessageContent.apply {
|
||||||
requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage)
|
requestMessage(chatId, messageId, this@TelegramHelper::addNewMessage)
|
||||||
|
@ -1622,7 +1332,6 @@ class TelegramHelper private constructor() {
|
||||||
newContent
|
newContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.debug("UpdateMessageContent " + message.senderUserId)
|
|
||||||
incomingMessagesListeners.forEach {
|
incomingMessagesListeners.forEach {
|
||||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||||
}
|
}
|
||||||
|
@ -1630,6 +1339,7 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
TdApi.UpdateNewMessage.CONSTRUCTOR -> {
|
TdApi.UpdateNewMessage.CONSTRUCTOR -> {
|
||||||
addNewMessage((obj as TdApi.UpdateNewMessage).message)
|
addNewMessage((obj as TdApi.UpdateNewMessage).message)
|
||||||
|
log.debug("UpdateNewMessage " + obj.message.id)
|
||||||
}
|
}
|
||||||
TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> {
|
TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> {
|
||||||
val updateChat = obj as TdApi.UpdateMessageMentionRead
|
val updateChat = obj as TdApi.UpdateMessageMentionRead
|
||||||
|
@ -1736,9 +1446,9 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> {
|
TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> {
|
||||||
val udateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded
|
val updateSucceeded = obj as TdApi.UpdateMessageSendSucceeded
|
||||||
val message = udateMessageSendSucceeded.message
|
val message = updateSucceeded.message
|
||||||
log.debug("UpdateMessageSendSucceeded: $message")
|
log.debug("UpdateMessageSendSucceeded: ${message.id} oldId: ${updateSucceeded.oldMessageId}")
|
||||||
outgoingMessagesListeners.forEach {
|
outgoingMessagesListeners.forEach {
|
||||||
it.onUpdateMessages(listOf(message))
|
it.onUpdateMessages(listOf(message))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,10 @@ import android.widget.ImageView
|
||||||
import net.osmand.data.LatLon
|
import net.osmand.data.LatLon
|
||||||
import net.osmand.telegram.R
|
import net.osmand.telegram.R
|
||||||
import net.osmand.telegram.TelegramApplication
|
import net.osmand.telegram.TelegramApplication
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation
|
import net.osmand.telegram.utils.GPXUtilities.GPXFile
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation
|
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||||
import net.osmand.telegram.utils.GPXUtilities
|
import net.osmand.telegram.utils.OsmandLocationUtils.MessageOsmAndBotLocation
|
||||||
|
import net.osmand.telegram.utils.OsmandLocationUtils.MessageUserLocation
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
|
|
||||||
object TelegramUiHelper {
|
object TelegramUiHelper {
|
||||||
|
@ -65,11 +66,11 @@ object TelegramUiHelper {
|
||||||
val user = helper.getUser(userId)
|
val user = helper.getUser(userId)
|
||||||
val message = messages.firstOrNull { it.viaBotUserId == 0 }
|
val message = messages.firstOrNull { it.viaBotUserId == 0 }
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
res.lastUpdated = helper.getLastUpdatedTime(message)
|
res.lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
val content = message.content
|
val content = message.content
|
||||||
if (content is TdApi.MessageLocation) {
|
if (content is TdApi.MessageLocation) {
|
||||||
res.latLon = LatLon(content.location.latitude, content.location.longitude)
|
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)
|
res.latLon = LatLon(content.lat, content.lon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +111,7 @@ object TelegramUiHelper {
|
||||||
val content = message.content
|
val content = message.content
|
||||||
return when (content) {
|
return when (content) {
|
||||||
is MessageOsmAndBotLocation -> botMessageToLocationItem(chat, 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)
|
is TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
@ -125,12 +126,12 @@ object TelegramUiHelper {
|
||||||
return when (content) {
|
return when (content) {
|
||||||
is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content)
|
is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content)
|
||||||
is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message)
|
is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message)
|
||||||
is MessageUserTextLocation -> locationMessageToChatItem(helper, chat, message)
|
is MessageUserLocation -> locationMessageToChatItem(helper, chat, message)
|
||||||
else -> null
|
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)
|
return if (simpleUserItem) gpxToUserGpxChatItem(helper, gpx) else gpxToGpxChatItem(helper, gpx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,14 +166,14 @@ object TelegramUiHelper {
|
||||||
name = TelegramUiHelper.getUserName(user)
|
name = TelegramUiHelper.getUserName(user)
|
||||||
latLon = when (content) {
|
latLon = when (content) {
|
||||||
is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude)
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
photoPath = helper.getUserPhotoPath(user)
|
photoPath = helper.getUserPhotoPath(user)
|
||||||
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
|
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
|
||||||
placeholderId = R.drawable.img_user_picture
|
placeholderId = R.drawable.img_user_picture
|
||||||
userId = message.senderUserId
|
userId = message.senderUserId
|
||||||
lastUpdated = helper.getLastUpdatedTime(message)
|
lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +212,7 @@ object TelegramUiHelper {
|
||||||
name = TelegramUiHelper.getUserName(user)
|
name = TelegramUiHelper.getUserName(user)
|
||||||
latLon = when (content) {
|
latLon = when (content) {
|
||||||
is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude)
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
if (helper.isGroup(chat)) {
|
if (helper.isGroup(chat)) {
|
||||||
|
@ -225,13 +226,13 @@ object TelegramUiHelper {
|
||||||
userId = message.senderUserId
|
userId = message.senderUserId
|
||||||
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
|
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
|
||||||
chatWithBot = helper.isBot(userId)
|
chatWithBot = helper.isBot(userId)
|
||||||
lastUpdated = helper.getLastUpdatedTime(message)
|
lastUpdated = OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun gpxToGpxChatItem(
|
private fun gpxToGpxChatItem(
|
||||||
helper: TelegramHelper,
|
helper: TelegramHelper,
|
||||||
gpx: GPXUtilities.GPXFile
|
gpx: GPXFile
|
||||||
): GpxChatItem? {
|
): GpxChatItem? {
|
||||||
val user = helper.getUser(gpx.userId) ?: return null
|
val user = helper.getUser(gpx.userId) ?: return null
|
||||||
val chat = helper.getChat(gpx.chatId) ?: 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(
|
private fun gpxToUserGpxChatItem(
|
||||||
helper: TelegramHelper,
|
helper: TelegramHelper,
|
||||||
gpx: GPXUtilities.GPXFile
|
gpx: GPXFile
|
||||||
): GpxChatItem? {
|
): GpxChatItem? {
|
||||||
val user = helper.getUser(gpx.userId) ?: return null
|
val user = helper.getUser(gpx.userId) ?: return null
|
||||||
return GpxChatItem().apply {
|
return GpxChatItem().apply {
|
||||||
|
@ -322,7 +350,25 @@ object TelegramUiHelper {
|
||||||
|
|
||||||
class GpxChatItem : ListItem() {
|
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
|
internal set
|
||||||
var groupPhotoPath: String? = null
|
var groupPhotoPath: String? = null
|
||||||
internal set
|
internal set
|
||||||
|
|
|
@ -19,7 +19,6 @@ import net.osmand.telegram.TelegramApplication
|
||||||
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
||||||
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
|
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
|
||||||
import net.osmand.telegram.TelegramSettings
|
import net.osmand.telegram.TelegramSettings
|
||||||
import net.osmand.telegram.helpers.SavingTracksDbHelper
|
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.*
|
import net.osmand.telegram.helpers.TelegramHelper.*
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem
|
import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem
|
||||||
|
|
|
@ -19,6 +19,8 @@ import android.widget.*
|
||||||
import net.osmand.PlatformUtil
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.R
|
import net.osmand.telegram.R
|
||||||
import net.osmand.telegram.TelegramApplication
|
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.OsmandAidlHelper
|
||||||
import net.osmand.telegram.helpers.TelegramHelper
|
import net.osmand.telegram.helpers.TelegramHelper
|
||||||
import net.osmand.telegram.helpers.TelegramHelper.*
|
import net.osmand.telegram.helpers.TelegramHelper.*
|
||||||
|
@ -183,7 +185,6 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
settings.save()
|
settings.save()
|
||||||
app.messagesDbHelper.saveMessages()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -291,6 +292,11 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
||||||
if (!app.showLocationHelper.showingLocation && settings.hasAnyChatToShowOnMap()) {
|
if (!app.showLocationHelper.showingLocation && settings.hasAnyChatToShowOnMap()) {
|
||||||
app.showLocationHelper.startShowingLocation()
|
app.showLocationHelper.startShowingLocation()
|
||||||
}
|
}
|
||||||
|
if (app.telegramService == null) {
|
||||||
|
messages.forEach {
|
||||||
|
app.locationMessages.addNewLocationMessage(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {}
|
override fun onDeleteChatLocationMessages(chatId: Long, messages: List<TdApi.Message>) {}
|
||||||
|
@ -341,7 +347,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
|
||||||
fun logoutTelegram(silent: Boolean = false) {
|
fun logoutTelegram(silent: Boolean = false) {
|
||||||
if (telegramHelper.getTelegramAuthorizationState() == TelegramHelper.TelegramAuthorizationState.READY) {
|
if (telegramHelper.getTelegramAuthorizationState() == TelegramHelper.TelegramAuthorizationState.READY) {
|
||||||
if (app.isInternetConnectionAvailable) {
|
if (app.isInternetConnectionAvailable) {
|
||||||
app.messagesDbHelper.clearMessages()
|
app.locationMessages.clearBufferedMessages()
|
||||||
settings.clear()
|
settings.clear()
|
||||||
telegramHelper.logout()
|
telegramHelper.logout()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -231,7 +231,6 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
updateCurrentUserPhoto()
|
updateCurrentUserPhoto()
|
||||||
telegramHelper.getActiveLiveLocationMessages(null)
|
|
||||||
updateContent()
|
updateContent()
|
||||||
updateEnable = true
|
updateEnable = true
|
||||||
startHandler()
|
startHandler()
|
||||||
|
@ -599,6 +598,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.title?.text = title
|
holder.title?.text = title
|
||||||
|
holder.icon?.setOnClickListener {
|
||||||
|
app.forceUpdateMyLocation()
|
||||||
|
}
|
||||||
if (holder is ChatViewHolder) {
|
if (holder is ChatViewHolder) {
|
||||||
holder.description?.visibility = View.GONE
|
holder.description?.visibility = View.GONE
|
||||||
if (live) {
|
if (live) {
|
||||||
|
@ -714,6 +716,17 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true)
|
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,6 +764,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
val stopSharingDescr: TextView? = view.findViewById(R.id.stop_in)
|
val stopSharingDescr: TextView? = view.findViewById(R.id.stop_in)
|
||||||
val stopSharingFirstPart: TextView? = view.findViewById(R.id.ending_in_first_part)
|
val stopSharingFirstPart: TextView? = view.findViewById(R.id.ending_in_first_part)
|
||||||
val stopSharingSecondPart: TextView? = view.findViewById(R.id.ending_in_second_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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import net.osmand.telegram.helpers.TelegramUiHelper
|
||||||
import net.osmand.telegram.ui.SetTimeDialogFragment.SetTimeListAdapter.ChatViewHolder
|
import net.osmand.telegram.ui.SetTimeDialogFragment.SetTimeListAdapter.ChatViewHolder
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
import net.osmand.telegram.utils.AndroidUtils
|
||||||
import net.osmand.telegram.utils.OsmandFormatter
|
import net.osmand.telegram.utils.OsmandFormatter
|
||||||
|
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||||
import net.osmand.telegram.utils.UiUtils
|
import net.osmand.telegram.utils.UiUtils
|
||||||
import net.osmand.util.MapUtils
|
import net.osmand.util.MapUtils
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
|
@ -328,7 +329,7 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
||||||
val message = telegramHelper.getChatMessages(itemId).firstOrNull()
|
val message = telegramHelper.getChatMessages(itemId).firstOrNull()
|
||||||
val content = message?.content
|
val content = message?.content
|
||||||
if (message != null && content is TdApi.MessageLocation && (location != null && content.location != null)) {
|
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?.visibility = View.VISIBLE
|
||||||
holder.description?.text = OsmandFormatter.getListItemLiveTimeDescr(app, lastUpdated)
|
holder.description?.text = OsmandFormatter.getListItemLiveTimeDescr(app, lastUpdated)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.app.DatePickerDialog
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
import android.support.annotation.DrawableRes
|
import android.support.annotation.DrawableRes
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
|
@ -17,11 +18,11 @@ import android.widget.TextView
|
||||||
import net.osmand.PlatformUtil
|
import net.osmand.PlatformUtil
|
||||||
import net.osmand.telegram.R
|
import net.osmand.telegram.R
|
||||||
import net.osmand.telegram.TelegramApplication
|
import net.osmand.telegram.TelegramApplication
|
||||||
|
import net.osmand.telegram.helpers.LocationMessages
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper.ListItem
|
import net.osmand.telegram.helpers.TelegramUiHelper.ListItem
|
||||||
import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder
|
import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
import net.osmand.telegram.utils.AndroidUtils
|
||||||
import net.osmand.telegram.utils.GPXUtilities
|
|
||||||
import net.osmand.telegram.utils.OsmandFormatter
|
import net.osmand.telegram.utils.OsmandFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -38,13 +39,14 @@ class TimelineTabFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var adapter: LiveNowListAdapter
|
private lateinit var adapter: LiveNowListAdapter
|
||||||
|
|
||||||
private lateinit var dateStartBtn: TextView
|
private lateinit var dateBtn: TextView
|
||||||
private lateinit var dateEndBtn: TextView
|
|
||||||
private lateinit var mainView: View
|
private lateinit var mainView: View
|
||||||
|
|
||||||
private var start = 0L
|
private var start = 0L
|
||||||
private var end = 0L
|
private var end = 0L
|
||||||
|
|
||||||
|
private var updateEnable: Boolean = false
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -74,43 +76,50 @@ class TimelineTabFragment : Fragment() {
|
||||||
monitoringTv.setText(if (monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled)
|
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 {
|
setOnClickListener {
|
||||||
selectStartDate()
|
selectDate()
|
||||||
}
|
}
|
||||||
setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_start), null, null, null)
|
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
|
return mainView
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBtnTextColor(textView: TextView) {
|
override fun onResume() {
|
||||||
textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light))
|
super.onResume()
|
||||||
|
updateEnable = true
|
||||||
|
startHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectStartDate() {
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
updateEnable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectDate() {
|
||||||
val dateFromDialog =
|
val dateFromDialog =
|
||||||
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||||
val from = Calendar.getInstance()
|
val from = Calendar.getInstance()
|
||||||
from.set(Calendar.YEAR, year)
|
from.set(Calendar.YEAR, year)
|
||||||
from.set(Calendar.MONTH, monthOfYear)
|
from.set(Calendar.MONTH, monthOfYear)
|
||||||
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
from.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||||
|
|
||||||
from.set(Calendar.HOUR_OF_DAY, 0)
|
from.set(Calendar.HOUR_OF_DAY, 0)
|
||||||
from.clear(Calendar.MINUTE)
|
from.clear(Calendar.MINUTE)
|
||||||
from.clear(Calendar.SECOND)
|
from.clear(Calendar.SECOND)
|
||||||
from.clear(Calendar.MILLISECOND)
|
from.clear(Calendar.MILLISECOND)
|
||||||
start = from.timeInMillis
|
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()
|
updateList()
|
||||||
updateDateButtons()
|
updateDateButton()
|
||||||
}
|
}
|
||||||
val startCalendar = Calendar.getInstance()
|
val startCalendar = Calendar.getInstance()
|
||||||
startCalendar.timeInMillis = start
|
startCalendar.timeInMillis = start
|
||||||
|
@ -121,34 +130,8 @@ class TimelineTabFragment : Fragment() {
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectEndDate() {
|
private fun updateDateButton() {
|
||||||
val dateFromDialog =
|
dateBtn.text = OsmandFormatter.getFormattedDate(start / 1000)
|
||||||
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 getPressedStateIcon(@DrawableRes iconId: Int): Drawable? {
|
private fun getPressedStateIcon(@DrawableRes iconId: Int): Drawable? {
|
||||||
|
@ -162,32 +145,35 @@ class TimelineTabFragment : Fragment() {
|
||||||
return normal
|
return normal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startHandler() {
|
||||||
|
val updateAdapter = Handler()
|
||||||
|
updateAdapter.postDelayed({
|
||||||
|
if (updateEnable) {
|
||||||
|
updateList()
|
||||||
|
startHandler()
|
||||||
|
}
|
||||||
|
}, ADAPTER_UPDATE_INTERVAL_MIL)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateList() {
|
private fun updateList() {
|
||||||
val res = mutableListOf<ListItem>()
|
val res = mutableListOf<ListItem>()
|
||||||
val s = System.currentTimeMillis()
|
|
||||||
log.debug("updateList $s")
|
|
||||||
val ignoredUsersIds = ArrayList<Int>()
|
|
||||||
val currentUserId = telegramHelper.getCurrentUser()?.id
|
val currentUserId = telegramHelper.getCurrentUser()?.id
|
||||||
if (currentUserId != null) {
|
if (currentUserId != null) {
|
||||||
val currentUserGpx:GPXUtilities.GPXFile? = app.savingTracksDbHelper.collectRecordedDataForUser(currentUserId, 0, start, end)
|
val outgoingMessages = app.locationMessages.getMessagesForUser(currentUserId, start, end)
|
||||||
if (currentUserGpx != null) {
|
TelegramUiHelper.locationMessagesToChatItem(telegramHelper, outgoingMessages)?.also { chatItem ->
|
||||||
TelegramUiHelper.gpxToChatItem(telegramHelper, currentUserGpx, true)?.also {
|
|
||||||
res.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
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)
|
adapter.items = sortAdapterItems(res)
|
||||||
log.debug("updateList $s dif: ${e - s}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortAdapterItems(list: MutableList<ListItem>): MutableList<ListItem> {
|
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.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||||
holder.lastTelegramUpdateTime?.visibility = View.GONE
|
holder.lastTelegramUpdateTime?.visibility = View.GONE
|
||||||
|
|
||||||
if (item is TelegramUiHelper.GpxChatItem) {
|
if (item is TelegramUiHelper.LocationMessagesChatItem) {
|
||||||
val gpx = item.gpxFile
|
val distance = OsmandFormatter.getFormattedDistance(getDistance(item.locationMessages),app)
|
||||||
val groupDescrRowVisible = (!item.privateChat || item.chatWithBot) && item.userId != currentUserId
|
val name = if ((!item.privateChat || item.chatWithBot) && item.userId != currentUserId) item.getVisibleName() else ""
|
||||||
if (groupDescrRowVisible) {
|
|
||||||
holder.groupDescrContainer?.visibility = View.VISIBLE
|
holder.groupDescrContainer?.visibility = View.VISIBLE
|
||||||
holder.groupTitle?.text = item.getVisibleName()
|
holder.groupTitle?.text = "$distance (${getString(R.string.points_size, item.locationMessages.size)}) $name"
|
||||||
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
|
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
|
||||||
} else {
|
|
||||||
holder.groupDescrContainer?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
holder.userRow?.setOnClickListener {
|
holder.userRow?.setOnClickListener {
|
||||||
if (gpx != null) {
|
|
||||||
childFragmentManager.also {
|
childFragmentManager.also {
|
||||||
UserGpxInfoFragment.showInstance(it, gpx, start, end)
|
UserGpxInfoFragment.showInstance(it, item.userId, item.chatId, start, end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
holder.imageButton?.visibility = View.GONE
|
holder.imageButton?.visibility = View.GONE
|
||||||
holder.showOnMapRow?.visibility = View.GONE
|
holder.showOnMapRow?.visibility = View.GONE
|
||||||
holder.bottomDivider?.visibility = if (lastItem) View.GONE else View.VISIBLE
|
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
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
@ -269,4 +256,8 @@ class TimelineTabFragment : Fragment() {
|
||||||
val bottomDivider: View? = view.findViewById(R.id.bottom_divider)
|
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.aidl.gpx.AGpxBitmap
|
||||||
import net.osmand.telegram.R
|
import net.osmand.telegram.R
|
||||||
import net.osmand.telegram.helpers.OsmandAidlHelper
|
import net.osmand.telegram.helpers.OsmandAidlHelper
|
||||||
import net.osmand.telegram.helpers.SavingTracksDbHelper
|
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||||
import net.osmand.telegram.utils.AndroidUtils
|
import net.osmand.telegram.utils.AndroidUtils
|
||||||
import net.osmand.telegram.utils.GPXUtilities
|
import net.osmand.telegram.utils.GPXUtilities
|
||||||
import net.osmand.telegram.utils.OsmandFormatter
|
import net.osmand.telegram.utils.OsmandFormatter
|
||||||
|
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||||
import net.osmand.util.Algorithms
|
import net.osmand.util.Algorithms
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -37,7 +37,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
|
|
||||||
private val uiUtils get() = app.uiUtils
|
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 dateStartBtn: TextView
|
||||||
private lateinit var timeStartBtn: TextView
|
private lateinit var timeStartBtn: TextView
|
||||||
|
@ -52,6 +52,9 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
private var startCalendar = Calendar.getInstance()
|
private var startCalendar = Calendar.getInstance()
|
||||||
private var endCalendar = Calendar.getInstance()
|
private var endCalendar = Calendar.getInstance()
|
||||||
|
|
||||||
|
private var userId = -1
|
||||||
|
private var chatId = -1L
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
parent: ViewGroup?,
|
parent: ViewGroup?,
|
||||||
|
@ -62,9 +65,6 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
|
|
||||||
readFromBundle(savedInstanceState ?: arguments)
|
readFromBundle(savedInstanceState ?: arguments)
|
||||||
|
|
||||||
val userId = gpxFile.userId
|
|
||||||
val chatId = gpxFile.chatId
|
|
||||||
|
|
||||||
val user = app.telegramHelper.getUser(userId)
|
val user = app.telegramHelper.getUser(userId)
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
mainView.findViewById<TextView>(R.id.title).text = TelegramUiHelper.getUserName(user)
|
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)
|
val backBtn = mainView.findViewById<ImageView>(R.id.back_button)
|
||||||
backBtn.setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_arrow_back))
|
backBtn.setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_arrow_back))
|
||||||
backBtn.setOnClickListener {
|
backBtn.setOnClickListener {
|
||||||
|
@ -134,12 +132,13 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
openGpx(gpx.path)
|
openGpx(gpx.path)
|
||||||
} else {
|
} else {
|
||||||
saveCurrentGpxToFile(object :
|
saveCurrentGpxToFile(object :
|
||||||
SavingTracksDbHelper.SaveGpxListener {
|
OsmandLocationUtils.SaveGpxListener {
|
||||||
|
|
||||||
override fun onSavingGpxFinish(path: String) {
|
override fun onSavingGpxFinish(path: String) {
|
||||||
openGpx(path)
|
openGpx(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSavingGpxError(warnings: MutableList<String>?) {
|
override fun onSavingGpxError(warnings: List<String>?) {
|
||||||
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
|
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -156,12 +155,12 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
(activity as MainActivity).shareGpx(gpx.path)
|
(activity as MainActivity).shareGpx(gpx.path)
|
||||||
} else {
|
} else {
|
||||||
saveCurrentGpxToFile(object :
|
saveCurrentGpxToFile(object :
|
||||||
SavingTracksDbHelper.SaveGpxListener {
|
OsmandLocationUtils.SaveGpxListener {
|
||||||
override fun onSavingGpxFinish(path: String) {
|
override fun onSavingGpxFinish(path: String) {
|
||||||
(activity as MainActivity).shareGpx(path)
|
(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()
|
Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -169,6 +168,8 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateGpxInfo()
|
||||||
|
|
||||||
return mainView
|
return mainView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +190,16 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveCurrentGpxToFile(listener: SavingTracksDbHelper.SaveGpxListener) {
|
private fun saveCurrentGpxToFile(listener: OsmandLocationUtils.SaveGpxListener) {
|
||||||
app.savingTracksDbHelper.saveGpx(listener, app.getExternalFilesDir(null), gpxFile)
|
if (!gpxFile.isEmpty) {
|
||||||
|
OsmandLocationUtils.saveGpx(app, listener, app.getExternalFilesDir(null)!!, gpxFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readFromBundle(bundle: Bundle?) {
|
private fun readFromBundle(bundle: Bundle?) {
|
||||||
bundle?.also {
|
bundle?.also {
|
||||||
|
userId = it.getInt(USER_ID_KEY)
|
||||||
|
chatId = it.getLong(CHAT_ID_KEY)
|
||||||
startCalendar.timeInMillis = it.getLong(START_KEY)
|
startCalendar.timeInMillis = it.getLong(START_KEY)
|
||||||
endCalendar.timeInMillis = it.getLong(END_KEY)
|
endCalendar.timeInMillis = it.getLong(END_KEY)
|
||||||
}
|
}
|
||||||
|
@ -205,7 +210,10 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateGpxInfo() {
|
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()
|
updateGPXStatisticRow()
|
||||||
updateDateAndTimeButtons()
|
updateDateAndTimeButtons()
|
||||||
updateGPXMap()
|
updateGPXMap()
|
||||||
|
@ -229,7 +237,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
|
|
||||||
private fun updateGPXMap() {
|
private fun updateGPXMap() {
|
||||||
saveCurrentGpxToFile(object :
|
saveCurrentGpxToFile(object :
|
||||||
SavingTracksDbHelper.SaveGpxListener {
|
OsmandLocationUtils.SaveGpxListener {
|
||||||
override fun onSavingGpxFinish(path: String) {
|
override fun onSavingGpxFinish(path: String) {
|
||||||
val mgr = activity?.getSystemService(Context.WINDOW_SERVICE)
|
val mgr = activity?.getSystemService(Context.WINDOW_SERVICE)
|
||||||
if (mgr != null) {
|
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()}")
|
log.debug("onSavingGpxError ${warnings?.firstOrNull()}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -299,18 +307,21 @@ class UserGpxInfoFragment : BaseDialogFragment() {
|
||||||
private const val TAG = "UserGpxInfoFragment"
|
private const val TAG = "UserGpxInfoFragment"
|
||||||
private const val START_KEY = "start_key"
|
private const val START_KEY = "start_key"
|
||||||
private const val END_KEY = "end_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
|
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 {
|
return try {
|
||||||
val fragment = UserGpxInfoFragment().apply {
|
val fragment = UserGpxInfoFragment().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
|
putInt(USER_ID_KEY, userId)
|
||||||
|
putLong(CHAT_ID_KEY, chatId)
|
||||||
putLong(START_KEY, start)
|
putLong(START_KEY, start)
|
||||||
putLong(END_KEY, end)
|
putLong(END_KEY, end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fragment.gpxFile = gpxFile
|
|
||||||
fragment.show(fm, TAG)
|
fragment.show(fm, TAG)
|
||||||
true
|
true
|
||||||
} catch (e: RuntimeException) {
|
} 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