Merge pull request #6153 from osmandapp/RefactorTelegram

Move sharing settings to shareInfo
This commit is contained in:
Alexey 2018-10-12 13:16:50 +03:00 committed by GitHub
commit 519827262c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 312 additions and 230 deletions

View file

@ -82,7 +82,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
fun stopSharingLocation() {
settings.stopSharingLocationToChats()
shareLocationHelper.stopSharingLocation()
telegramHelper.stopSendingLiveLocationMessages()
telegramHelper.stopSendingLiveLocationMessages(settings.getChatsShareInfo())
}
fun isOsmAndInstalled() = AndroidUtils.isAppInstalled(this, settings.appToConnectPackage)

View file

@ -13,18 +13,25 @@ import android.os.*
import android.util.Log
import android.widget.Toast
import net.osmand.PlatformUtil
import net.osmand.telegram.helpers.TelegramHelper.TelegramOutgoingMessagesListener
import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import net.osmand.telegram.utils.AndroidUtils
import org.drinkless.td.libcore.telegram.TdApi
import java.util.*
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener {
private const val UPDATE_LIVE_MESSAGES_INTERVAL_MS = 10000L // 10 sec
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener,
TelegramOutgoingMessagesListener {
private fun app() = application as TelegramApplication
private val binder = LocationServiceBinder()
private var shouldCleanupResources: Boolean = false
private var updateShareInfoHandler: Handler? = null
private var mHandlerThread = HandlerThread("SharingServiceThread")
var handler: Handler? = null
private set
var usedBy = 0
@ -43,6 +50,12 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
class LocationServiceBinder : Binder()
override fun onCreate() {
super.onCreate()
mHandlerThread.start()
updateShareInfoHandler = Handler(mHandlerThread.looper)
}
override fun onBind(intent: Intent): IBinder? {
return binder
}
@ -77,9 +90,11 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app.telegramService = this
app.telegramHelper.addIncomingMessagesListener(this)
app.telegramHelper.addOutgoingMessagesListener(this)
if (isUsedByMyLocation(usedBy)) {
initLocationUpdates()
startShareInfoUpdates()
}
if (isUsedByUsersLocations(usedBy)) {
app.telegramHelper.startLiveMessagesUpdates(app.settings.sendMyLocInterval)
@ -107,7 +122,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
val app = app()
app.telegramHelper.stopLiveMessagesUpdates()
app.telegramHelper.removeIncomingMessagesListener(this)
app.telegramHelper.removeOutgoingMessagesListener(this)
app.telegramService = null
mHandlerThread.quit()
usedBy = 0
@ -159,6 +176,15 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
}
}
private fun startShareInfoUpdates() {
updateShareInfoHandler?.postDelayed({
if (isUsedByMyLocation(usedBy)) {
app().shareLocationHelper.updateSendLiveMessages()
startShareInfoUpdates()
}
}, UPDATE_LIVE_MESSAGES_INTERVAL_MS)
}
@SuppressLint("MissingPermission")
private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? {
val app = app()
@ -260,6 +286,16 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app().showLocationHelper.startUpdateMessagesTask()
}
override fun onUpdateMessages(messages: List<TdApi.Message>) {
messages.forEach {
app().settings.updateShareInfo(it)
}
}
override fun onDeleteMessages(chatId: Long, messages: List<Long>) {
app().settings.onDeleteLiveMessages(chatId, messages)
}
companion object {
const val USED_BY_MY_LOCATION: Int = 1

View file

@ -3,12 +3,19 @@ package net.osmand.telegram
import android.content.Context
import android.support.annotation.DrawableRes
import android.support.annotation.StringRes
import net.osmand.data.LatLon
import net.osmand.telegram.helpers.OsmandAidlHelper
import net.osmand.telegram.helpers.TelegramHelper
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.OsmandFormatter
import net.osmand.telegram.utils.OsmandFormatter.MetricsConstants
import net.osmand.telegram.utils.OsmandFormatter.SpeedConstants
import org.drinkless.td.libcore.telegram.TdApi
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
val ADDITIONAL_ACTIVE_TIME_VALUES_SEC = listOf(15 * 60L, 30 * 60L, 60 * 60L, 180 * 60L)
private val SEND_MY_LOC_VALUES_SEC =
listOf(1L, 2L, 3L, 5L, 10L, 15L, 30L, 60L, 90L, 2 * 60L, 3 * 60L, 5 * 60L)
@ -26,7 +33,6 @@ private val LOC_HISTORY_VALUES_SEC = listOf(
12 * 60 * 60L,
24 * 60 * 60L
)
private val MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC = listOf(15 * 60L, 30 * 60L, 60 * 60L, 180 * 60L)
private const val SEND_MY_LOC_DEFAULT_INDEX = 6
private const val STALE_LOC_DEFAULT_INDEX = 4
@ -54,14 +60,11 @@ private const val TITLES_REPLACED_WITH_IDS = "changed_to_chat_id"
private const val LIVE_NOW_SORT_TYPE_KEY = "live_now_sort_type"
private const val SHARE_CHATS_INFO_KEY = "share_chats_info"
class TelegramSettings(private val app: TelegramApplication) {
private var chatLivePeriods = mutableMapOf<Long, Long>()
private var chatShareLocStartSec = mutableMapOf<Long, Long>()
private var chatShareAddActiveTime = mutableMapOf<Long, Long>()
private var shareLocationChats: Set<Long> = emptySet()
private var shareChatsInfo = mutableMapOf<Long, ShareChatInfo>()
private var hiddenOnMapChats: Set<Long> = emptySet()
var shareDevicesIds = mutableMapOf<String, String>()
@ -86,32 +89,20 @@ class TelegramSettings(private val app: TelegramApplication) {
read()
}
fun hasAnyChatToShareLocation() = shareLocationChats.isNotEmpty()
fun hasAnyChatToShareLocation() = shareChatsInfo.isNotEmpty()
fun isSharingLocationToChat(chatId: Long) = shareLocationChats.contains(chatId)
fun isSharingLocationToChat(chatId: Long) = shareChatsInfo.contains(chatId)
fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats())
fun isShowingChatOnMap(chatId: Long) = !hiddenOnMapChats.contains(chatId)
fun removeNonexistingChats(presentChatIds: List<Long>) {
val shareLocationChats = shareLocationChats.toMutableList()
shareLocationChats.intersect(presentChatIds)
this.shareLocationChats = shareLocationChats.toHashSet()
val hiddenChats = hiddenOnMapChats.toMutableList()
hiddenChats.intersect(presentChatIds)
hiddenOnMapChats = hiddenChats.toHashSet()
chatLivePeriods = chatLivePeriods.filter { (key, _) ->
presentChatIds.contains(key)
}.toMutableMap()
chatShareAddActiveTime = chatShareAddActiveTime.filter { (key, _) ->
presentChatIds.contains(key)
}.toMutableMap()
chatShareLocStartSec = chatShareLocStartSec.filter { (key, _) ->
shareChatsInfo = shareChatsInfo.filter { (key, _) ->
presentChatIds.contains(key)
}.toMutableMap()
}
@ -120,25 +111,31 @@ class TelegramSettings(private val app: TelegramApplication) {
chatId: Long,
share: Boolean,
livePeriod: Long = DEFAULT_VISIBLE_TIME_SECONDS,
addActiveTime: Long = MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC[0]
addActiveTime: Long = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
) {
val shareLocationChats = shareLocationChats.toMutableList()
if (share) {
val lp: Long = when {
livePeriod < TelegramHelper.MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC -> TelegramHelper.MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()
else -> livePeriod
}
chatLivePeriods[chatId] = lp
chatShareLocStartSec[chatId] = (System.currentTimeMillis() / 1000)
chatShareAddActiveTime[chatId] = addActiveTime
shareLocationChats.add(chatId)
var shareChatInfo = shareChatsInfo[chatId]
if (shareChatInfo == null) {
shareChatInfo = ShareChatInfo()
}
val currentTime = System.currentTimeMillis() / 1000
shareChatInfo.start = currentTime
if (shareChatInfo.livePeriod == -1L) {
shareChatInfo.livePeriod = lp
}
shareChatInfo.userSetLivePeriod = lp
shareChatInfo.userSetLivePeriodStart = currentTime
shareChatInfo.currentMessageLimit = currentTime +
Math.min(lp, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
shareChatInfo.additionalActiveTime = addActiveTime
shareChatsInfo[chatId] = shareChatInfo
} else {
shareLocationChats.remove(chatId)
chatLivePeriods.remove(chatId)
chatShareLocStartSec.remove(chatId)
chatShareAddActiveTime.remove(chatId)
shareChatsInfo.remove(chatId)
}
this.shareLocationChats = shareLocationChats.toHashSet()
}
fun updateShareDevicesIds(list: List<DeviceBot>) {
@ -147,55 +144,22 @@ class TelegramSettings(private val app: TelegramApplication) {
shareDevicesIds[it.externalId] = it.deviceName
}
}
fun getChatLivePeriod(chatId: Long) = chatLivePeriods[chatId]
fun getChatAddActiveTime(chatId: Long) = chatShareAddActiveTime[chatId] ?: MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC[0]
fun getChatLivePeriod(chatId: Long) = shareChatsInfo[chatId]?.livePeriod
fun getChatNextAddActiveTime(chatId: Long): Long {
return if (chatShareAddActiveTime.containsKey(chatId)) {
var index = MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC.indexOf(chatShareAddActiveTime[chatId])
if (MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC.lastIndex > index) {
MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC[++index]
} else {
MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC[index]
}
} else {
MESSAGE_ADD_ACTIVE_TIME_VALUES_SEC[0]
}
}
fun getChatLivePeriods(): Map<Long, Long> {
return chatLivePeriods.filter {
getChatLiveMessageExpireTime(it.key) > 0
}
}
fun getChatShareLocStartSec(chatId: Long) = chatShareLocStartSec[chatId]
fun getChatsShareInfo() = shareChatsInfo
fun getChatLiveMessageExpireTime(chatId: Long): Long {
val startTime = getChatShareLocStartSec(chatId)
val livePeriod = getChatLivePeriod(chatId)
return if (startTime != null && livePeriod != null) {
livePeriod - ((System.currentTimeMillis() / 1000) - startTime)
val shareInfo = shareChatsInfo[chatId]
return if (shareInfo != null) {
shareInfo.userSetLivePeriod - ((System.currentTimeMillis() / 1000) - shareInfo.start)
} else {
0
}
}
fun updateChatShareLocStartSec(chatId: Long, startTime: Long) {
chatShareLocStartSec[chatId] = startTime
}
fun updateChatAddActiveTime(chatId: Long, newTime: Long) {
chatShareAddActiveTime[chatId] = newTime
}
fun stopSharingLocationToChats() {
this.shareLocationChats = emptySet()
this.chatLivePeriods.clear()
this.chatShareLocStartSec.clear()
this.chatShareAddActiveTime.clear()
shareChatsInfo.clear()
}
fun showChatOnMap(chatId: Long, show: Boolean) {
@ -208,7 +172,7 @@ class TelegramSettings(private val app: TelegramApplication) {
hiddenOnMapChats = hiddenChats.toHashSet()
}
fun getShareLocationChats() = ArrayList(shareLocationChats)
fun getShareLocationChats() = shareChatsInfo.keys
fun getShowOnMapChats() = getLiveNowChats().minus(hiddenOnMapChats)
@ -225,17 +189,27 @@ class TelegramSettings(private val app: TelegramApplication) {
app.osmandAidlHelper.reconnectOsmand()
}
fun updateShareInfo(message: TdApi.Message) {
val shareChatInfo = shareChatsInfo[message.chatId]
val content = message.content
if (shareChatInfo != null && content is TdApi.MessageLocation) {
shareChatInfo.currentMessageId = message.id
shareChatInfo.lastSuccessfulLocation = LatLon(content.location.latitude, content.location.longitude)
shareChatInfo.lastSuccessfulSendTime = Math.max(message.editDate, message.date).toLong()
}
}
fun onDeleteLiveMessages(chatId: Long, messages: List<Long>) {
val currentMessageId = shareChatsInfo[chatId]?.currentMessageId
if (messages.contains(currentMessageId)) {
shareChatsInfo[chatId]?.currentMessageId = -1
}
}
fun save() {
val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE)
val edit = prefs.edit()
val shareLocationChatsSet = mutableSetOf<String>()
val shareLocationChats = ArrayList(shareLocationChats)
for (chatId in shareLocationChats) {
shareLocationChatsSet.add(chatId.toString())
}
edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet)
val hiddenChatsSet = mutableSetOf<String>()
val hiddenChats = ArrayList(hiddenOnMapChats)
for (chatId in hiddenChats) {
@ -256,19 +230,30 @@ class TelegramSettings(private val app: TelegramApplication) {
edit.putString(LIVE_NOW_SORT_TYPE_KEY, liveNowSortType.name)
try {
val jArray = JSONArray()
shareChatsInfo.forEach { (chatId, chatInfo) ->
val obj = JSONObject()
obj.put(ShareChatInfo.CHAT_ID_KEY, chatId)
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.USER_SET_LIVE_PERIOD_KEY, chatInfo.userSetLivePeriod)
obj.put(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY, chatInfo.userSetLivePeriodStart)
jArray.put(obj)
}
edit.putString(SHARE_CHATS_INFO_KEY, jArray.toString())
} catch (e: JSONException) {
e.printStackTrace()
}
edit.apply()
}
fun read() {
val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE)
val shareLocationChats = mutableSetOf<Long>()
val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf())
for (chatId in shareLocationChatsSet) {
shareLocationChats.add(chatId.toLong())
}
this.shareLocationChats = shareLocationChats
val hiddenChats = mutableSetOf<Long>()
val hiddenChatsSet = prefs.getStringSet(HIDDEN_ON_MAP_CHATS_KEY, mutableSetOf())
for (chatId in hiddenChatsSet) {
@ -283,6 +268,12 @@ class TelegramSettings(private val app: TelegramApplication) {
prefs.getString(SPEED_CONSTANTS_KEY, SpeedConstants.KILOMETERS_PER_HOUR.name)
)
try {
parseShareChatsInfo(JSONArray(prefs.getString(SHARE_CHATS_INFO_KEY, "")))
} catch (e: JSONException) {
e.printStackTrace()
}
val sendMyLocDef = SEND_MY_LOC_VALUES_SEC[SEND_MY_LOC_DEFAULT_INDEX]
sendMyLocInterval = prefs.getLong(SEND_MY_LOC_INTERVAL_KEY, sendMyLocDef)
val staleLocDef = STALE_LOC_VALUES_SEC[STALE_LOC_DEFAULT_INDEX]
@ -299,6 +290,22 @@ class TelegramSettings(private val app: TelegramApplication) {
)
}
private fun parseShareChatsInfo(json: JSONArray) {
for (i in 0 until json.length()) {
val obj = json.getJSONObject(i)
val shareInfo = ShareChatInfo().apply {
chatId = obj.optLong(ShareChatInfo.CHAT_ID_KEY)
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)
userSetLivePeriod = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_KEY)
userSetLivePeriodStart = obj.optLong(ShareChatInfo.USER_SET_LIVE_PERIOD_START_KEY)
}
shareChatsInfo[shareInfo.chatId] = shareInfo
}
}
private fun getLiveNowChats() = app.telegramHelper.getMessagesByChatIds(locHistoryTime).keys
private fun updatePrefs() {
@ -454,4 +461,39 @@ class TelegramSettings(private val app: TelegramApplication) {
var externalId: String = ""
var data: String = ""
}
}
class ShareChatInfo {
var chatId = -1L
var start = -1L
var livePeriod = -1L
var currentMessageLimit = -1L
var currentMessageId = -1L
var userSetLivePeriod = -1L
var userSetLivePeriodStart = -1L
var lastSuccessfulLocation: LatLon? = null
var lastSuccessfulSendTime = -1L
var shouldDeletePreviousMessage = false
var additionalActiveTime = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
fun getNextAdditionalActiveTime(): Long {
var index = ADDITIONAL_ACTIVE_TIME_VALUES_SEC.indexOf(additionalActiveTime)
return if (ADDITIONAL_ACTIVE_TIME_VALUES_SEC.lastIndex > index) {
ADDITIONAL_ACTIVE_TIME_VALUES_SEC[++index]
} else {
ADDITIONAL_ACTIVE_TIME_VALUES_SEC[index]
}
}
companion object {
internal const val CHAT_ID_KEY = "chatId"
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 USER_SET_LIVE_PERIOD_KEY = "userSetLivePeriod"
internal const val USER_SET_LIVE_PERIOD_START_KEY = "userSetLivePeriodStart"
}
}
}

View file

@ -1,12 +1,17 @@
package net.osmand.telegram.helpers
import net.osmand.Location
import net.osmand.PlatformUtil
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import net.osmand.telegram.utils.AndroidNetworkUtils
private const val USER_SET_LIVE_PERIOD_DELAY_MS = 5000 // 5 sec
class ShareLocationHelper(private val app: TelegramApplication) {
private val log = PlatformUtil.getLog(ShareLocationHelper::class.java)
var sharingLocation: Boolean = false
private set
@ -38,37 +43,58 @@ class ShareLocationHelper(private val app: TelegramApplication) {
fun updateLocation(location: Location?) {
lastLocation = location
if (location != null && app.isInternetConnectionAvailable) {
val chatLivePeriods = app.settings.getChatLivePeriods()
val updatedLivePeriods = mutableMapOf<Long, Long>()
if (chatLivePeriods.isNotEmpty()) {
chatLivePeriods.forEach { (chatId, livePeriod) ->
if (livePeriod > TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC) {
val startTime = app.settings.getChatShareLocStartSec(chatId)
val currTime = (System.currentTimeMillis() / 1000)
if (startTime != null && startTime + TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC < currTime) {
app.settings.shareLocationToChat(chatId, true, livePeriod - TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)
} else if (startTime != null) {
updatedLivePeriods[chatId] = TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()
}
} else {
updatedLivePeriods[chatId] = livePeriod
}
}
if (location != null) {
val chatsShareInfo = app.settings.getChatsShareInfo()
if (chatsShareInfo.isNotEmpty()) {
val user = app.telegramHelper.getCurrentUser()
val sharingMode = app.settings.currentSharingMode
if (user != null && sharingMode == user.id.toString()) {
app.telegramHelper.sendLiveLocationMessage(updatedLivePeriods, location.latitude, location.longitude)
app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, location.latitude, location.longitude)
} else if (sharingMode.isNotEmpty()) {
val url = "https://live.osmand.net/device/$sharingMode/send?lat=${location.latitude}&lon=${location.longitude}"
AndroidNetworkUtils.sendRequestAsync(url, null)
}
lastLocationMessageSentTime = System.currentTimeMillis()
}
lastLocationMessageSentTime = System.currentTimeMillis()
}
refreshNotification()
}
fun updateSendLiveMessages() {
log.info("updateSendLiveMessages")
app.settings.getChatsShareInfo().forEach { chatId, shareInfo ->
val currentTime = System.currentTimeMillis() / 1000
when {
app.settings.getChatLiveMessageExpireTime(chatId) <= 0 ->
app.settings.shareLocationToChat(chatId, false)
currentTime > shareInfo.currentMessageLimit -> {
shareInfo.apply {
val newLivePeriod =
if (livePeriod > TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC) {
livePeriod - TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
} else {
livePeriod
}
livePeriod = newLivePeriod
shouldDeletePreviousMessage = true
currentMessageLimit = currentTime + Math.min(
newLivePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
}
}
shareInfo.userSetLivePeriod != shareInfo.livePeriod
&& (shareInfo.userSetLivePeriodStart + USER_SET_LIVE_PERIOD_DELAY_MS) > currentTime -> {
shareInfo.apply {
shouldDeletePreviousMessage = true
livePeriod = shareInfo.userSetLivePeriod
currentMessageLimit = currentTime + Math.min(
livePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()
)
}
}
}
}
}
fun startSharingLocation() {
if (!sharingLocation) {
sharingLocation = true

View file

@ -2,6 +2,7 @@ package net.osmand.telegram.helpers
import android.text.TextUtils
import net.osmand.PlatformUtil
import net.osmand.telegram.TelegramSettings
import net.osmand.telegram.helpers.TelegramHelper.TelegramAuthenticationParameterType.*
import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_DIR
import net.osmand.telegram.utils.GRAYSCALE_PHOTOS_EXT
@ -70,9 +71,6 @@ class TelegramHelper private constructor() {
private val chats = ConcurrentHashMap<Long, TdApi.Chat>()
private val chatList = TreeSet<OrderedChat>()
private val chatLiveMessages = ConcurrentHashMap<Long, TdApi.Message>()
private val pausedLiveChatIds = mutableListOf<Long>()
private val downloadChatFilesMap = ConcurrentHashMap<String, TdApi.Chat>()
private val downloadUserFilesMap = ConcurrentHashMap<String, TdApi.User>()
@ -105,6 +103,7 @@ class TelegramHelper private constructor() {
var listener: TelegramListener? = null
private val incomingMessagesListeners = HashSet<TelegramIncomingMessagesListener>()
private val outgoingMessagesListeners = HashSet<TelegramOutgoingMessagesListener>()
private val fullInfoUpdatesListeners = HashSet<FullInfoUpdatesListener>()
fun addIncomingMessagesListener(listener: TelegramIncomingMessagesListener) {
@ -115,6 +114,14 @@ class TelegramHelper private constructor() {
incomingMessagesListeners.remove(listener)
}
fun addOutgoingMessagesListener(listener: TelegramOutgoingMessagesListener) {
outgoingMessagesListeners.add(listener)
}
fun removeOutgoingMessagesListener(listener: TelegramOutgoingMessagesListener) {
outgoingMessagesListeners.remove(listener)
}
fun addFullInfoUpdatesListener(listener: FullInfoUpdatesListener) {
fullInfoUpdatesListeners.add(listener)
}
@ -147,8 +154,6 @@ class TelegramHelper private constructor() {
fun getMessages() = usersLocationMessages.values.toList()
fun getChatLiveMessages() = chatLiveMessages
fun getMessagesByChatIds(messageExpTime: Long): Map<Long, List<TdApi.Message>> {
val res = mutableMapOf<Long, MutableList<TdApi.Message>>()
for (message in usersLocationMessages.values) {
@ -238,6 +243,11 @@ class TelegramHelper private constructor() {
fun updateLocationMessages()
}
interface TelegramOutgoingMessagesListener {
fun onUpdateMessages(messages: List<TdApi.Message>)
fun onDeleteMessages(chatId: Long, messages: List<Long>)
}
interface FullInfoUpdatesListener {
fun onBasicGroupFullInfoUpdated(groupId: Int, info: TdApi.BasicGroupFullInfo)
fun onSupergroupFullInfoUpdated(groupId: Int, info: TdApi.SupergroupFullInfo)
@ -570,58 +580,34 @@ class TelegramHelper private constructor() {
* @latitude Latitude of the location
* @longitude Longitude of the location
*/
fun sendLiveLocationMessage(chatLivePeriods: Map<Long, Long>, latitude: Double, longitude: Double): Boolean {
fun sendLiveLocationMessage(chatsShareInfo:Map<Long, TelegramSettings.ShareChatInfo>, latitude: Double, longitude: Double): Boolean {
if (!requestingActiveLiveLocationMessages && haveAuthorization) {
if (needRefreshActiveLiveLocationMessages) {
getActiveLiveLocationMessages {
sendLiveLocationImpl(chatLivePeriods, latitude, longitude)
sendLiveLocationImpl(chatsShareInfo, latitude, longitude)
}
needRefreshActiveLiveLocationMessages = false
} else {
sendLiveLocationImpl(chatLivePeriods, latitude, longitude)
sendLiveLocationImpl(chatsShareInfo, latitude, longitude)
}
return true
}
return false
}
fun stopSendingLiveLocationToChat(chatId: Long) {
val msgId = chatLiveMessages[chatId]?.id
if (msgId != null && msgId != 0L) {
fun stopSendingLiveLocationToChat(chatId: Long, msgId: Long) {
if (msgId != -1L) {
client?.send(
TdApi.EditMessageLiveLocation(chatId, msgId, null, null),
liveLocationMessageUpdatesHandler
)
}
chatLiveMessages.remove(chatId)
needRefreshActiveLiveLocationMessages = true
}
fun pauseSendingLiveLocationToChat(chatId: Long) {
synchronized(pausedLiveChatIds) {
pausedLiveChatIds.add(chatId)
}
}
fun resumeSendingLiveLocationToChat(chatId: Long) {
synchronized(pausedLiveChatIds) {
pausedLiveChatIds.remove(chatId)
}
}
fun deleteLiveLocationMessage(chatId: Long) {
val msgId = chatLiveMessages[chatId]?.id
if (msgId != null && msgId != 0L) {
val array = LongArray(1)
array[0] = msgId
client?.send(TdApi.DeleteMessages(chatId, array, true), UpdatesHandler())
}
needRefreshActiveLiveLocationMessages = true
}
fun stopSendingLiveLocationMessages() {
chatLiveMessages.forEach { (chatId, _ )->
stopSendingLiveLocationToChat(chatId)
fun stopSendingLiveLocationMessages(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>) {
chatsShareInfo.forEach { (chatId, chatInfo) ->
stopSendingLiveLocationToChat(chatId, chatInfo.currentMessageId)
}
}
@ -638,11 +624,9 @@ class TelegramHelper private constructor() {
}
TdApi.Messages.CONSTRUCTOR -> {
val messages = (obj as TdApi.Messages).messages
chatLiveMessages.clear()
if (messages.isNotEmpty()) {
for (msg in messages) {
val chatId = msg.chatId
chatLiveMessages[chatId] = msg
outgoingMessagesListeners.forEach {
it.onUpdateMessages(messages.asList())
}
}
onComplete?.invoke()
@ -653,28 +637,63 @@ class TelegramHelper private constructor() {
}
}
private fun sendLiveLocationImpl(chatLivePeriods: Map<Long, Long>, latitude: Double, longitude: Double) {
val location = TdApi.Location(latitude, longitude)
chatLivePeriods.forEach { (chatId, livePeriod) ->
synchronized(pausedLiveChatIds) {
if (pausedLiveChatIds.contains(chatId)) {
return@forEach
private fun recreateLiveLocationMessage(chatId: Long, msgId: Long, content: TdApi.InputMessageLocation) {
if (msgId != -1L) {
log.info("recreateLiveLocationMessage - $msgId")
val array = LongArray(1)
array[0] = msgId
client?.send(TdApi.DeleteMessages(chatId, array, true)) { obj ->
when (obj.constructor) {
TdApi.Ok.CONSTRUCTOR -> {
sendNewLiveLocationMessage(chatId, content)
}
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(error.code, error.message)
}
}
}
}
val content = TdApi.InputMessageLocation(location, livePeriod.toInt())
val msgId = chatLiveMessages[chatId]?.id
if (msgId != null) {
if (msgId != 0L) {
}
needRefreshActiveLiveLocationMessages = true
}
private fun sendNewLiveLocationMessage(chatId: Long, content: TdApi.InputMessageLocation) {
needRefreshActiveLiveLocationMessages = true
log.info("sendNewLiveLocationMessage")
client?.send(
TdApi.SendMessage(chatId, 0, false, true, null, content),
liveLocationMessageUpdatesHandler
)
}
private fun sendLiveLocationImpl(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, latitude: Double, longitude: Double) {
val location = TdApi.Location(latitude, longitude)
chatsShareInfo.forEach { (chatId, shareInfo) ->
val livePeriod =
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
} else {
shareInfo.livePeriod.toInt()
}
val content = TdApi.InputMessageLocation(location, livePeriod)
val msgId = shareInfo.currentMessageId
if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousMessage) {
recreateLiveLocationMessage(chatId, msgId, content)
shareInfo.shouldDeletePreviousMessage = false
shareInfo.currentMessageId = -1
} else {
log.info("EditMessageLiveLocation - $msgId")
client?.send(
TdApi.EditMessageLiveLocation(chatId, msgId, null, location),
liveLocationMessageUpdatesHandler
)
}
} else {
client?.send(
TdApi.SendMessage(chatId, 0, false, true, null, content),
liveLocationMessageUpdatesHandler
)
sendNewLiveLocationMessage(chatId, content)
}
}
}
@ -742,12 +761,14 @@ class TelegramHelper private constructor() {
listener?.onSendLiveLocationError(error.code, error.message)
}
}
else -> {
TdApi.Message.CONSTRUCTOR -> {
if (obj is TdApi.Message) {
when (obj.sendingState?.constructor) {
TdApi.MessageSendingStateFailed.CONSTRUCTOR -> {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(-1, "Live location message ${obj.id} failed to send")
if (obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(-1, "Live location message ${obj.id} failed to send")
} else {
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(obj))
}
}
}
@ -1199,23 +1220,18 @@ class TelegramHelper private constructor() {
TdApi.UpdateMessageSendFailed.CONSTRUCTOR -> {
needRefreshActiveLiveLocationMessages = true
}
TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> {
val updateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded
val message = updateMessageSendSucceeded.message
chatLiveMessages[message.chatId] = message
}
TdApi.UpdateDeleteMessages.CONSTRUCTOR -> {
val updateDeleteMessages = obj as TdApi.UpdateDeleteMessages
if (updateDeleteMessages.isPermanent) {
val chatId = updateDeleteMessages.chatId
val deletedMessages = mutableListOf<TdApi.Message>()
for (messageId in updateDeleteMessages.messageIds) {
if (chatLiveMessages[chatId]?.id == messageId) {
chatLiveMessages.remove(chatId)
}
usersLocationMessages.remove(messageId)
?.also { deletedMessages.add(it) }
}
outgoingMessagesListeners.forEach {
it.onDeleteMessages(chatId, updateDeleteMessages.messageIds.toList())
}
if (deletedMessages.isNotEmpty()) {
incomingMessagesListeners.forEach {
it.onDeleteChatLocationMessages(chatId, deletedMessages)

View file

@ -15,6 +15,7 @@ import android.support.v7.widget.RecyclerView
import android.view.*
import android.view.animation.LinearInterpolator
import android.widget.*
import net.osmand.telegram.ADDITIONAL_ACTIVE_TIME_VALUES_SEC
import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.helpers.TelegramHelper
@ -300,24 +301,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
val updateAdapter = Handler()
updateAdapter.postDelayed({
if (updateEnable) {
if (sharingMode) {
updateExistingLiveMessages()
val iterator = adapter.chats.iterator()
while (iterator.hasNext()) {
val chat = iterator.next()
if (settings.getChatLiveMessageExpireTime(chat.id) <= 0) {
settings.shareLocationToChat(chat.id, false)
iterator.remove()
}
}
if (adapter.chats.isNotEmpty()) {
adapter.chats = sortAdapterItems(adapter.chats)
adapter.notifyDataSetChanged()
} else {
sharingMode = false
updateContent()
}
}
updateContent()
startHandler()
}
}, ADAPTER_UPDATE_INTERVAL_MIL)
@ -436,15 +420,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
for (chatId in chatList) {
val chat = telegramHelper.getChat(chatId)
if (chat != null) {
if (settings.isSharingLocationToChat(chatId)) {
if (sharingMode) {
val message = telegramHelper.getChatLiveMessages()[chat.id]
if (message != null) {
// settings.updateChatShareLocStartSec(chatId, message.date.toLong())
}
} else {
continue
}
if (!sharingMode && settings.isSharingLocationToChat(chatId)) {
continue
} else if (telegramHelper.isPrivateChat(chat)) {
if ((chat.type as TdApi.ChatTypePrivate).userId == currentUser?.id) {
continue
@ -460,20 +437,6 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
}
private fun updateExistingLiveMessages() {
telegramHelper.getChatLiveMessages().values.forEach {
if (settings.isSharingLocationToChat(it.chatId)
&& (settings.getChatShareLocStartSec(it.chatId) == null || settings.getChatLivePeriod(it.chatId) == null)) {
settings.shareLocationToChat(it.chatId, true, (it.content as TdApi.MessageLocation).livePeriod.toLong())
// settings.updateChatShareLocStartSec(it.chatId, it.date.toLong())
}
}
sharingMode = settings.hasAnyChatToShareLocation()
if (!shareLocationHelper.sharingLocation && sharingMode && AndroidUtils.isLocationPermissionAvailable(app)) {
shareLocationHelper.startSharingLocation()
}
}
private fun sortAdapterItems(list: MutableList<TdApi.Chat>): MutableList<TdApi.Chat> {
list.sortWith(Comparator<TdApi.Chat> { o1, o2 -> o1.title.compareTo(o2.title) })
return list
@ -516,6 +479,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
val lastItem = position == itemCount - 1
val placeholderId = if (telegramHelper.isGroup(chat)) R.drawable.img_group_picture else R.drawable.img_user_picture
val live = settings.isSharingLocationToChat(chat.id)
val shareInfo = settings.getChatsShareInfo()[chat.id]
TelegramUiHelper.setupPhoto(app, holder.icon, chat.photo?.small?.local?.path, placeholderId, false)
holder.title?.text = chat.title
@ -556,14 +520,17 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
isChecked = live
setOnCheckedChangeListener { _, isChecked ->
if (!isChecked) {
val currentMessageId = shareInfo?.currentMessageId
settings.shareLocationToChat(chat.id, false)
telegramHelper.stopSendingLiveLocationToChat(chat.id)
if (currentMessageId != null) {
telegramHelper.stopSendingLiveLocationToChat(chat.id, currentMessageId)
}
removeItem(chat)
}
}
}
val duration = settings.getChatLivePeriod(chat.id)
val duration = shareInfo?.userSetLivePeriod
if (duration != null && duration > 0) {
holder.descriptionDuration?.text = OsmandFormatter.getFormattedDuration(context!!, duration)
holder.description?.apply {
@ -575,18 +542,13 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
val expiresIn = settings.getChatLiveMessageExpireTime(chat.id)
holder.textInArea?.apply {
val time = shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
visibility = View.VISIBLE
text = "${getText(R.string.plus)} ${OsmandFormatter.getFormattedDuration(
context!!, settings.getChatAddActiveTime(chat.id))}"
text = "${getText(R.string.plus)} ${OsmandFormatter.getFormattedDuration(context!!, time)}"
setOnClickListener {
val chatNextAddTime = settings.getChatNextAddActiveTime(chat.id)
val newLivePeriod = settings.getChatLiveMessageExpireTime(chat.id) + settings.getChatAddActiveTime(chat.id)
settings.shareLocationToChat(chat.id, true, newLivePeriod, chatNextAddTime)
if (app.isInternetConnectionAvailable) {
telegramHelper.pauseSendingLiveLocationToChat(chat.id)
telegramHelper.deleteLiveLocationMessage(chat.id)
telegramHelper.resumeSendingLiveLocationToChat(chat.id)
}
val newLivePeriod = settings.getChatLiveMessageExpireTime(chat.id) + (shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0])
val nextAdditionalActiveTime = shareInfo?.getNextAdditionalActiveTime() ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[1]
settings.shareLocationToChat(chat.id, true, newLivePeriod, nextAdditionalActiveTime)
notifyItemChanged(position)
}
}