diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index fe976517fb..3e134ade9b 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,4 +1,9 @@ + Send location as + Choose how messages with your location will look like. + Map + Text + Map and text Last update from Telegram Pick a name you haven\'t already used %1$s added. diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index 64047a3a05..346fa3f5c7 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -7,7 +7,6 @@ import android.support.annotation.DrawableRes import android.support.annotation.StringRes import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan -import net.osmand.data.LatLon import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.utils.AndroidUtils @@ -43,9 +42,15 @@ private val LOC_HISTORY_VALUES_SEC = listOf( 24 * 60 * 60L ) +const val SHARE_TYPE_MAP = "Map" +const val SHARE_TYPE_TEXT = "Text" +const val SHARE_TYPE_MAP_AND_TEXT = "Map_and_text" +private val SHARE_TYPE_VALUES = listOf(SHARE_TYPE_MAP, SHARE_TYPE_TEXT, SHARE_TYPE_MAP_AND_TEXT) + private const val SEND_MY_LOC_DEFAULT_INDEX = 6 private const val STALE_LOC_DEFAULT_INDEX = 0 private const val LOC_HISTORY_DEFAULT_INDEX = 7 +private const val SHARE_TYPE_DEFAULT_INDEX = 2 private const val SETTINGS_NAME = "osmand_telegram_settings" @@ -60,6 +65,7 @@ private const val SPEED_CONSTANTS_KEY = "speed_constants" private const val SEND_MY_LOC_INTERVAL_KEY = "send_my_loc_interval" private const val STALE_LOC_TIME_KEY = "stale_loc_time" private const val LOC_HISTORY_TIME_KEY = "loc_history_time" +private const val SHARE_TYPE_KEY = "share_type" private const val APP_TO_CONNECT_PACKAGE_KEY = "app_to_connect_package" @@ -94,13 +100,14 @@ class TelegramSettings(private val app: TelegramApplication) { var sendMyLocInterval = SEND_MY_LOC_VALUES_SEC[SEND_MY_LOC_DEFAULT_INDEX] var staleLocTime = STALE_LOC_VALUES_SEC[STALE_LOC_DEFAULT_INDEX] var locHistoryTime = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX] + var shareTypeValue = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX] var appToConnectPackage = "" private set var liveNowSortType = LiveNowSortType.SORT_BY_GROUP - val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref()) + val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref()) var batteryOptimisationAsked = false @@ -236,11 +243,14 @@ class TelegramSettings(private val app: TelegramApplication) { val shareChatInfo = shareChatsInfo[message.chatId] val content = message.content if (shareChatInfo != null) { - shareChatInfo.currentMessageId = message.id - if (content is TdApi.MessageLocation) { - shareChatInfo.lastSuccessfulLocation = LatLon(content.location.latitude, content.location.longitude) - shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L + when (content) { + is TdApi.MessageLocation -> shareChatInfo.currentMapMessageId = message.id + is TdApi.MessageText -> { + shareChatInfo.currentTextMessageId = message.id + shareChatInfo.updateTextMessageId++ + } } + shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L } } @@ -384,9 +394,14 @@ class TelegramSettings(private val app: TelegramApplication) { } fun onDeleteLiveMessages(chatId: Long, messages: List) { - val currentMessageId = shareChatsInfo[chatId]?.currentMessageId - if (messages.contains(currentMessageId)) { - shareChatsInfo[chatId]?.currentMessageId = -1 + val currentMapMessageId = shareChatsInfo[chatId]?.currentMapMessageId + if (messages.contains(currentMapMessageId)) { + shareChatsInfo[chatId]?.currentMapMessageId = -1 + } + val currentTextMessageId = shareChatsInfo[chatId]?.currentTextMessageId + if (messages.contains(currentTextMessageId)) { + shareChatsInfo[chatId]?.currentTextMessageId = -1 + shareChatsInfo[chatId]?.updateTextMessageId = 1 } } @@ -410,6 +425,8 @@ class TelegramSettings(private val app: TelegramApplication) { edit.putLong(STALE_LOC_TIME_KEY, staleLocTime) edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime) + edit.putString(SHARE_TYPE_KEY, shareTypeValue) + edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage) edit.putString(LIVE_NOW_SORT_TYPE_KEY, liveNowSortType.name) @@ -460,6 +477,8 @@ class TelegramSettings(private val app: TelegramApplication) { staleLocTime = prefs.getLong(STALE_LOC_TIME_KEY, staleLocDef) val locHistoryDef = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX] locHistoryTime = prefs.getLong(LOC_HISTORY_TIME_KEY, locHistoryDef) + val shareTypeDef = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX] + shareTypeValue = prefs.getString(SHARE_TYPE_KEY, shareTypeDef) currentSharingMode = prefs.getString(SHARING_MODE_KEY, "") @@ -503,7 +522,9 @@ class TelegramSettings(private val app: TelegramApplication) { obj.put(ShareChatInfo.START_KEY, chatInfo.start) obj.put(ShareChatInfo.LIVE_PERIOD_KEY, chatInfo.livePeriod) obj.put(ShareChatInfo.LIMIT_KEY, chatInfo.currentMessageLimit) - obj.put(ShareChatInfo.CURRENT_MESSAGE_ID_KEY, chatInfo.currentMessageId) + obj.put(ShareChatInfo.UPDATE_TEXT_MESSAGE_ID_KEY, chatInfo.updateTextMessageId) + obj.put(ShareChatInfo.CURRENT_MAP_MESSAGE_ID_KEY, chatInfo.currentMapMessageId) + obj.put(ShareChatInfo.CURRENT_TEXT_MESSAGE_ID_KEY, chatInfo.currentTextMessageId) obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY, chatInfo.userSetLivePeriod) obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY, chatInfo.userSetLivePeriodStart) obj.put(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastSuccessfulSendTimeMs) @@ -525,7 +546,9 @@ class TelegramSettings(private val app: TelegramApplication) { start = obj.optLong(ShareChatInfo.START_KEY) livePeriod = obj.optLong(ShareChatInfo.LIVE_PERIOD_KEY) currentMessageLimit = obj.optLong(ShareChatInfo.LIMIT_KEY) - currentMessageId = obj.optLong(ShareChatInfo.CURRENT_MESSAGE_ID_KEY) + updateTextMessageId = obj.optInt(ShareChatInfo.UPDATE_TEXT_MESSAGE_ID_KEY) + currentMapMessageId = obj.optLong(ShareChatInfo.CURRENT_MAP_MESSAGE_ID_KEY) + currentTextMessageId = obj.optLong(ShareChatInfo.CURRENT_TEXT_MESSAGE_ID_KEY) userSetLivePeriod = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY) userSetLivePeriodStart = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY) lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY) @@ -601,6 +624,35 @@ class TelegramSettings(private val app: TelegramApplication) { } } + inner class ShareTypePref : DurationPref( + R.drawable.ic_action_location_history, + R.string.send_location_as, + R.string.send_location_as_descr, + emptyList() + ) { + + override fun getCurrentValue(): String { + return getTextValue(shareTypeValue) + } + + override fun setCurrentValue(index: Int) { + shareTypeValue = SHARE_TYPE_VALUES[index] + } + + override fun getMenuItems(): List { + return SHARE_TYPE_VALUES.map { getTextValue(it) } + } + + private fun getTextValue(shareType: String): String { + return when (shareType) { + SHARE_TYPE_MAP -> app.getString(R.string.shared_string_map) + SHARE_TYPE_TEXT -> app.getString(R.string.shared_string_text) + SHARE_TYPE_MAP_AND_TEXT -> app.getString(R.string.map_and_text) + else -> "" + } + } + } + abstract inner class DurationPref( @DrawableRes val iconId: Int, @StringRes val titleId: Int, @@ -612,7 +664,7 @@ class TelegramSettings(private val app: TelegramApplication) { abstract fun setCurrentValue(index: Int) - fun getMenuItems() = values.map { OsmandFormatter.getFormattedDuration(app, it) } + open fun getMenuItems() = values.map { OsmandFormatter.getFormattedDuration(app, it) } } enum class AppConnect( @@ -770,11 +822,12 @@ class TelegramSettings(private val app: TelegramApplication) { var userId = -1 var start = -1L var livePeriod = -1L + var updateTextMessageId = 1 var currentMessageLimit = -1L - var currentMessageId = -1L + var currentMapMessageId = -1L + var currentTextMessageId = -1L var userSetLivePeriod = -1L var userSetLivePeriodStart = -1L - var lastSuccessfulLocation: LatLon? = null var lastSuccessfulSendTimeMs = -1L var shouldDeletePreviousMessage = false var shouldSendViaBotMessage = false @@ -801,7 +854,9 @@ class TelegramSettings(private val app: TelegramApplication) { internal const val START_KEY = "start" internal const val LIVE_PERIOD_KEY = "livePeriod" internal const val LIMIT_KEY = "limit" - internal const val CURRENT_MESSAGE_ID_KEY = "currentMessageId" + internal const val UPDATE_TEXT_MESSAGE_ID_KEY = "updateTextMessageId" + internal const val CURRENT_MAP_MESSAGE_ID_KEY = "currentMapMessageId" + internal const val CURRENT_TEXT_MESSAGE_ID_KEY = "currentTextMessageId" internal const val USER_SET_LIVE_PERIOD_KEY = "userSetLivePeriod" internal const val USER_SET_LIVE_PERIOD_START_KEY = "userSetLivePeriodStart" internal const val LAST_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime" diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt index 3860139bf5..c71e2ec8f2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt @@ -2,8 +2,7 @@ package net.osmand.telegram.helpers import net.osmand.Location import net.osmand.PlatformUtil -import net.osmand.telegram.TelegramApplication -import net.osmand.telegram.TelegramSettings +import net.osmand.telegram.* import net.osmand.telegram.notifications.TelegramNotification.NotificationType import net.osmand.telegram.utils.AndroidNetworkUtils import net.osmand.telegram.utils.BASE_URL @@ -57,7 +56,14 @@ class ShareLocationHelper(private val app: TelegramApplication) { val sharingMode = app.settings.currentSharingMode if (user != null && sharingMode == user.id.toString()) { - app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, latitude, longitude) + 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 = "$BASE_URL/device/$sharingMode/send?lat=$latitude&lon=$longitude" AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false, diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index cb08aafdb1..9d52abdb37 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -9,6 +9,7 @@ import net.osmand.aidl.maplayer.point.AMapPoint import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation +import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation import net.osmand.telegram.helpers.TelegramUiHelper.ListItem import net.osmand.telegram.utils.AndroidUtils import org.drinkless.td.libcore.telegram.TdApi @@ -79,7 +80,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { val content = message.content val date = telegramHelper.getLastUpdatedTime(message) val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime - if (chatTitle != null && content is TdApi.MessageLocation) { + if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserTextLocation && content.isValid()))) { var userName = "" var photoPath: String? = null val user = telegramHelper.getUser(message.senderUserId) @@ -102,12 +103,19 @@ class ShowLocationHelper(private val app: TelegramApplication) { } setupMapLayer() val params = generatePointParams(photoPath, stale) - if (update) { - osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, - chatTitle, Color.WHITE, ALatLon(content.location.latitude, content.location.longitude), null, params) - } else { - osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, - chatTitle, Color.WHITE, ALatLon(content.location.latitude, content.location.longitude), null, params) + val aLatLon = when (content) { + is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude) + is MessageUserTextLocation -> ALatLon(content.lat, content.lon) + else -> null + } + if (aLatLon != null) { + if (update) { + osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, + chatTitle, Color.WHITE, aLatLon, null, params) + } else { + osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, + chatTitle, Color.WHITE, aLatLon, null, params) + } } } else if (chatTitle != null && content is MessageOsmAndBotLocation && content.isValid()) { val name = content.name @@ -242,7 +250,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { private fun removeMapPoint(chatId: Long, message: TdApi.Message) { val content = message.content - if (content is TdApi.MessageLocation) { + if (content is TdApi.MessageLocation || content is MessageUserTextLocation) { osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}") } else if (content is MessageOsmAndBotLocation) { osmandAidlHelper.removeMapPoint(MAP_LAYER_ID, "${chatId}_${content.name}") diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index 142fac8c83..1e8a72cb44 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -1,6 +1,7 @@ package net.osmand.telegram.helpers import android.text.TextUtils +import net.osmand.Location import net.osmand.PlatformUtil import net.osmand.telegram.TelegramSettings import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.* @@ -34,6 +35,7 @@ class TelegramHelper private constructor() { 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 FEW_SECONDS_AGO = "few seconds ago" private const val SECONDS_AGO_SUFFIX = " seconds ago" @@ -200,10 +202,10 @@ class TelegramHelper private constructor() { fun getLastUpdatedTime(message: TdApi.Message): Int { val content = message.content - return if (content is MessageOsmAndBotLocation) { - content.lastUpdated - } else { - Math.max(message.editDate, message.date) + return when (content) { + is MessageOsmAndBotLocation -> content.lastUpdated + is MessageUserTextLocation -> content.lastUpdated + else -> Math.max(message.editDate, message.date) } } @@ -391,7 +393,12 @@ class TelegramHelper private constructor() { fun hasGrayscaleUserPhoto(userId: Int): Boolean { return File("$appDir/$GRAYSCALE_PHOTOS_DIR$userId$GRAYSCALE_PHOTOS_EXT").exists() } - + + private fun isUserLocationMessage(message: TdApi.Message): Boolean { + val cont = message.content + return (cont is MessageUserTextLocation || cont is TdApi.MessageLocation) + } + private fun hasLocalUserPhoto(user: TdApi.User): Boolean { val localPhoto = user.profilePhoto?.small?.local return if (localPhoto != null) { @@ -651,7 +658,11 @@ class TelegramHelper private constructor() { val viaBot = isOsmAndBot(message.viaBotUserId) val oldContent = message.content if (oldContent is TdApi.MessageText) { - message.content = parseOsmAndBotLocation(oldContent.text.text) + if (oldContent.text.text.startsWith(DEVICE_PREFIX)) { + message.content = parseTextLocation(oldContent.text.text) + } else if (oldContent.text.text.startsWith(USER_TEXT_LOCATION_TITLE)) { + message.content = parseTextLocation(oldContent.text.text, false) + } } else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) { message.content = parseOsmAndBotLocation(message) } @@ -680,7 +691,7 @@ class TelegramHelper private constructor() { } } } - } else if (sameSender) { + } else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)) { iterator.remove() } } @@ -709,9 +720,9 @@ class TelegramHelper private constructor() { } fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) { - if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) { + if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) { client?.send( - TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMessageId, null, null)) { obj -> + TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, null)) { obj -> handleLiveLocationMessageUpdate(obj, shareInfo) } } @@ -754,20 +765,28 @@ class TelegramHelper private constructor() { } } - private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) { - if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) { - log.info("recreateLiveLocationMessage - $shareInfo.currentMessageId") + private fun recreateLiveLocationMessage( + shareInfo: TelegramSettings.ShareChatInfo, + content: TdApi.InputMessageContent + ) { + if (shareInfo.chatId != -1L) { val array = LongArray(1) - array[0] = shareInfo.currentMessageId - client?.send(TdApi.DeleteMessages(shareInfo.chatId, array, true)) { obj -> - when (obj.constructor) { - TdApi.Ok.CONSTRUCTOR -> sendNewLiveLocationMessage(shareInfo, content) - TdApi.Error.CONSTRUCTOR -> { - val error = obj as TdApi.Error - if (error.code != IGNORED_ERROR_CODE) { - needRefreshActiveLiveLocationMessages = true - outgoingMessagesListeners.forEach { - it.onSendLiveLocationError(error.code, error.message) + if (content is TdApi.InputMessageLocation) { + array[0] = shareInfo.currentMapMessageId + } else if (content is TdApi.InputMessageLocation) { + array[0] = shareInfo.currentTextMessageId + } + if (array[0] != 0L) { + client?.send(TdApi.DeleteMessages(shareInfo.chatId, array, true)) { obj -> + when (obj.constructor) { + TdApi.Ok.CONSTRUCTOR -> sendNewLiveLocationMessage(shareInfo, content) + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + if (error.code != IGNORED_ERROR_CODE) { + needRefreshActiveLiveLocationMessages = true + outgoingMessagesListeners.forEach { + it.onSendLiveLocationError(error.code, error.message) + } } } } @@ -777,7 +796,7 @@ class TelegramHelper private constructor() { needRefreshActiveLiveLocationMessages = true } - private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) { + private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) { needRefreshActiveLiveLocationMessages = true log.info("sendNewLiveLocationMessage") client?.send( @@ -799,12 +818,12 @@ class TelegramHelper private constructor() { shareInfo.livePeriod.toInt() } val content = TdApi.InputMessageLocation(location, livePeriod) - val msgId = shareInfo.currentMessageId + val msgId = shareInfo.currentMapMessageId if (msgId != -1L) { if (shareInfo.shouldDeletePreviousMessage) { recreateLiveLocationMessage(shareInfo, content) shareInfo.shouldDeletePreviousMessage = false - shareInfo.currentMessageId = -1 + shareInfo.currentMapMessageId = -1 } else { log.info("EditMessageLiveLocation - $msgId") client?.send( @@ -851,6 +870,91 @@ class TelegramHelper private constructor() { } } + fun sendLiveLocationText(chatsShareInfo: Map, location: Location?) { + chatsShareInfo.forEach { (chatId, shareInfo) -> + if (shareInfo.getChatLiveMessageExpireTime() <= 0) { + return@forEach + } + val msgId = shareInfo.currentTextMessageId + if (msgId == -1L) { + shareInfo.updateTextMessageId = 1 + } + val content = getTextMessageContent(shareInfo.updateTextMessageId, location) + if (msgId != -1L) { + if (shareInfo.shouldDeletePreviousMessage) { + recreateLiveLocationMessage(shareInfo, content) + shareInfo.shouldDeletePreviousMessage = false + shareInfo.currentTextMessageId = -1 + shareInfo.updateTextMessageId = 1 + } else { + client?.send(TdApi.EditMessageText(chatId, msgId, null, content)) { obj -> + handleLiveLocationMessageUpdate(obj, shareInfo) + } + } + } else { + client?.send(TdApi.SendMessage(chatId, 0, false, false, null, content), UpdatesHandler()) + } + } + } + + private fun formatLocation(sig: Location?, now: Boolean): String { + var locMsg = "n/a" + if (sig != null) { + locMsg = String.format(Locale.US,"%.5f, %.5f (%s)", sig.latitude, sig.longitude, formatTime(sig.time, now)) + } + return locMsg + } + + private fun formatTime(ti: Long, now: Boolean): String { + val dt = Date(ti) + val current = System.currentTimeMillis() / 1000 + val tm = ti / 1000 + return when { + current - tm < 10 -> if (now) "now" else "few seconds ago" + current - tm < 50 -> (current - tm).toString() + " seconds ago" + current - tm < 60 * 60 * 2 -> ((current - tm) / 60).toString() + " minutes ago" + current - tm < 60 * 60 * 24 -> ((current - tm) / (60 * 60)).toString() + " hours ago" + else -> UTC_DATE_FORMAT.format(dt) + " " + UTC_TIME_FORMAT.format(dt) + " UTC" + } + } + + 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() + val builder = StringBuilder() + val locationMessage = formatLocation(location, true) + builder.append("$USER_TEXT_LOCATION_TITLE\n") + entities.add(TdApi.TextEntity(builder.lastIndex, LOCATION_PREFIX.length, TdApi.TextEntityTypeBold())) + builder.append(String.format("$LOCATION_PREFIX%s\n", locationMessage)) + + if (location != null) { + if (location.hasAltitude()) { + entities.add(TdApi.TextEntity(builder.lastIndex, "Altitude:".length, TdApi.TextEntityTypeBold())) + builder.append(String.format(Locale.US, "Altitude: %.1f\n", location.altitude)) + } + if (location.hasSpeed() && location.speed > 0) { + entities.add(TdApi.TextEntity(builder.lastIndex, "Speed:".length, TdApi.TextEntityTypeBold())) + builder.append(String.format(Locale.US, "Speed: %.1f\n", location.speed)) + } + if (location.hasAccuracy() && location.speed == 0.0f) { + entities.add(TdApi.TextEntity(builder.lastIndex, "Horizontal precision:".length, TdApi.TextEntityTypeBold())) + builder.append(String.format(Locale.US, "Horizontal precision: %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()), false, true) + } + /** * @chatId Id of the chat * @message Text of the message @@ -987,9 +1091,10 @@ class TelegramHelper private constructor() { return false } val content = content + val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE) return when (content) { is TdApi.MessageLocation -> true - is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) + is TdApi.MessageText -> (isOsmAndBot) && content.text.text.startsWith(DEVICE_PREFIX) || (isUserTextLocation && senderUserId != currentUser?.id) else -> false } } @@ -1014,13 +1119,16 @@ class TelegramHelper private constructor() { } } - private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation { - val res = MessageOsmAndBotLocation() + private fun parseTextLocation(text: String, botLocation: Boolean = true): MessageLocation { + val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserTextLocation() + var locationNA = false for (s in text.lines()) { when { s.startsWith(DEVICE_PREFIX) -> { - res.name = s.removePrefix(DEVICE_PREFIX) + if (res is MessageOsmAndBotLocation) { + res.name = s.removePrefix(DEVICE_PREFIX) + } } s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> { var locStr: String @@ -1101,10 +1209,8 @@ class TelegramHelper private constructor() { return 0 } - class MessageOsmAndBotLocation : TdApi.MessageContent() { + abstract class MessageLocation : TdApi.MessageContent() { - var name: String = "" - internal set var lat: Double = Double.NaN internal set var lon: Double = Double.NaN @@ -1114,7 +1220,21 @@ class TelegramHelper private constructor() { override fun getConstructor() = -1 - fun isValid() = name != "" && lat != Double.NaN && lon != Double.NaN + 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 { @@ -1331,8 +1451,10 @@ class TelegramHelper private constructor() { synchronized(message) { lastTelegramUpdateTime = Math.max(message.date, message.editDate) val newContent = updateMessageContent.newContent + val fromBot = isOsmAndBot(message.senderUserId) + val viaBot = isOsmAndBot(message.viaBotUserId) message.content = if (newContent is TdApi.MessageText) { - parseOsmAndBotLocation(newContent.text.text) + parseTextLocation(newContent.text.text, (fromBot || viaBot)) } else if (newContent is TdApi.MessageLocation && (isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) { parseOsmAndBotLocationContent(message.content as MessageOsmAndBotLocation, newContent) @@ -1452,6 +1574,13 @@ class TelegramHelper private constructor() { fullInfoUpdatesListeners.forEach { it.onSupergroupFullInfoUpdated(id, info) } } } + TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> { + val udateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded + val message = udateMessageSendSucceeded.message + outgoingMessagesListeners.forEach { + it.onUpdateMessages(listOf(message)) + } + } } } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt index e9b11f081d..a1670f1912 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt @@ -7,6 +7,7 @@ import net.osmand.data.LatLon import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation +import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation import org.drinkless.td.libcore.telegram.TdApi object TelegramUiHelper { @@ -67,6 +68,8 @@ object TelegramUiHelper { val content = message.content if (content is TdApi.MessageLocation) { res.latLon = LatLon(content.location.latitude, content.location.longitude) + } else if (content is MessageUserTextLocation) { + res.latLon = LatLon(content.lat, content.lon) } } if (user != null) { @@ -106,6 +109,7 @@ object TelegramUiHelper { val content = message.content return when (content) { is MessageOsmAndBotLocation -> botMessageToLocationItem(chat, content) + is MessageUserTextLocation -> locationMessageToLocationItem(helper, chat, message) is TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message) else -> null } @@ -120,6 +124,7 @@ object TelegramUiHelper { return when (content) { is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content) is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message) + is MessageUserTextLocation -> locationMessageToChatItem(helper, chat, message) else -> null } } @@ -148,12 +153,16 @@ object TelegramUiHelper { message: TdApi.Message ): LocationItem? { val user = helper.getUser(message.senderUserId) ?: return null - val content = message.content as TdApi.MessageLocation + val content = message.content return LocationItem().apply { chatId = chat.id chatTitle = chat.title name = TelegramUiHelper.getUserName(user) - latLon = LatLon(content.location.latitude, content.location.longitude) + latLon = when (content) { + is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude) + is MessageUserTextLocation -> LatLon(content.lat, content.lon) + else -> null + } photoPath = helper.getUserPhotoPath(user) grayscalePhotoPath = helper.getUserGreyPhotoPath(user) placeholderId = R.drawable.img_user_picture @@ -190,12 +199,16 @@ object TelegramUiHelper { message: TdApi.Message ): ChatItem? { val user = helper.getUser(message.senderUserId) ?: return null - val content = message.content as TdApi.MessageLocation + val content = message.content return ChatItem().apply { chatId = chat.id chatTitle = chat.title name = TelegramUiHelper.getUserName(user) - latLon = LatLon(content.location.latitude, content.location.longitude) + latLon = when (content) { + is TdApi.MessageLocation -> LatLon(content.location.latitude, content.location.longitude) + is MessageUserTextLocation -> LatLon(content.lat, content.lon) + else -> null + } if (helper.isGroup(chat)) { photoPath = helper.getUserPhotoPath(user) groupPhotoPath = chat.photo?.small?.local?.path diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt index 58fdf0e6d0..bb56ec9643 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt @@ -98,8 +98,14 @@ class MyLocationTabFragment : Fragment(), TelegramListener { sharingMode = settings.hasAnyChatToShareLocation() savedInstanceState?.apply { - selectedChats.addAll(getLongArray(SELECTED_CHATS_KEY).toSet()) - selectedUsers.addAll(getLongArray(SELECTED_CHATS_USERS).toSet()) + val chatsArray = getLongArray(SELECTED_CHATS_KEY) + val usersArray = getLongArray(SELECTED_CHATS_KEY) + if (chatsArray != null) { + selectedChats.addAll(chatsArray.toSet()) + } + if (usersArray != null) { + selectedUsers.addAll(usersArray.toSet()) + } actionButtonsListener?.switchButtonsVisibility((selectedUsers.isNotEmpty() || selectedChats.isNotEmpty())) } @@ -237,6 +243,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray()) + outState.putLongArray(SELECTED_CHATS_USERS, selectedUsers.toLongArray()) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -461,6 +468,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener { private fun updateList() { val items: MutableList = mutableListOf() + val chats: MutableList = mutableListOf() val currentUser = telegramHelper.getCurrentUser() val contacts = telegramHelper.getContacts() val chatList = if (sharingMode) { @@ -478,12 +486,14 @@ class MyLocationTabFragment : Fragment(), TelegramListener { continue } } - items.add(chat) + chats.add(chat) } } + items.addAll(chats) if (!sharingMode) { for (user in contacts.values) { - if ((!sharingMode && settings.isSharingLocationToUser(user.id)) || user.id == currentUser?.id) { + val containsInChats = chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id } + if ((!sharingMode && settings.isSharingLocationToUser(user.id)) || user.id == currentUser?.id || containsInChats) { continue } items.add(user) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt index 2c169ce44d..44db164777 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt @@ -228,7 +228,11 @@ class SettingsDialogFragment : BaseDialogFragment() { isModal = true anchorView = valueView setContentWidth(AndroidUtils.getPopupMenuWidth(ctx, menuList)) - height = AndroidUtils.getPopupMenuHeight(ctx) + height = if (menuList.size < 6) { + ListPopupWindow.WRAP_CONTENT + } else { + AndroidUtils.getPopupMenuHeight(ctx) + } setDropDownGravity(Gravity.END or Gravity.TOP) setAdapter(ArrayAdapter(ctx, R.layout.popup_list_text_item, menuList)) setOnItemClickListener { _, _, position, _ ->