Telegram - added location expire feature

This commit is contained in:
crimean 2018-06-16 14:55:14 +03:00
parent 35b2ce93fe
commit c3ca853451
7 changed files with 111 additions and 18 deletions

View file

@ -173,7 +173,10 @@ class MainActivity : AppCompatActivity(), TelegramListener {
} }
override fun onTelegramUserChanged(user: TdApi.User) { 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) { override fun onTelegramError(code: Int, message: String) {

View file

@ -79,6 +79,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
if (isUsedByMyLocation(usedBy)) { if (isUsedByMyLocation(usedBy)) {
initLocationUpdates() initLocationUpdates()
} }
if (isUsedByUsersLocations(usedBy)) {
app.telegramHelper.startLiveMessagesUpdates()
}
val locationNotification = app.notificationHelper.locationNotification val locationNotification = app.notificationHelper.locationNotification
val notification = app.notificationHelper.buildNotification(locationNotification) val notification = app.notificationHelper.buildNotification(locationNotification)
@ -100,6 +103,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
val app = app() val app = app()
app.telegramHelper.stopLiveMessagesUpdates()
app.telegramHelper.incomingMessagesListener = null app.telegramHelper.incomingMessagesListener = null
app.telegramService = 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<TdApi.Message, Void, Void?>() { private class ShowMessagesTask(private val app: TelegramApplication, private val chatTitle: String) : AsyncTask<TdApi.Message, Void, Void?>() {
override fun doInBackground(vararg messages: TdApi.Message): Void? { override fun doInBackground(vararg messages: TdApi.Message): Void? {
for (message in messages) { for (message in messages) {
app.showLocationHelper.showLocationOnMap(chatTitle, message) app.showLocationHelper.showLocationOnMap(message)
} }
return null return null
} }
} }
private class UpdateMessagesTask(private val app: TelegramApplication) : AsyncTask<Void, Void, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
app.showLocationHelper.updateLocationsOnMap()
return null
}
}
companion object { companion object {
const val USED_BY_MY_LOCATION: Int = 1 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 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 { fun isOffIntervalDepended(usedBy: Int): Boolean {
return isUsedByMyLocation(usedBy) return isUsedByMyLocation(usedBy)
} }

View file

@ -17,7 +17,10 @@ class TelegramSettings(private val app: TelegramApplication) {
private const val SPEED_CONSTANTS_KEY = "speed_constants" 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_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<String> = emptySet() private var shareLocationChats: Set<String> = emptySet()
@ -27,6 +30,7 @@ class TelegramSettings(private val app: TelegramApplication) {
var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR
var sendMyLocationInterval = SEND_MY_LOCATION_INTERVAL_DEFAULT var sendMyLocationInterval = SEND_MY_LOCATION_INTERVAL_DEFAULT
var userLocationExpireTime = USER_LOCATION_EXPIRE_TIME_DEFAULT
init { init {
read() read()
@ -130,5 +134,6 @@ class TelegramSettings(private val app: TelegramApplication) {
speedConstants = SpeedConstants.valueOf(prefs.getString(SPEED_CONSTANTS_KEY, SpeedConstants.KILOMETERS_PER_HOUR.name)) 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) 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)
} }
} }

View file

@ -50,7 +50,8 @@ class OsmandAidlHelper(private val app: Application) {
companion object { companion object {
private const val OSMAND_FREE_PACKAGE_NAME = "net.osmand" private const val OSMAND_FREE_PACKAGE_NAME = "net.osmand"
private const val OSMAND_PLUS_PACKAGE_NAME = "net.osmand.plus" 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 private var mIOsmAndAidlInterface: IOsmAndAidlInterface? = null

View file

@ -24,13 +24,30 @@ class ShowLocationHelper(private val app: TelegramApplication) {
private set private set
fun setupMapLayer() { fun setupMapLayer() {
execOsmandApi {
osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null)
} }
}
fun showLocationOnMap(chatTitle: String, message: TdApi.Message) { fun updateLocationsOnMap() {
if (osmandHelper.isOsmandConnected()) { 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 val content = message.content
if (content is TdApi.MessageLocation) { if (chatTitle != null && content is TdApi.MessageLocation) {
var userName = "" var userName = ""
var photoUri: Uri? = null var photoUri: Uri? = null
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
@ -45,7 +62,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
val photoPath = telegramHelper.getUserPhotoPath(user) val photoPath = telegramHelper.getUserPhotoPath(user)
if (!TextUtils.isEmpty(photoPath)) { if (!TextUtils.isEmpty(photoPath)) {
photoUri = AndroidUtils.getUriForFile(app, File(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()) { if (userName.isEmpty()) {
@ -59,22 +76,20 @@ class ShowLocationHelper(private val app: TelegramApplication) {
osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName,
chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null, params) chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null, params)
} }
} else if (osmandHelper.isOsmandBound()) {
osmandHelper.connectOsmand()
} }
} }
fun showChatMessages(chatTitle: String) { fun showChatMessages(chatTitle: String) {
if (osmandHelper.isOsmandConnected()) { execOsmandApi {
val messages = telegramHelper.getChatMessages(chatTitle) val messages = telegramHelper.getChatMessages(chatTitle)
for (message in messages) { for (message in messages) {
showLocationOnMap(chatTitle, message) showLocationOnMap(message)
} }
} }
} }
fun hideChatMessages(chatTitle: String) { fun hideChatMessages(chatTitle: String) {
if (osmandHelper.isOsmandConnected()) { execOsmandApi {
val messages = telegramHelper.getChatMessages(chatTitle) val messages = telegramHelper.getChatMessages(chatTitle)
for (message in messages) { for (message in messages) {
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
@ -98,4 +113,12 @@ class ShowLocationHelper(private val app: TelegramApplication) {
app.stopUserLocationService() app.stopUserLocationService()
} }
} }
private fun execOsmandApi(action: (() -> Unit)) {
if (osmandHelper.isOsmandConnected()) {
action.invoke()
} else if (osmandHelper.isOsmandBound()) {
osmandHelper.connectOsmand()
}
}
} }

View file

@ -10,6 +10,9 @@ import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState
import java.io.File import java.io.File
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
class TelegramHelper private constructor() { class TelegramHelper private constructor() {
@ -19,6 +22,7 @@ class TelegramHelper private constructor() {
private const val CHATS_LIMIT = 100 private const val CHATS_LIMIT = 100
private const val CHAT_LIVE_USERS_LIMIT = 100 private const val CHAT_LIVE_USERS_LIMIT = 100
private const val IGNORED_ERROR_CODE = 406 private const val IGNORED_ERROR_CODE = 406
private const val UPDATE_LIVE_MESSAGES_INTERVAL_SEC = 30L
private var helper: TelegramHelper? = null private var helper: TelegramHelper? = null
@ -66,6 +70,8 @@ class TelegramHelper private constructor() {
private val defaultHandler = DefaultHandler() private val defaultHandler = DefaultHandler()
private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler() private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler()
private var updateLiveMessagesExecutor: ScheduledExecutorService? = null
var listener: TelegramListener? = null var listener: TelegramListener? = null
var incomingMessagesListener: TelegramIncomingMessagesListener? = null var incomingMessagesListener: TelegramIncomingMessagesListener? = null
@ -98,6 +104,15 @@ class TelegramHelper private constructor() {
return users[id] 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<TdApi.Message> { fun getChatMessages(chatTitle: String): List<TdApi.Message> {
val res = mutableListOf<TdApi.Message>() val res = mutableListOf<TdApi.Message>()
for (message in usersLiveMessages.values) { for (message in usersLiveMessages.values) {
@ -109,6 +124,10 @@ class TelegramHelper private constructor() {
return res return res
} }
fun getMessages(): List<TdApi.Message> {
return usersLiveMessages.values.toList()
}
private fun updateChatTitles() { private fun updateChatTitles() {
chatTitles.clear() chatTitles.clear()
for (chatEntry in chats.entries) { for (chatEntry in chats.entries) {
@ -148,6 +167,7 @@ class TelegramHelper private constructor() {
interface TelegramIncomingMessagesListener { interface TelegramIncomingMessagesListener {
fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message)
fun updateLocationMessages()
} }
interface TelegramAuthorizationRequestListener { interface TelegramAuthorizationRequestListener {
@ -217,7 +237,7 @@ class TelegramHelper private constructor() {
return client != null && haveAuthorization return client != null && haveAuthorization
} }
fun getUserPhotoPath(user: TdApi.User):String? { fun getUserPhotoPath(user: TdApi.User): String? {
return if (hasLocalUserPhoto(user)) { return if (hasLocalUserPhoto(user)) {
user.profilePhoto?.small?.local?.path user.profilePhoto?.small?.local?.path
} else { } 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 { 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) {
@ -326,7 +361,7 @@ class TelegramHelper private constructor() {
TdApi.Messages.CONSTRUCTOR -> { TdApi.Messages.CONSTRUCTOR -> {
val messages = (obj as TdApi.Messages).messages val messages = (obj as TdApi.Messages).messages
for (message in messages) { for (message in messages) {
if (!message.isOutgoing) { if (!message.isOutgoing && message.content is TdApi.MessageLocation) {
usersLiveMessages[message.id] = message usersLiveMessages[message.id] = message
} }
} }

View file

@ -95,6 +95,11 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
return (int) (r * tb.getDensity()); 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 @Override
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { 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 ex = (int) point.x;
int ey = (int) point.y; int ey = (int) point.y;
final int rp = getRadiusPoi(tb); 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; int radius = rp * 3 / 2;
for (AMapPoint p : aidlLayer.getPoints()) { for (AMapPoint p : aidlLayer.getPoints()) {
ALatLon position = p.getLocation(); ALatLon position = p.getLocation();
if (position != null) { if (position != null) {
compare = hasBitmap(p) ? bitmapRadius : radius;
int x = (int) tb.getPixXFromLatLon(position.getLatitude(), position.getLongitude()); int x = (int) tb.getPixXFromLatLon(position.getLatitude(), position.getLongitude());
int y = (int) tb.getPixYFromLatLon(position.getLatitude(), position.getLongitude()); int y = (int) tb.getPixYFromLatLon(position.getLatitude(), position.getLongitude());
if (Math.abs(x - ex) <= compare && Math.abs(y - ey) <= compare) { if (Math.abs(x - ex) <= compare && Math.abs(y - ey) <= compare) {
compare = radius;
points.add(p); points.add(p);
} }
} }