Merge branch 'master' of ssh://github.com/osmandapp/Osmand into js_voice_routing
|
@ -1,4 +1,7 @@
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="time_ago">ago</string>
|
||||||
|
<string name="last_response">Last response</string>
|
||||||
|
<string name="shared_string_group">Group</string>
|
||||||
<string name="logout_no_internet_msg">To properly log out from your Telegram account, the Internet is needed.</string>
|
<string name="logout_no_internet_msg">To properly log out from your Telegram account, the Internet is needed.</string>
|
||||||
<string name="shared_string_close">Close</string>
|
<string name="shared_string_close">Close</string>
|
||||||
<string name="disconnect_from_telegram_desc">To invoke access to your Telegram account from OsmAnd, open Telegram, go to Settings - Privacy and Security - Sessions and terminate OsmAnd Telegram session. After that, OsmAnd Location Sharing will no longer have access to your account, and you will not be able to use this app until you log in again.</string>
|
<string name="disconnect_from_telegram_desc">To invoke access to your Telegram account from OsmAnd, open Telegram, go to Settings - Privacy and Security - Sessions and terminate OsmAnd Telegram session. After that, OsmAnd Location Sharing will no longer have access to your account, and you will not be able to use this app until you log in again.</string>
|
||||||
|
|
|
@ -54,7 +54,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
val messages = telegramHelper.getMessages()
|
val messages = telegramHelper.getMessages()
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
val date = Math.max(message.date, message.editDate) * 1000L
|
val date = Math.max(message.date, message.editDate) * 1000L
|
||||||
val expired = System.currentTimeMillis() - date > app.settings.locHistoryTime
|
val expired = System.currentTimeMillis() - date > app.settings.locHistoryTime * 1000L
|
||||||
if (expired) {
|
if (expired) {
|
||||||
removeMapPoint(message.chatId, message)
|
removeMapPoint(message.chatId, message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ class TelegramHelper private constructor() {
|
||||||
|
|
||||||
private const val DEVICE_PREFIX = "Device: "
|
private const val DEVICE_PREFIX = "Device: "
|
||||||
private const val LOCATION_PREFIX = "Location: "
|
private const val LOCATION_PREFIX = "Location: "
|
||||||
|
private const val LAST_LOCATION_PREFIX = "Last location: "
|
||||||
|
|
||||||
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"
|
||||||
|
@ -177,6 +178,8 @@ 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) = 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
|
||||||
|
|
||||||
private fun isChannel(chat: TdApi.Chat): Boolean {
|
private fun isChannel(chat: TdApi.Chat): Boolean {
|
||||||
|
@ -481,16 +484,15 @@ class TelegramHelper private constructor() {
|
||||||
|
|
||||||
private fun addNewMessage(message: TdApi.Message) {
|
private fun addNewMessage(message: TdApi.Message) {
|
||||||
if (message.isAppropriate()) {
|
if (message.isAppropriate()) {
|
||||||
|
val fromBot = isOsmAndBot(message.senderUserId)
|
||||||
|
val viaBot = isOsmAndBot(message.viaBotUserId)
|
||||||
val oldContent = message.content
|
val oldContent = message.content
|
||||||
if (oldContent is TdApi.MessageText) {
|
if (oldContent is TdApi.MessageText) {
|
||||||
val messageOsmAndBotLocation = parseOsmAndBotLocation(oldContent.text.text)
|
message.content = parseOsmAndBotLocation(oldContent.text.text)
|
||||||
messageOsmAndBotLocation.created = message.date
|
} else if (oldContent is TdApi.MessageLocation && (fromBot || viaBot)) {
|
||||||
message.content = messageOsmAndBotLocation
|
|
||||||
} else if (oldContent is TdApi.MessageLocation &&
|
|
||||||
(isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) {
|
|
||||||
message.content = parseOsmAndBotLocation(message)
|
message.content = parseOsmAndBotLocation(message)
|
||||||
}
|
}
|
||||||
removeOldMessages(message.senderUserId, message.chatId)
|
removeOldMessages(message, fromBot, viaBot)
|
||||||
usersLocationMessages[message.id] = message
|
usersLocationMessages[message.id] = message
|
||||||
incomingMessagesListeners.forEach {
|
incomingMessagesListeners.forEach {
|
||||||
it.onReceiveChatLocationMessages(message.chatId, message)
|
it.onReceiveChatLocationMessages(message.chatId, message)
|
||||||
|
@ -498,12 +500,24 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeOldMessages(userId: Int, chatId: Long) {
|
private fun removeOldMessages(newMessage: TdApi.Message, fromBot: Boolean, viaBot: Boolean) {
|
||||||
val user = users[userId]
|
val iterator = usersLocationMessages.entries.iterator()
|
||||||
if (user != null && user.username != OSMAND_BOT_USERNAME) {
|
while (iterator.hasNext()) {
|
||||||
usersLocationMessages.values.filter { it.senderUserId == userId && it.chatId == chatId }
|
val message = iterator.next().value
|
||||||
.forEach {
|
if (newMessage.chatId == message.chatId) {
|
||||||
usersLocationMessages.remove(it.id)
|
val sameSender = newMessage.senderUserId == message.senderUserId
|
||||||
|
val viaSameBot = newMessage.viaBotUserId == message.viaBotUserId
|
||||||
|
if ((fromBot && sameSender) || (viaBot && viaSameBot)) {
|
||||||
|
val newCont = newMessage.content
|
||||||
|
val cont = message.content
|
||||||
|
if (newCont is MessageOsmAndBotLocation && cont is MessageOsmAndBotLocation) {
|
||||||
|
if (newCont.name == cont.name) {
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sameSender) {
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -749,7 +763,8 @@ class TelegramHelper private constructor() {
|
||||||
val content = content
|
val content = content
|
||||||
return when (content) {
|
return when (content) {
|
||||||
is TdApi.MessageLocation -> true
|
is TdApi.MessageLocation -> true
|
||||||
is TdApi.MessageText -> isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId)
|
is TdApi.MessageText -> (isOsmAndBot(senderUserId) || isOsmAndBot(viaBotUserId))
|
||||||
|
&& content.text.text.startsWith(DEVICE_PREFIX)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -760,13 +775,7 @@ class TelegramHelper private constructor() {
|
||||||
name = getOsmAndBotDeviceName(message)
|
name = getOsmAndBotDeviceName(message)
|
||||||
lat = messageLocation.location.latitude
|
lat = messageLocation.location.latitude
|
||||||
lon = messageLocation.location.longitude
|
lon = messageLocation.location.longitude
|
||||||
created = message.date
|
lastUpdated = getLastUpdatedTime(message)
|
||||||
val date = message.editDate
|
|
||||||
lastUpdated = if (date != 0) {
|
|
||||||
date
|
|
||||||
} else {
|
|
||||||
message.date
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,58 +785,71 @@ class TelegramHelper private constructor() {
|
||||||
name = oldContent.name
|
name = oldContent.name
|
||||||
lat = messageLocation.location.latitude
|
lat = messageLocation.location.latitude
|
||||||
lon = messageLocation.location.longitude
|
lon = messageLocation.location.longitude
|
||||||
created = oldContent.created
|
|
||||||
lastUpdated = (System.currentTimeMillis() / 1000).toInt()
|
lastUpdated = (System.currentTimeMillis() / 1000).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation {
|
private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation {
|
||||||
val res = MessageOsmAndBotLocation()
|
val res = MessageOsmAndBotLocation()
|
||||||
|
var locationNA = false
|
||||||
for (s in text.lines()) {
|
for (s in text.lines()) {
|
||||||
when {
|
when {
|
||||||
s.startsWith(DEVICE_PREFIX) -> {
|
s.startsWith(DEVICE_PREFIX) -> {
|
||||||
res.name = s.removePrefix(DEVICE_PREFIX)
|
res.name = s.removePrefix(DEVICE_PREFIX)
|
||||||
}
|
}
|
||||||
s.startsWith(LOCATION_PREFIX) -> {
|
s.startsWith(LOCATION_PREFIX) || s.startsWith(LAST_LOCATION_PREFIX) -> {
|
||||||
val locStr = s.removePrefix(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 {
|
try {
|
||||||
val (latS, lonS) = locStr.split(" ")
|
val (latS, lonS) = locStr.split(" ")
|
||||||
val updatedS = locStr.substring(locStr.indexOf("("), locStr.length)
|
val updatedS = locStr.substring(locStr.indexOf("("), locStr.length)
|
||||||
val timeSecs = parseTime(updatedS.removePrefix("(").removeSuffix(")"))
|
|
||||||
|
res.lastUpdated =
|
||||||
|
(parseTime(updatedS.removePrefix("(").removeSuffix(")")) / 1000).toInt()
|
||||||
res.lat = latS.dropLast(1).toDouble()
|
res.lat = latS.dropLast(1).toDouble()
|
||||||
res.lon = lonS.toDouble()
|
res.lon = lonS.toDouble()
|
||||||
if (timeSecs < messageActiveTimeSec) {
|
|
||||||
res.lastUpdated = (System.currentTimeMillis() / 1000 - timeSecs).toInt()
|
|
||||||
} else {
|
|
||||||
res.lastUpdated = timeSecs
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseTime(timeS: String): Int {
|
private fun parseTime(timeS: String): Long {
|
||||||
try {
|
try {
|
||||||
when {
|
when {
|
||||||
timeS.endsWith(FEW_SECONDS_AGO) -> return 5
|
timeS.endsWith(FEW_SECONDS_AGO) -> return System.currentTimeMillis() - 5000
|
||||||
|
|
||||||
timeS.endsWith(SECONDS_AGO_SUFFIX) -> {
|
timeS.endsWith(SECONDS_AGO_SUFFIX) -> {
|
||||||
val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX)
|
val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX)
|
||||||
return locStr.toInt()
|
return System.currentTimeMillis() - locStr.toLong() * 1000
|
||||||
}
|
}
|
||||||
timeS.endsWith(MINUTES_AGO_SUFFIX) -> {
|
timeS.endsWith(MINUTES_AGO_SUFFIX) -> {
|
||||||
val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX)
|
val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX)
|
||||||
val minutes = locStr.toInt()
|
val minutes = locStr.toLong()
|
||||||
return minutes * 60
|
return System.currentTimeMillis() - minutes * 60 * 1000
|
||||||
}
|
}
|
||||||
timeS.endsWith(HOURS_AGO_SUFFIX) -> {
|
timeS.endsWith(HOURS_AGO_SUFFIX) -> {
|
||||||
val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX)
|
val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX)
|
||||||
val hours = locStr.toInt()
|
val hours = locStr.toLong()
|
||||||
return hours * 60 * 60
|
return (System.currentTimeMillis() - hours * 60 * 60 * 1000)
|
||||||
}
|
}
|
||||||
timeS.endsWith(UTC_FORMAT_SUFFIX) -> {
|
timeS.endsWith(UTC_FORMAT_SUFFIX) -> {
|
||||||
val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX)
|
val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX)
|
||||||
|
@ -835,7 +857,7 @@ class TelegramHelper private constructor() {
|
||||||
val date = UTC_DATE_FORMAT.parse(latS)
|
val date = UTC_DATE_FORMAT.parse(latS)
|
||||||
val time = UTC_TIME_FORMAT.parse(lonS)
|
val time = UTC_TIME_FORMAT.parse(lonS)
|
||||||
val res = date.time + time.time
|
val res = date.time + time.time
|
||||||
return res.toInt()
|
return res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1068,9 +1090,7 @@ class TelegramHelper private constructor() {
|
||||||
synchronized(message) {
|
synchronized(message) {
|
||||||
val newContent = updateMessageContent.newContent
|
val newContent = updateMessageContent.newContent
|
||||||
message.content = if (newContent is TdApi.MessageText) {
|
message.content = if (newContent is TdApi.MessageText) {
|
||||||
val messageOsmAndBotLocation = parseOsmAndBotLocation(newContent.text.text)
|
parseOsmAndBotLocation(newContent.text.text)
|
||||||
messageOsmAndBotLocation.created = message.date
|
|
||||||
messageOsmAndBotLocation
|
|
||||||
} 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)
|
||||||
|
|
|
@ -52,11 +52,6 @@ object TelegramUiHelper {
|
||||||
placeholderId = R.drawable.img_user_picture
|
placeholderId = R.drawable.img_user_picture
|
||||||
}
|
}
|
||||||
val type = chat.type
|
val type = chat.type
|
||||||
val message = messages.firstOrNull()
|
|
||||||
if (message != null) {
|
|
||||||
res.lastUpdated = message.editDate
|
|
||||||
res.created = message.date
|
|
||||||
}
|
|
||||||
if (type is TdApi.ChatTypePrivate || type is TdApi.ChatTypeSecret) {
|
if (type is TdApi.ChatTypePrivate || type is TdApi.ChatTypeSecret) {
|
||||||
val userId = getUserIdFromChatType(type)
|
val userId = getUserIdFromChatType(type)
|
||||||
val chatWithBot = helper.isBot(userId)
|
val chatWithBot = helper.isBot(userId)
|
||||||
|
@ -64,11 +59,15 @@ object TelegramUiHelper {
|
||||||
res.chatWithBot = chatWithBot
|
res.chatWithBot = chatWithBot
|
||||||
if (!chatWithBot) {
|
if (!chatWithBot) {
|
||||||
res.userId = userId
|
res.userId = userId
|
||||||
val content = message?.content
|
val message = messages.firstOrNull { it.viaBotUserId == 0 }
|
||||||
|
if (message != null) {
|
||||||
|
res.lastUpdated = helper.getLastUpdatedTime(message)
|
||||||
|
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 (type is TdApi.ChatTypeBasicGroup) {
|
} else if (type is TdApi.ChatTypeBasicGroup) {
|
||||||
res.placeholderId = R.drawable.img_group_picture
|
res.placeholderId = R.drawable.img_group_picture
|
||||||
res.membersCount = helper.getBasicGroupFullInfo(type.basicGroupId)?.members?.size ?: 0
|
res.membersCount = helper.getBasicGroupFullInfo(type.basicGroupId)?.members?.size ?: 0
|
||||||
|
@ -125,7 +124,6 @@ object TelegramUiHelper {
|
||||||
latLon = LatLon(content.lat, content.lon)
|
latLon = LatLon(content.lat, content.lon)
|
||||||
placeholderId = R.drawable.img_user_picture
|
placeholderId = R.drawable.img_user_picture
|
||||||
lastUpdated = content.lastUpdated
|
lastUpdated = content.lastUpdated
|
||||||
created = content.created
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
@ -147,8 +145,7 @@ object TelegramUiHelper {
|
||||||
photoPath = helper.getUserPhotoPath(user)
|
photoPath = helper.getUserPhotoPath(user)
|
||||||
placeholderId = R.drawable.img_user_picture
|
placeholderId = R.drawable.img_user_picture
|
||||||
userId = message.senderUserId
|
userId = message.senderUserId
|
||||||
lastUpdated = message.editDate
|
lastUpdated = helper.getLastUpdatedTime(message)
|
||||||
created = message.date
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,8 +165,6 @@ object TelegramUiHelper {
|
||||||
internal set
|
internal set
|
||||||
var lastUpdated: Int = 0
|
var lastUpdated: Int = 0
|
||||||
internal set
|
internal set
|
||||||
var created: Int = 0
|
|
||||||
internal set
|
|
||||||
|
|
||||||
abstract fun canBeOpenedOnMap(): Boolean
|
abstract fun canBeOpenedOnMap(): Boolean
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ import net.osmand.telegram.utils.OsmandFormatter
|
||||||
import net.osmand.telegram.utils.UiUtils.UpdateLocationViewCache
|
import net.osmand.telegram.utils.UiUtils.UpdateLocationViewCache
|
||||||
import net.osmand.util.MapUtils
|
import net.osmand.util.MapUtils
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
private const val CHAT_VIEW_TYPE = 0
|
private const val CHAT_VIEW_TYPE = 0
|
||||||
private const val LOCATION_ITEM_VIEW_TYPE = 1
|
private const val LOCATION_ITEM_VIEW_TYPE = 1
|
||||||
|
@ -221,33 +223,34 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
||||||
for ((id, messages) in telegramHelper.getMessagesByChatIds()) {
|
for ((id, messages) in telegramHelper.getMessagesByChatIds()) {
|
||||||
telegramHelper.getChat(id)?.also { chat ->
|
telegramHelper.getChat(id)?.also { chat ->
|
||||||
res.add(TelegramUiHelper.chatToChatItem(telegramHelper, chat, messages))
|
res.add(TelegramUiHelper.chatToChatItem(telegramHelper, chat, messages))
|
||||||
if (needLocationItems(chat.type)) {
|
val type = chat.type
|
||||||
|
if (type is TdApi.ChatTypeBasicGroup || type is TdApi.ChatTypeSupergroup) {
|
||||||
res.addAll(convertToLocationItems(chat, messages))
|
res.addAll(convertToLocationItems(chat, messages))
|
||||||
|
} else if (type is TdApi.ChatTypePrivate) {
|
||||||
|
if (telegramHelper.isOsmAndBot(type.userId)) {
|
||||||
|
res.addAll(convertToLocationItems(chat, messages))
|
||||||
|
} else if (messages.firstOrNull { it.viaBotUserId != 0 } != null) {
|
||||||
|
res.addAll(convertToLocationItems(chat, messages, true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter.items = res
|
adapter.items = res
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun needLocationItems(type: TdApi.ChatType): Boolean {
|
|
||||||
return when (type) {
|
|
||||||
is TdApi.ChatTypeBasicGroup -> true
|
|
||||||
is TdApi.ChatTypeSupergroup -> true
|
|
||||||
is TdApi.ChatTypePrivate -> telegramHelper.isOsmAndBot(type.userId)
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun convertToLocationItems(
|
private fun convertToLocationItems(
|
||||||
chat: TdApi.Chat,
|
chat: TdApi.Chat,
|
||||||
messages: List<TdApi.Message>
|
messages: List<TdApi.Message>,
|
||||||
|
addOnlyViaBotMessages: Boolean = false
|
||||||
): List<LocationItem> {
|
): List<LocationItem> {
|
||||||
val res = mutableListOf<LocationItem>()
|
val res = mutableListOf<LocationItem>()
|
||||||
messages.forEach { message ->
|
messages.forEach { message ->
|
||||||
|
if (!addOnlyViaBotMessages || message.viaBotUserId != 0) {
|
||||||
TelegramUiHelper.messageToLocationItem(telegramHelper, chat, message)?.also {
|
TelegramUiHelper.messageToLocationItem(telegramHelper, chat, message)?.also {
|
||||||
res.add(it)
|
res.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,12 +364,18 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getListItemLiveTimeDescr(item: ListItem):String {
|
private fun getListItemLiveTimeDescr(item: ListItem): String {
|
||||||
return getString(R.string.shared_string_live) +
|
val duration = System.currentTimeMillis() / 1000 - item.lastUpdated
|
||||||
": ${OsmandFormatter.getFormattedDuration(app, getListItemLiveTime(item))}"
|
var formattedTime = OsmandFormatter.getFormattedDuration(app, duration)
|
||||||
|
return if (duration > 48 * 60 * 60) {
|
||||||
|
// TODO make constant
|
||||||
|
val day = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
|
||||||
|
formattedTime = day.format(Date(item.lastUpdated * 1000.toLong()))
|
||||||
|
getString(R.string.last_response) + ": $formattedTime"
|
||||||
|
} else {
|
||||||
|
getString(R.string.last_response) + ": $formattedTime " + getString(R.string.time_ago)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getListItemLiveTime(item: ListItem): Long = (System.currentTimeMillis() / 1000) - item.created
|
|
||||||
|
|
||||||
private fun showPopupMenu(holder: ChatViewHolder, chatId: Long) {
|
private fun showPopupMenu(holder: ChatViewHolder, chatId: Long) {
|
||||||
val ctx = holder.itemView.context
|
val ctx = holder.itemView.context
|
||||||
|
|
|
@ -577,23 +577,25 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.stopSharingDescr?.apply {
|
holder.stopSharingDescr?.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = getStopSharingVisibility(expiresIn)
|
||||||
text = "${getText(R.string.stop_at)}:"
|
text = "${getText(R.string.stop_at)}:"
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.stopSharingFirstPart?.apply {
|
holder.stopSharingFirstPart?.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = getStopSharingVisibility(expiresIn)
|
||||||
text = OsmandFormatter.getFormattedTime(expiresIn)
|
text = OsmandFormatter.getFormattedTime(expiresIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.stopSharingSecondPart?.apply {
|
holder.stopSharingSecondPart?.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = getStopSharingVisibility(expiresIn)
|
||||||
text = "(${getString(R.string.in_time,
|
text = "(${getString(R.string.in_time,
|
||||||
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true))})"
|
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true))})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getStopSharingVisibility(expiresIn: Long) = if (expiresIn > 0) View.VISIBLE else View.INVISIBLE
|
||||||
|
|
||||||
private fun removeItem(chat: TdApi.Chat) {
|
private fun removeItem(chat: TdApi.Chat) {
|
||||||
chats.remove(chat)
|
chats.remove(chat)
|
||||||
if (chats.isEmpty()) {
|
if (chats.isEmpty()) {
|
||||||
|
|
|
@ -12,16 +12,24 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import net.osmand.Location
|
||||||
|
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.TelegramLocationProvider.TelegramLocationListener
|
||||||
|
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
||||||
import net.osmand.telegram.helpers.ShareLocationHelper
|
import net.osmand.telegram.helpers.ShareLocationHelper
|
||||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
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.UiUtils
|
||||||
|
import net.osmand.util.MapUtils
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SetTimeDialogFragment : DialogFragment() {
|
class SetTimeDialogFragment : DialogFragment(), TelegramLocationListener, TelegramCompassListener {
|
||||||
|
|
||||||
private val app: TelegramApplication
|
private val app: TelegramApplication
|
||||||
get() = activity?.application as TelegramApplication
|
get() = activity?.application as TelegramApplication
|
||||||
|
@ -29,6 +37,7 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
private val telegramHelper get() = app.telegramHelper
|
private val telegramHelper get() = app.telegramHelper
|
||||||
private val settings get() = app.settings
|
private val settings get() = app.settings
|
||||||
|
|
||||||
|
private lateinit var locationViewCache: UiUtils.UpdateLocationViewCache
|
||||||
private val adapter = SetTimeListAdapter()
|
private val adapter = SetTimeListAdapter()
|
||||||
|
|
||||||
private lateinit var timeForAllTitle: TextView
|
private lateinit var timeForAllTitle: TextView
|
||||||
|
@ -36,6 +45,10 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
private val chatLivePeriods = HashMap<Long, Long>()
|
private val chatLivePeriods = HashMap<Long, Long>()
|
||||||
|
|
||||||
|
private var location: Location? = null
|
||||||
|
private var heading: Float? = null
|
||||||
|
private var locationUiUpdateAllowed: Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar)
|
setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar)
|
||||||
|
@ -66,6 +79,12 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
view.findViewById<RecyclerView>(R.id.recycler_view).apply {
|
view.findViewById<RecyclerView>(R.id.recycler_view).apply {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
adapter = this@SetTimeDialogFragment.adapter
|
adapter = this@SetTimeDialogFragment.adapter
|
||||||
|
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
super.onScrollStateChanged(recyclerView, newState)
|
||||||
|
locationUiUpdateAllowed = newState == RecyclerView.SCROLL_STATE_IDLE
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById<TextView>(R.id.secondary_btn).apply {
|
view.findViewById<TextView>(R.id.secondary_btn).apply {
|
||||||
|
@ -98,9 +117,16 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
locationViewCache = app.uiUtils.getUpdateLocationViewCache()
|
||||||
|
startLocationUpdate()
|
||||||
updateList()
|
updateList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
stopLocationUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
val chats = mutableListOf<Long>()
|
val chats = mutableListOf<Long>()
|
||||||
|
@ -111,6 +137,47 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
outState.putLongArray(CHATS_KEY, chats.toLongArray())
|
outState.putLongArray(CHATS_KEY, chats.toLongArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateLocation(location: Location?) {
|
||||||
|
val loc = this.location
|
||||||
|
val newLocation = loc == null && location != null
|
||||||
|
val locationChanged = loc != null && location != null
|
||||||
|
&& loc.latitude != location.latitude
|
||||||
|
&& loc.longitude != location.longitude
|
||||||
|
if (newLocation || locationChanged) {
|
||||||
|
this.location = location
|
||||||
|
updateLocationUi()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateCompassValue(value: Float) {
|
||||||
|
// 99 in next line used to one-time initialize arrows (with reference vs. fixed-north direction)
|
||||||
|
// on non-compass devices
|
||||||
|
val lastHeading = heading ?: 99f
|
||||||
|
heading = value
|
||||||
|
if (Math.abs(MapUtils.degreesDiff(lastHeading.toDouble(), value.toDouble())) > 5) {
|
||||||
|
updateLocationUi()
|
||||||
|
} else {
|
||||||
|
heading = lastHeading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLocationUpdate() {
|
||||||
|
app.locationProvider.addLocationListener(this)
|
||||||
|
app.locationProvider.addCompassListener(this)
|
||||||
|
updateLocationUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLocationUpdate() {
|
||||||
|
app.locationProvider.removeLocationListener(this)
|
||||||
|
app.locationProvider.removeCompassListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateLocationUi() {
|
||||||
|
if (locationUiUpdateAllowed) {
|
||||||
|
app.runInUIThread { adapter.notifyDataSetChanged() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun readFromBundle(bundle: Bundle?) {
|
private fun readFromBundle(bundle: Bundle?) {
|
||||||
chatLivePeriods.clear()
|
chatLivePeriods.clear()
|
||||||
bundle?.getLongArray(CHATS_KEY)?.also {
|
bundle?.getLongArray(CHATS_KEY)?.also {
|
||||||
|
@ -216,7 +283,35 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
TelegramUiHelper.setupPhoto(app, holder.icon, chat.photo?.small?.local?.path, placeholderId, false)
|
TelegramUiHelper.setupPhoto(app, holder.icon, chat.photo?.small?.local?.path, placeholderId, false)
|
||||||
holder.title?.text = chat.title
|
holder.title?.text = chat.title
|
||||||
|
|
||||||
|
if (telegramHelper.isGroup(chat)) {
|
||||||
|
holder.locationViewContainer?.visibility = View.GONE
|
||||||
|
holder.description?.visibility = View.VISIBLE
|
||||||
|
holder.description?.text = getString(R.string.shared_string_group)
|
||||||
|
} else {
|
||||||
|
val message = telegramHelper.getChatMessages(chat.id).firstOrNull()
|
||||||
|
val content = message?.content
|
||||||
|
if (message != null && content is TdApi.MessageLocation && (location != null && content.location != null)) {
|
||||||
|
val lastUpdated = telegramHelper.getLastUpdatedTime(message)
|
||||||
|
holder.description?.visibility = View.VISIBLE
|
||||||
|
holder.description?.text = getListItemLiveTimeDescr(lastUpdated)
|
||||||
|
|
||||||
|
holder.locationViewContainer?.visibility = View.VISIBLE
|
||||||
|
locationViewCache.outdatedLocation = System.currentTimeMillis() / 1000 -
|
||||||
|
lastUpdated > settings.staleLocTime
|
||||||
|
|
||||||
|
app.uiUtils.updateLocationView(
|
||||||
|
holder.directionIcon,
|
||||||
|
holder.distanceText,
|
||||||
|
LatLon(content.location.latitude, content.location.longitude),
|
||||||
|
locationViewCache
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
holder.locationViewContainer?.visibility = View.GONE
|
||||||
holder.description?.visibility = View.INVISIBLE
|
holder.description?.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
holder.textInArea?.apply {
|
holder.textInArea?.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
chatLivePeriods[chat.id]?.also { text = formatLivePeriod(it) }
|
chatLivePeriods[chat.id]?.also { text = formatLivePeriod(it) }
|
||||||
|
@ -229,9 +324,22 @@ class SetTimeDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
override fun getItemCount() = chats.size
|
override fun getItemCount() = chats.size
|
||||||
|
|
||||||
|
private fun getListItemLiveTimeDescr(lastUpdated: Int): String {
|
||||||
|
val duration = System.currentTimeMillis() / 1000 - lastUpdated
|
||||||
|
var formattedTime = OsmandFormatter.getFormattedDuration(app, duration)
|
||||||
|
if (duration > 48 * 60 * 60) {
|
||||||
|
// TODO make constant
|
||||||
|
formattedTime = Date(lastUpdated * 1000.toLong()).toString();
|
||||||
|
}
|
||||||
|
return "$formattedTime " + getString(R.string.time_ago)
|
||||||
|
}
|
||||||
|
|
||||||
inner class ChatViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
inner class ChatViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
val icon: ImageView? = view.findViewById(R.id.icon)
|
val icon: ImageView? = view.findViewById(R.id.icon)
|
||||||
val title: TextView? = view.findViewById(R.id.title)
|
val title: TextView? = view.findViewById(R.id.title)
|
||||||
|
val directionIcon: ImageView? = view.findViewById(R.id.direction_icon)
|
||||||
|
val distanceText: TextView? = view.findViewById(R.id.distance_text)
|
||||||
|
val locationViewContainer: View? = view.findViewById(R.id.location_view_container)
|
||||||
val description: TextView? = view.findViewById(R.id.description)
|
val description: TextView? = view.findViewById(R.id.description)
|
||||||
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
||||||
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||||
|
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
OsmAnd/res/drawable-hdpi/map_pin_user_stale_location_day.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
OsmAnd/res/drawable-hdpi/map_pin_user_stale_location_night.png
Normal file
After Width: | Height: | Size: 5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
OsmAnd/res/drawable-mdpi/map_pin_user_stale_location_day.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
OsmAnd/res/drawable-mdpi/map_pin_user_stale_location_night.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3 KiB |
BIN
OsmAnd/res/drawable-xhdpi/map_pin_user_stale_location_day.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
OsmAnd/res/drawable-xhdpi/map_pin_user_stale_location_night.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
OsmAnd/res/drawable-xxhdpi/map_pin_user_stale_location_day.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
OsmAnd/res/drawable-xxhdpi/map_pin_user_stale_location_night.png
Normal file
After Width: | Height: | Size: 11 KiB |