Merge pull request #6351 from osmandapp/TelegramSharingImprovements

Telegram share text messages
This commit is contained in:
Alexey 2018-12-10 19:48:16 +03:00 committed by GitHub
commit 999d8cb459
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 299 additions and 69 deletions

View file

@ -1,4 +1,9 @@
<resources> <resources>
<string name="send_location_as">Send location as</string>
<string name="send_location_as_descr">Choose how messages with your location will look like.</string>
<string name="shared_string_map">Map</string>
<string name="shared_string_text">Text</string>
<string name="map_and_text">Map and text</string>
<string name="last_update_from_telegram">Last update from Telegram</string> <string name="last_update_from_telegram">Last update from Telegram</string>
<string name="enter_another_device_name">Pick a name you haven\'t already used</string> <string name="enter_another_device_name">Pick a name you haven\'t already used</string>
<string name="device_added_successfully">%1$s added.</string> <string name="device_added_successfully">%1$s added.</string>

View file

@ -7,7 +7,6 @@ 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.data.LatLon
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
@ -43,9 +42,15 @@ private val LOC_HISTORY_VALUES_SEC = listOf(
24 * 60 * 60L 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 SEND_MY_LOC_DEFAULT_INDEX = 6
private const val STALE_LOC_DEFAULT_INDEX = 0 private const val STALE_LOC_DEFAULT_INDEX = 0
private const val LOC_HISTORY_DEFAULT_INDEX = 7 private const val LOC_HISTORY_DEFAULT_INDEX = 7
private const val SHARE_TYPE_DEFAULT_INDEX = 2
private const val SETTINGS_NAME = "osmand_telegram_settings" 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 SEND_MY_LOC_INTERVAL_KEY = "send_my_loc_interval"
private const val STALE_LOC_TIME_KEY = "stale_loc_time" private const val STALE_LOC_TIME_KEY = "stale_loc_time"
private const val LOC_HISTORY_TIME_KEY = "loc_history_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" 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 sendMyLocInterval = SEND_MY_LOC_VALUES_SEC[SEND_MY_LOC_DEFAULT_INDEX]
var staleLocTime = STALE_LOC_VALUES_SEC[STALE_LOC_DEFAULT_INDEX] var staleLocTime = STALE_LOC_VALUES_SEC[STALE_LOC_DEFAULT_INDEX]
var locHistoryTime = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX] var locHistoryTime = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX]
var shareTypeValue = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX]
var appToConnectPackage = "" var appToConnectPackage = ""
private set private set
var liveNowSortType = LiveNowSortType.SORT_BY_GROUP var liveNowSortType = LiveNowSortType.SORT_BY_GROUP
val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref()) val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref())
var batteryOptimisationAsked = false var batteryOptimisationAsked = false
@ -236,12 +243,15 @@ class TelegramSettings(private val app: TelegramApplication) {
val shareChatInfo = shareChatsInfo[message.chatId] val shareChatInfo = shareChatsInfo[message.chatId]
val content = message.content val content = message.content
if (shareChatInfo != null) { if (shareChatInfo != null) {
shareChatInfo.currentMessageId = message.id when (content) {
if (content is TdApi.MessageLocation) { is TdApi.MessageLocation -> shareChatInfo.currentMapMessageId = message.id
shareChatInfo.lastSuccessfulLocation = LatLon(content.location.latitude, content.location.longitude) is TdApi.MessageText -> {
shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L shareChatInfo.currentTextMessageId = message.id
shareChatInfo.updateTextMessageId++
} }
} }
shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L
}
} }
fun updateSharingStatusHistory() { fun updateSharingStatusHistory() {
@ -384,9 +394,14 @@ class TelegramSettings(private val app: TelegramApplication) {
} }
fun onDeleteLiveMessages(chatId: Long, messages: List<Long>) { fun onDeleteLiveMessages(chatId: Long, messages: List<Long>) {
val currentMessageId = shareChatsInfo[chatId]?.currentMessageId val currentMapMessageId = shareChatsInfo[chatId]?.currentMapMessageId
if (messages.contains(currentMessageId)) { if (messages.contains(currentMapMessageId)) {
shareChatsInfo[chatId]?.currentMessageId = -1 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(STALE_LOC_TIME_KEY, staleLocTime)
edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime) edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime)
edit.putString(SHARE_TYPE_KEY, shareTypeValue)
edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage) edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage)
edit.putString(LIVE_NOW_SORT_TYPE_KEY, liveNowSortType.name) 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) staleLocTime = prefs.getLong(STALE_LOC_TIME_KEY, staleLocDef)
val locHistoryDef = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX] val locHistoryDef = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX]
locHistoryTime = prefs.getLong(LOC_HISTORY_TIME_KEY, locHistoryDef) 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, "") 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.START_KEY, chatInfo.start)
obj.put(ShareChatInfo.LIVE_PERIOD_KEY, chatInfo.livePeriod) obj.put(ShareChatInfo.LIVE_PERIOD_KEY, chatInfo.livePeriod)
obj.put(ShareChatInfo.LIMIT_KEY, chatInfo.currentMessageLimit) 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_KEY, chatInfo.userSetLivePeriod)
obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY, chatInfo.userSetLivePeriodStart) obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY, chatInfo.userSetLivePeriodStart)
obj.put(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_KEY, chatInfo.lastSuccessfulSendTimeMs) 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) start = obj.optLong(ShareChatInfo.START_KEY)
livePeriod = obj.optLong(ShareChatInfo.LIVE_PERIOD_KEY) livePeriod = obj.optLong(ShareChatInfo.LIVE_PERIOD_KEY)
currentMessageLimit = obj.optLong(ShareChatInfo.LIMIT_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) userSetLivePeriod = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY)
userSetLivePeriodStart = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY) userSetLivePeriodStart = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY)
lastSuccessfulSendTimeMs = obj.optLong(ShareChatInfo.LAST_SUCCESSFUL_SEND_TIME_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<String> {
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( abstract inner class DurationPref(
@DrawableRes val iconId: Int, @DrawableRes val iconId: Int,
@StringRes val titleId: Int, @StringRes val titleId: Int,
@ -612,7 +664,7 @@ class TelegramSettings(private val app: TelegramApplication) {
abstract fun setCurrentValue(index: Int) 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( enum class AppConnect(
@ -770,11 +822,12 @@ class TelegramSettings(private val app: TelegramApplication) {
var userId = -1 var userId = -1
var start = -1L var start = -1L
var livePeriod = -1L var livePeriod = -1L
var updateTextMessageId = 1
var currentMessageLimit = -1L var currentMessageLimit = -1L
var currentMessageId = -1L var currentMapMessageId = -1L
var currentTextMessageId = -1L
var userSetLivePeriod = -1L var userSetLivePeriod = -1L
var userSetLivePeriodStart = -1L var userSetLivePeriodStart = -1L
var lastSuccessfulLocation: LatLon? = null
var lastSuccessfulSendTimeMs = -1L var lastSuccessfulSendTimeMs = -1L
var shouldDeletePreviousMessage = false var shouldDeletePreviousMessage = false
var shouldSendViaBotMessage = false var shouldSendViaBotMessage = false
@ -801,7 +854,9 @@ class TelegramSettings(private val app: TelegramApplication) {
internal const val START_KEY = "start" internal const val START_KEY = "start"
internal const val LIVE_PERIOD_KEY = "livePeriod" internal const val LIVE_PERIOD_KEY = "livePeriod"
internal const val LIMIT_KEY = "limit" 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_KEY = "userSetLivePeriod"
internal const val USER_SET_LIVE_PERIOD_START_KEY = "userSetLivePeriodStart" internal const val USER_SET_LIVE_PERIOD_START_KEY = "userSetLivePeriodStart"
internal const val LAST_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime" internal const val LAST_SUCCESSFUL_SEND_TIME_KEY = "lastSuccessfulSendTime"

View file

@ -2,8 +2,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.TelegramApplication import net.osmand.telegram.*
import net.osmand.telegram.TelegramSettings
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
@ -57,7 +56,14 @@ class ShareLocationHelper(private val app: TelegramApplication) {
val sharingMode = app.settings.currentSharingMode val sharingMode = app.settings.currentSharingMode
if (user != null && sharingMode == user.id.toString()) { 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.sendLiveLocationMessage(chatsShareInfo, latitude, longitude)
app.telegramHelper.sendLiveLocationText(chatsShareInfo, location)
}
}
} else if (sharingMode.isNotEmpty()) { } else if (sharingMode.isNotEmpty()) {
val url = "$BASE_URL/device/$sharingMode/send?lat=$latitude&lon=$longitude" val url = "$BASE_URL/device/$sharingMode/send?lat=$latitude&lon=$longitude"
AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false, AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false,

View file

@ -9,6 +9,7 @@ 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.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 org.drinkless.td.libcore.telegram.TdApi import org.drinkless.td.libcore.telegram.TdApi
@ -79,7 +80,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
val content = message.content val content = message.content
val date = telegramHelper.getLastUpdatedTime(message) val date = telegramHelper.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) { if (chatTitle != null && (content is TdApi.MessageLocation || (content is MessageUserTextLocation && 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)
@ -102,12 +103,19 @@ class ShowLocationHelper(private val app: TelegramApplication) {
} }
setupMapLayer() setupMapLayer()
val params = generatePointParams(photoPath, stale) val params = generatePointParams(photoPath, stale)
val aLatLon = when (content) {
is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude)
is MessageUserTextLocation -> ALatLon(content.lat, content.lon)
else -> null
}
if (aLatLon != null) {
if (update) { if (update) {
osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName,
chatTitle, Color.WHITE, ALatLon(content.location.latitude, content.location.longitude), null, params) chatTitle, Color.WHITE, aLatLon, null, params)
} else { } else {
osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName, osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_${message.senderUserId}", userName, userName,
chatTitle, Color.WHITE, ALatLon(content.location.latitude, content.location.longitude), null, params) chatTitle, Color.WHITE, aLatLon, null, params)
}
} }
} else if (chatTitle != null && content is MessageOsmAndBotLocation && content.isValid()) { } else if (chatTitle != null && content is MessageOsmAndBotLocation && content.isValid()) {
val name = content.name val name = content.name
@ -242,7 +250,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) { if (content is TdApi.MessageLocation || content is MessageUserTextLocation) {
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}")

View file

@ -1,6 +1,7 @@
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.TelegramSettings import net.osmand.telegram.TelegramSettings
import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.* import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.*
@ -34,6 +35,7 @@ class TelegramHelper private constructor() {
private const val LOCATION_PREFIX = "Location: " private const val LOCATION_PREFIX = "Location: "
private const val LAST_LOCATION_PREFIX = "Last location: " private const val LAST_LOCATION_PREFIX = "Last location: "
private const val UPDATED_PREFIX = "Updated: " 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 FEW_SECONDS_AGO = "few seconds ago"
private const val SECONDS_AGO_SUFFIX = " seconds ago" private const val SECONDS_AGO_SUFFIX = " seconds ago"
@ -200,10 +202,10 @@ class TelegramHelper private constructor() {
fun getLastUpdatedTime(message: TdApi.Message): Int { fun getLastUpdatedTime(message: TdApi.Message): Int {
val content = message.content val content = message.content
return if (content is MessageOsmAndBotLocation) { return when (content) {
content.lastUpdated is MessageOsmAndBotLocation -> content.lastUpdated
} else { is MessageUserTextLocation -> content.lastUpdated
Math.max(message.editDate, message.date) else -> Math.max(message.editDate, message.date)
} }
} }
@ -392,6 +394,11 @@ class TelegramHelper private constructor() {
return File("$appDir/$GRAYSCALE_PHOTOS_DIR$userId$GRAYSCALE_PHOTOS_EXT").exists() 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 { private fun hasLocalUserPhoto(user: TdApi.User): Boolean {
val localPhoto = user.profilePhoto?.small?.local val localPhoto = user.profilePhoto?.small?.local
return if (localPhoto != null) { return if (localPhoto != null) {
@ -651,7 +658,11 @@ class TelegramHelper private constructor() {
val viaBot = isOsmAndBot(message.viaBotUserId) val viaBot = isOsmAndBot(message.viaBotUserId)
val oldContent = message.content val oldContent = message.content
if (oldContent is TdApi.MessageText) { 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)) { } else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
message.content = parseOsmAndBotLocation(message) message.content = parseOsmAndBotLocation(message)
} }
@ -680,7 +691,7 @@ class TelegramHelper private constructor() {
} }
} }
} }
} else if (sameSender) { } else if (sameSender && isUserLocationMessage(message) && isUserLocationMessage(newMessage)) {
iterator.remove() iterator.remove()
} }
} }
@ -709,9 +720,9 @@ class TelegramHelper private constructor() {
} }
fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) { fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) {
if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) { if (shareInfo.currentMapMessageId != -1L && shareInfo.chatId != -1L) {
client?.send( client?.send(
TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMessageId, null, null)) { obj -> TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMapMessageId, null, null)) { obj ->
handleLiveLocationMessageUpdate(obj, shareInfo) handleLiveLocationMessageUpdate(obj, shareInfo)
} }
} }
@ -754,11 +765,18 @@ class TelegramHelper private constructor() {
} }
} }
private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) { private fun recreateLiveLocationMessage(
if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) { shareInfo: TelegramSettings.ShareChatInfo,
log.info("recreateLiveLocationMessage - $shareInfo.currentMessageId") content: TdApi.InputMessageContent
) {
if (shareInfo.chatId != -1L) {
val array = LongArray(1) val array = LongArray(1)
array[0] = shareInfo.currentMessageId 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 -> client?.send(TdApi.DeleteMessages(shareInfo.chatId, array, true)) { obj ->
when (obj.constructor) { when (obj.constructor) {
TdApi.Ok.CONSTRUCTOR -> sendNewLiveLocationMessage(shareInfo, content) TdApi.Ok.CONSTRUCTOR -> sendNewLiveLocationMessage(shareInfo, content)
@ -774,10 +792,11 @@ class TelegramHelper private constructor() {
} }
} }
} }
}
needRefreshActiveLiveLocationMessages = true needRefreshActiveLiveLocationMessages = true
} }
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) { private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageContent) {
needRefreshActiveLiveLocationMessages = true needRefreshActiveLiveLocationMessages = true
log.info("sendNewLiveLocationMessage") log.info("sendNewLiveLocationMessage")
client?.send( client?.send(
@ -799,12 +818,12 @@ 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.currentMessageId val msgId = shareInfo.currentMapMessageId
if (msgId != -1L) { if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousMessage) { if (shareInfo.shouldDeletePreviousMessage) {
recreateLiveLocationMessage(shareInfo, content) recreateLiveLocationMessage(shareInfo, content)
shareInfo.shouldDeletePreviousMessage = false shareInfo.shouldDeletePreviousMessage = false
shareInfo.currentMessageId = -1 shareInfo.currentMapMessageId = -1
} else { } else {
log.info("EditMessageLiveLocation - $msgId") log.info("EditMessageLiveLocation - $msgId")
client?.send( client?.send(
@ -851,6 +870,91 @@ class TelegramHelper private constructor() {
} }
} }
fun sendLiveLocationText(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, 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<TdApi.TextEntity>()
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 * @chatId Id of the chat
* @message Text of the message * @message Text of the message
@ -987,9 +1091,10 @@ class TelegramHelper private constructor() {
return false return false
} }
val content = content val content = content
val isUserTextLocation = (content is TdApi.MessageText) && content.text.text.startsWith(USER_TEXT_LOCATION_TITLE)
return when (content) { return when (content) {
is TdApi.MessageLocation -> true 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 else -> false
} }
} }
@ -1014,14 +1119,17 @@ class TelegramHelper private constructor() {
} }
} }
private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation { private fun parseTextLocation(text: String, botLocation: Boolean = true): MessageLocation {
val res = MessageOsmAndBotLocation() val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserTextLocation()
var locationNA = false var locationNA = false
for (s in text.lines()) { for (s in text.lines()) {
when { when {
s.startsWith(DEVICE_PREFIX) -> { s.startsWith(DEVICE_PREFIX) -> {
if (res is MessageOsmAndBotLocation) {
res.name = s.removePrefix(DEVICE_PREFIX) res.name = s.removePrefix(DEVICE_PREFIX)
} }
}
s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> { s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> {
var locStr: String var locStr: String
var parse = true var parse = true
@ -1101,10 +1209,8 @@ class TelegramHelper private constructor() {
return 0 return 0
} }
class MessageOsmAndBotLocation : TdApi.MessageContent() { abstract class MessageLocation : TdApi.MessageContent() {
var name: String = ""
internal set
var lat: Double = Double.NaN var lat: Double = Double.NaN
internal set internal set
var lon: Double = Double.NaN var lon: Double = Double.NaN
@ -1114,7 +1220,21 @@ class TelegramHelper private constructor() {
override fun getConstructor() = -1 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<OrderedChat> { class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long, internal val isChannel: Boolean) : Comparable<OrderedChat> {
@ -1331,8 +1451,10 @@ class TelegramHelper private constructor() {
synchronized(message) { synchronized(message) {
lastTelegramUpdateTime = Math.max(message.date, message.editDate) lastTelegramUpdateTime = Math.max(message.date, message.editDate)
val newContent = updateMessageContent.newContent val newContent = updateMessageContent.newContent
val fromBot = isOsmAndBot(message.senderUserId)
val viaBot = isOsmAndBot(message.viaBotUserId)
message.content = if (newContent is TdApi.MessageText) { message.content = if (newContent is TdApi.MessageText) {
parseOsmAndBotLocation(newContent.text.text) parseTextLocation(newContent.text.text, (fromBot || viaBot))
} else if (newContent is TdApi.MessageLocation && } else if (newContent is TdApi.MessageLocation &&
(isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) { (isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) {
parseOsmAndBotLocationContent(message.content as MessageOsmAndBotLocation, newContent) parseOsmAndBotLocationContent(message.content as MessageOsmAndBotLocation, newContent)
@ -1452,6 +1574,13 @@ class TelegramHelper private constructor() {
fullInfoUpdatesListeners.forEach { it.onSupergroupFullInfoUpdated(id, info) } 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))
}
}
} }
} }
} }

View file

@ -7,6 +7,7 @@ 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.helpers.TelegramHelper.MessageOsmAndBotLocation
import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation
import org.drinkless.td.libcore.telegram.TdApi import org.drinkless.td.libcore.telegram.TdApi
object TelegramUiHelper { object TelegramUiHelper {
@ -67,6 +68,8 @@ object TelegramUiHelper {
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) {
res.latLon = LatLon(content.lat, content.lon)
} }
} }
if (user != null) { if (user != null) {
@ -106,6 +109,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 TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message) is TdApi.MessageLocation -> locationMessageToLocationItem(helper, chat, message)
else -> null else -> null
} }
@ -120,6 +124,7 @@ 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)
else -> null else -> null
} }
} }
@ -148,12 +153,16 @@ object TelegramUiHelper {
message: TdApi.Message message: TdApi.Message
): LocationItem? { ): LocationItem? {
val user = helper.getUser(message.senderUserId) ?: return null val user = helper.getUser(message.senderUserId) ?: return null
val content = message.content as TdApi.MessageLocation val content = message.content
return LocationItem().apply { return LocationItem().apply {
chatId = chat.id chatId = chat.id
chatTitle = chat.title chatTitle = chat.title
name = TelegramUiHelper.getUserName(user) 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) photoPath = helper.getUserPhotoPath(user)
grayscalePhotoPath = helper.getUserGreyPhotoPath(user) grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
placeholderId = R.drawable.img_user_picture placeholderId = R.drawable.img_user_picture
@ -190,12 +199,16 @@ object TelegramUiHelper {
message: TdApi.Message message: TdApi.Message
): ChatItem? { ): ChatItem? {
val user = helper.getUser(message.senderUserId) ?: return null val user = helper.getUser(message.senderUserId) ?: return null
val content = message.content as TdApi.MessageLocation val content = message.content
return ChatItem().apply { return ChatItem().apply {
chatId = chat.id chatId = chat.id
chatTitle = chat.title chatTitle = chat.title
name = TelegramUiHelper.getUserName(user) 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)) { if (helper.isGroup(chat)) {
photoPath = helper.getUserPhotoPath(user) photoPath = helper.getUserPhotoPath(user)
groupPhotoPath = chat.photo?.small?.local?.path groupPhotoPath = chat.photo?.small?.local?.path

View file

@ -98,8 +98,14 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
sharingMode = settings.hasAnyChatToShareLocation() sharingMode = settings.hasAnyChatToShareLocation()
savedInstanceState?.apply { savedInstanceState?.apply {
selectedChats.addAll(getLongArray(SELECTED_CHATS_KEY).toSet()) val chatsArray = getLongArray(SELECTED_CHATS_KEY)
selectedUsers.addAll(getLongArray(SELECTED_CHATS_USERS).toSet()) 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())) actionButtonsListener?.switchButtonsVisibility((selectedUsers.isNotEmpty() || selectedChats.isNotEmpty()))
} }
@ -237,6 +243,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray()) outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray())
outState.putLongArray(SELECTED_CHATS_USERS, selectedUsers.toLongArray())
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -461,6 +468,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
private fun updateList() { private fun updateList() {
val items: MutableList<TdApi.Object> = mutableListOf() val items: MutableList<TdApi.Object> = mutableListOf()
val chats: MutableList<TdApi.Chat> = mutableListOf()
val currentUser = telegramHelper.getCurrentUser() val currentUser = telegramHelper.getCurrentUser()
val contacts = telegramHelper.getContacts() val contacts = telegramHelper.getContacts()
val chatList = if (sharingMode) { val chatList = if (sharingMode) {
@ -478,12 +486,14 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
continue continue
} }
} }
items.add(chat) chats.add(chat)
} }
} }
items.addAll(chats)
if (!sharingMode) { if (!sharingMode) {
for (user in contacts.values) { 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 continue
} }
items.add(user) items.add(user)

View file

@ -228,7 +228,11 @@ class SettingsDialogFragment : BaseDialogFragment() {
isModal = true isModal = true
anchorView = valueView anchorView = valueView
setContentWidth(AndroidUtils.getPopupMenuWidth(ctx, menuList)) 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) setDropDownGravity(Gravity.END or Gravity.TOP)
setAdapter(ArrayAdapter(ctx, R.layout.popup_list_text_item, menuList)) setAdapter(ArrayAdapter(ctx, R.layout.popup_list_text_item, menuList))
setOnItemClickListener { _, _, position, _ -> setOnItemClickListener { _, _, position, _ ->