diff --git a/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt b/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt index a3236a22fd..5bc51cf9ab 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt @@ -173,7 +173,10 @@ class MainActivity : AppCompatActivity(), TelegramListener { } override fun onTelegramUserChanged(user: TdApi.User) { - + val message = telegramHelper.getUserMessage(user) + if (message != null) { + app.showLocationHelper.showLocationOnMap(message) + } } override fun onTelegramError(code: Int, message: String) { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt index 21767c1680..8d406cf53a 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt @@ -79,6 +79,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis if (isUsedByMyLocation(usedBy)) { initLocationUpdates() } + if (isUsedByUsersLocations(usedBy)) { + app.telegramHelper.startLiveMessagesUpdates() + } val locationNotification = app.notificationHelper.locationNotification val notification = app.notificationHelper.buildNotification(locationNotification) @@ -100,6 +103,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis override fun onDestroy() { super.onDestroy() val app = app() + app.telegramHelper.stopLiveMessagesUpdates() app.telegramHelper.incomingMessagesListener = null app.telegramService = null @@ -204,16 +208,28 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis } } + override fun updateLocationMessages() { + UpdateMessagesTask(app()).executeOnExecutor(executor) + } + private class ShowMessagesTask(private val app: TelegramApplication, private val chatTitle: String) : AsyncTask() { override fun doInBackground(vararg messages: TdApi.Message): Void? { for (message in messages) { - app.showLocationHelper.showLocationOnMap(chatTitle, message) + app.showLocationHelper.showLocationOnMap(message) } return null } } + private class UpdateMessagesTask(private val app: TelegramApplication) : AsyncTask() { + + override fun doInBackground(vararg params: Void?): Void? { + app.showLocationHelper.updateLocationsOnMap() + return null + } + } + companion object { const val USED_BY_MY_LOCATION: Int = 1 @@ -242,6 +258,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis return (usedBy and USED_BY_MY_LOCATION) > 0 } + fun isUsedByUsersLocations(usedBy: Int): Boolean { + return (usedBy and USED_BY_USERS_LOCATIONS) > 0 + } + fun isOffIntervalDepended(usedBy: Int): Boolean { return isUsedByMyLocation(usedBy) } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index d3de2d4fa2..a20fcd08dd 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -17,7 +17,10 @@ class TelegramSettings(private val app: TelegramApplication) { private const val SPEED_CONSTANTS_KEY = "speed_constants" private const val SEND_MY_LOCATION_INTERVAL_KEY = "send_my_location_interval" - private const val SEND_MY_LOCATION_INTERVAL_DEFAULT = 5000L + private const val SEND_MY_LOCATION_INTERVAL_DEFAULT = 5L * 1000 // 5 seconds + + private const val USER_LOCATION_EXPIRE_TIME_KEY = "user_location_expire_time" + private const val USER_LOCATION_EXPIRE_TIME_DEFAULT = 15L * 60 * 1000 // 15 minutes } private var shareLocationChats: Set = emptySet() @@ -27,6 +30,7 @@ class TelegramSettings(private val app: TelegramApplication) { var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR var sendMyLocationInterval = SEND_MY_LOCATION_INTERVAL_DEFAULT + var userLocationExpireTime = USER_LOCATION_EXPIRE_TIME_DEFAULT init { read() @@ -130,5 +134,6 @@ class TelegramSettings(private val app: TelegramApplication) { speedConstants = SpeedConstants.valueOf(prefs.getString(SPEED_CONSTANTS_KEY, SpeedConstants.KILOMETERS_PER_HOUR.name)) sendMyLocationInterval = prefs.getLong(SEND_MY_LOCATION_INTERVAL_KEY, SEND_MY_LOCATION_INTERVAL_DEFAULT) + userLocationExpireTime = prefs.getLong(USER_LOCATION_EXPIRE_TIME_KEY, USER_LOCATION_EXPIRE_TIME_DEFAULT) } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt index ff7869b5df..8025520dbb 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt @@ -50,7 +50,8 @@ class OsmandAidlHelper(private val app: Application) { companion object { private const val OSMAND_FREE_PACKAGE_NAME = "net.osmand" private const val OSMAND_PLUS_PACKAGE_NAME = "net.osmand.plus" - private var OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME + var OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME + private set } private var mIOsmAndAidlInterface: IOsmAndAidlInterface? = null diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index 07cc5a1423..a7cf97d251 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -24,13 +24,30 @@ class ShowLocationHelper(private val app: TelegramApplication) { private set fun setupMapLayer() { - osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) + execOsmandApi { + osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) + } } - fun showLocationOnMap(chatTitle: String, message: TdApi.Message) { - if (osmandHelper.isOsmandConnected()) { + fun updateLocationsOnMap() { + execOsmandApi { + val messages = telegramHelper.getMessages() + for (message in messages) { + val chatTitle = telegramHelper.getChat(message.chatId)?.title + val date = Math.max(message.date, message.editDate) * 1000L + val expired = System.currentTimeMillis() - date > app.settings.userLocationExpireTime + if (chatTitle != null && message.content is TdApi.MessageLocation && expired) { + osmandHelper.removeMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}") + } + } + } + } + + fun showLocationOnMap(message: TdApi.Message) { + execOsmandApi { + val chatTitle = telegramHelper.getChat(message.chatId)?.title val content = message.content - if (content is TdApi.MessageLocation) { + if (chatTitle != null && content is TdApi.MessageLocation) { var userName = "" var photoUri: Uri? = null val user = telegramHelper.getUser(message.senderUserId) @@ -45,7 +62,7 @@ class ShowLocationHelper(private val app: TelegramApplication) { val photoPath = telegramHelper.getUserPhotoPath(user) if (!TextUtils.isEmpty(photoPath)) { photoUri = AndroidUtils.getUriForFile(app, File(photoPath)) - app.grantUriPermission("net.osmand.plus", photoUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + app.grantUriPermission(OsmandAidlHelper.OSMAND_PACKAGE_NAME, photoUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) } } if (userName.isEmpty()) { @@ -59,22 +76,20 @@ class ShowLocationHelper(private val app: TelegramApplication) { osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null, params) } - } else if (osmandHelper.isOsmandBound()) { - osmandHelper.connectOsmand() } } fun showChatMessages(chatTitle: String) { - if (osmandHelper.isOsmandConnected()) { + execOsmandApi { val messages = telegramHelper.getChatMessages(chatTitle) for (message in messages) { - showLocationOnMap(chatTitle, message) + showLocationOnMap(message) } } } fun hideChatMessages(chatTitle: String) { - if (osmandHelper.isOsmandConnected()) { + execOsmandApi { val messages = telegramHelper.getChatMessages(chatTitle) for (message in messages) { val user = telegramHelper.getUser(message.senderUserId) @@ -98,4 +113,12 @@ class ShowLocationHelper(private val app: TelegramApplication) { app.stopUserLocationService() } } + + private fun execOsmandApi(action: (() -> Unit)) { + if (osmandHelper.isOsmandConnected()) { + action.invoke() + } else if (osmandHelper.isOsmandBound()) { + osmandHelper.connectOsmand() + } + } } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index e8fe5a737f..11f150ebe2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -10,6 +10,9 @@ import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState import java.io.File import java.util.* import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit class TelegramHelper private constructor() { @@ -19,6 +22,7 @@ class TelegramHelper private constructor() { private const val CHATS_LIMIT = 100 private const val CHAT_LIVE_USERS_LIMIT = 100 private const val IGNORED_ERROR_CODE = 406 + private const val UPDATE_LIVE_MESSAGES_INTERVAL_SEC = 30L private var helper: TelegramHelper? = null @@ -66,6 +70,8 @@ class TelegramHelper private constructor() { private val defaultHandler = DefaultHandler() private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler() + private var updateLiveMessagesExecutor: ScheduledExecutorService? = null + var listener: TelegramListener? = null var incomingMessagesListener: TelegramIncomingMessagesListener? = null @@ -98,6 +104,15 @@ class TelegramHelper private constructor() { return users[id] } + fun getUserMessage(user: TdApi.User): TdApi.Message? { + for (message in usersLiveMessages.values) { + if (message.senderUserId == user.id) { + return message + } + } + return null + } + fun getChatMessages(chatTitle: String): List { val res = mutableListOf() for (message in usersLiveMessages.values) { @@ -109,6 +124,10 @@ class TelegramHelper private constructor() { return res } + fun getMessages(): List { + return usersLiveMessages.values.toList() + } + private fun updateChatTitles() { chatTitles.clear() for (chatEntry in chats.entries) { @@ -148,6 +167,7 @@ class TelegramHelper private constructor() { interface TelegramIncomingMessagesListener { fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) + fun updateLocationMessages() } interface TelegramAuthorizationRequestListener { @@ -217,7 +237,7 @@ class TelegramHelper private constructor() { return client != null && haveAuthorization } - fun getUserPhotoPath(user: TdApi.User):String? { + fun getUserPhotoPath(user: TdApi.User): String? { return if (hasLocalUserPhoto(user)) { user.profilePhoto?.small?.local?.path } else { @@ -228,6 +248,21 @@ class TelegramHelper private constructor() { } } + fun startLiveMessagesUpdates() { + stopLiveMessagesUpdates() + + val updateLiveMessagesExecutor = Executors.newSingleThreadScheduledExecutor() + this.updateLiveMessagesExecutor = updateLiveMessagesExecutor + updateLiveMessagesExecutor.scheduleWithFixedDelay({ + incomingMessagesListener?.updateLocationMessages() + }, UPDATE_LIVE_MESSAGES_INTERVAL_SEC, UPDATE_LIVE_MESSAGES_INTERVAL_SEC, TimeUnit.SECONDS); + } + + fun stopLiveMessagesUpdates() { + updateLiveMessagesExecutor?.shutdown() + updateLiveMessagesExecutor?.awaitTermination(1, TimeUnit.MINUTES); + } + private fun hasLocalUserPhoto(user: TdApi.User): Boolean { val localPhoto = user.profilePhoto?.small?.local return if (localPhoto != null) { @@ -326,7 +361,7 @@ class TelegramHelper private constructor() { TdApi.Messages.CONSTRUCTOR -> { val messages = (obj as TdApi.Messages).messages for (message in messages) { - if (!message.isOutgoing) { + if (!message.isOutgoing && message.content is TdApi.MessageLocation) { usersLiveMessages[message.id] = message } } diff --git a/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java b/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java index 4883765c6b..1d2d34bdec 100644 --- a/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java @@ -95,6 +95,11 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider return (int) (r * tb.getDensity()); } + private boolean hasBitmap(AMapPoint point) { + String imageUriStr = point.getParams().get(AMapPoint.POINT_IMAGE_URI_PARAM); + return !TextUtils.isEmpty(imageUriStr) && pointImages.containsKey(imageUriStr); + } + @Override public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { } @@ -208,15 +213,16 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider int ex = (int) point.x; int ey = (int) point.y; final int rp = getRadiusPoi(tb); - int compare = rp; + final int bitmapRadius = (int) (POINT_IMAGE_SIZE_PX / tb.getDensity()); + int compare; int radius = rp * 3 / 2; for (AMapPoint p : aidlLayer.getPoints()) { ALatLon position = p.getLocation(); if (position != null) { + compare = hasBitmap(p) ? bitmapRadius : radius; int x = (int) tb.getPixXFromLatLon(position.getLatitude(), position.getLongitude()); int y = (int) tb.getPixYFromLatLon(position.getLatitude(), position.getLongitude()); if (Math.abs(x - ex) <= compare && Math.abs(y - ey) <= compare) { - compare = radius; points.add(p); } }