Merge pull request #6181 from osmandapp/TelegramStatus

Telegram status
This commit is contained in:
Alexander Sytnyk 2018-10-17 13:09:56 +03:00 committed by GitHub
commit e9eb7e6bf6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 702 additions and 179 deletions

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:layout_gravity="bottom">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/scroll_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_view_bottom_padding">
<android.support.v4.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_hideable="true"
app:behavior_peekHeight="@dimen/bottom_sheet_peek_height"
app:layout_behavior="@string/bottom_sheet_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/card_bg_color"
android:orientation="vertical">
<net.osmand.telegram.ui.views.TextViewEx
android:layout_width="match_parent"
android:layout_height="48dp"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/sharing_status"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size"
app:firstBaselineToTopHeight="28sp"
app:typeface="@string/font_roboto_medium" />
<LinearLayout
android:id="@+id/items_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/card_bg_color">
<include
layout="@layout/secondary_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/content_padding_half" />
</FrameLayout>
</FrameLayout>

View file

@ -50,10 +50,10 @@
android:id="@+id/text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/content_padding_standard"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:paddingLeft="@dimen/my_location_text_sides_margin"
android:layout_marginBottom="@dimen/content_padding_standard"
android:paddingRight="@dimen/my_location_text_sides_margin">
<LinearLayout
@ -157,6 +157,7 @@
android:gravity="center_vertical">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/status_title"
android:layout_width="0dp"
android:layout_height="@dimen/action_bar_height"
android:layout_weight="1"
@ -166,20 +167,18 @@
android:maxLines="1"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/my_location"
android:textColor="@color/app_bar_title_light"
android:textSize="@dimen/title_text_size"
app:typeface="@string/font_roboto_mono_bold" />
app:typeface="@string/font_roboto_mono_bold"
tools:text="@string/sharing_enabled" />
<ImageView
android:id="@+id/options_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingLeft="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
tools:src="@drawable/ic_action_other_menu"
tools:tint="@color/icon_light"
tools:visibility="visible" />
@ -198,8 +197,15 @@
android:id="@+id/stop_all_sharing_row"
android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height"
android:gravity="center_vertical">
<LinearLayout
android:id="@+id/stop_all_sharing_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
@ -207,23 +213,84 @@
android:id="@+id/stop_all_sharing_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginEnd="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/stop_sharing_all"
android:text="@string/shared_string_disable"
android:textColor="?attr/ctrl_active_color"
android:textSize="@dimen/descr_text_size"
android:textSize="@dimen/hint_text_size"
app:lastBaselineToBottomHeight="24dp"
app:typeface="@string/font_roboto_medium" />
<Switch
android:id="@+id/stop_all_sharing_switcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:background="@null"
android:clickable="false"
android:focusable="false"/>
android:focusable="false" />
</LinearLayout>
<View
android:id="@+id/appbar_divider2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/card_divider_color" />
<LinearLayout
android:id="@+id/sharing_status_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<ImageView
android:id="@+id/sharing_status_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:layout_marginTop="@dimen/content_padding_standard"
tools:src="@drawable/ic_action_live_now"
tools:tint="@color/ctrl_active_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<net.osmand.telegram.ui.views.TextViewEx
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shared_string_status"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:typeface="@string/font_roboto_regular" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/sharing_status_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/ctrl_active_color"
android:textSize="@dimen/hint_text_size"
app:lastBaselineToBottomHeight="@dimen/content_padding_standard"
app:typeface="@string/font_roboto_medium"
tools:text="@string/no_gps_connection" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
@ -231,11 +298,10 @@
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_height="match_parent">
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
@ -258,9 +324,9 @@
android:paddingRight="32dp"
android:text="@string/share_location"
android:textColor="@color/white"
app:typeface="@string/font_roboto_medium"
android:visibility="gone"
tools:visibility="visible"/>
app:typeface="@string/font_roboto_medium"
tools:visibility="visible" />
</FrameLayout>

View file

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:layout_marginTop="@dimen/content_padding_standard"
tools:src="@drawable/ic_action_wifi_off"
tools:tint="@color/icon_light" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="horizontal">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size"
app:firstBaselineToTopHeight="28sp"
app:typeface="@string/font_roboto_regular"
tools:text="@string/no_internet_connection" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/status_change_time"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="26sp"
app:typeface="@string/font_roboto_regular"
tools:text="14:30" />
</LinearLayout>
<LinearLayout
android:id="@+id/description_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/last_location_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="22dp"
app:lastBaselineToBottomHeight="20dp"
app:typeface="@string/font_roboto_regular"
tools:text="@string/last_available_location" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/last_location_line_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="22dp"
app:lastBaselineToBottomHeight="20dp"
app:typeface="@string/font_roboto_medium"
tools:text="14:24" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/re_send_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="@dimen/list_item_height"
android:paddingRight="@dimen/list_item_height"
android:text="@string/re_send_location"
android:textColor="?attr/ctrl_active_color"
android:textSize="@dimen/hint_text_size"
app:lastBaselineToBottomHeight="16dp"
app:typeface="@string/font_roboto_medium" />
<View
android:id="@+id/bottom_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="@dimen/list_item_height"
android:layout_marginStart="@dimen/list_item_height"
android:background="?attr/card_divider_color" />
</LinearLayout>

View file

@ -38,4 +38,7 @@
<color name="app_bar_title_light">#333333</color>
<color name="sharing_status_icon_error">#ee5622</color>
<color name="sharing_status_icon_success">#78cc5c</color>
</resources>

View file

@ -1,4 +1,15 @@
<resources>
<string name="re_send_location">Re-send location</string>
<string name="last_sent_location">Last sent location</string>
<string name="last_available_location">Last available location</string>
<string name="sharing_status">Sharing status</string>
<string name="sharing_enabled">Sharing: Enabled</string>
<string name="shared_string_status">Status</string>
<string name="no_gps_connection">No GPS connection</string>
<string name="sharing_success">Successfully sent and updated</string>
<string name="not_possible_to_send_to_chats">Not possible to send to chats:</string>
<string name="no_internet_connection">No internet connection</string>
<string name="shared_string_disable">Disable</string>
<string name="shared_string_save">Save</string>
<string name="add_device_descr">Enter your device id that you can find at https://live.osmand.net/device/ID</string>
<string name="device_id">Device id</string>

View file

@ -232,7 +232,6 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
}
override fun onLocationChanged(l: Location?) {
if (l != null) {
val location = convertLocation(l)
if (!isContinuous()) {
// unregister listener and wait next time
@ -253,7 +252,6 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app().shareLocationHelper.updateLocation(location)
}
}
}
override fun onProviderDisabled(provider: String) {
Toast.makeText(this, getString(R.string.location_service_no_gps_available), Toast.LENGTH_LONG).show()
@ -296,6 +294,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app().settings.onDeleteLiveMessages(chatId, messages)
}
override fun onSendLiveLocationError(code: Int, message: String) {
Log.d(PlatformUtil.TAG, "Send live location error: $code - $message")
}
companion object {
const val USED_BY_MY_LOCATION: Int = 1

View file

@ -1,8 +1,11 @@
package net.osmand.telegram
import android.content.Context
import android.support.annotation.ColorRes
import android.support.annotation.DrawableRes
import android.support.annotation.StringRes
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import net.osmand.data.LatLon
import net.osmand.telegram.helpers.OsmandAidlHelper
import net.osmand.telegram.helpers.TelegramHelper
@ -14,6 +17,8 @@ import org.drinkless.td.libcore.telegram.TdApi
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
val ADDITIONAL_ACTIVE_TIME_VALUES_SEC = listOf(15 * 60L, 30 * 60L, 60 * 60L, 180 * 60L)
@ -64,9 +69,11 @@ private const val SHARE_CHATS_INFO_KEY = "share_chats_info"
class TelegramSettings(private val app: TelegramApplication) {
private var shareChatsInfo = mutableMapOf<Long, ShareChatInfo>()
private var shareChatsInfo = ConcurrentHashMap<Long, ShareChatInfo>()
private var hiddenOnMapChats: Set<Long> = emptySet()
var sharingStatusChanges = ConcurrentLinkedQueue<SharingStatus>()
var shareDevicesIds = mutableMapOf<String, String>()
var currentSharingMode = ""
@ -91,7 +98,7 @@ class TelegramSettings(private val app: TelegramApplication) {
fun hasAnyChatToShareLocation() = shareChatsInfo.isNotEmpty()
fun isSharingLocationToChat(chatId: Long) = shareChatsInfo.contains(chatId)
fun isSharingLocationToChat(chatId: Long) = shareChatsInfo.containsKey(chatId)
fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats())
@ -102,9 +109,7 @@ class TelegramSettings(private val app: TelegramApplication) {
hiddenChats.intersect(presentChatIds)
hiddenOnMapChats = hiddenChats.toHashSet()
shareChatsInfo = shareChatsInfo.filter { (key, _) ->
presentChatIds.contains(key)
}.toMutableMap()
shareChatsInfo = ConcurrentHashMap(shareChatsInfo.filter { (key, _) -> presentChatIds.contains(key) })
}
fun shareLocationToChat(
@ -123,14 +128,14 @@ class TelegramSettings(private val app: TelegramApplication) {
shareChatInfo = ShareChatInfo()
}
val currentTime = System.currentTimeMillis() / 1000
shareChatInfo.chatId = chatId
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.currentMessageLimit = currentTime + Math.min(lp, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
shareChatInfo.additionalActiveTime = addActiveTime
shareChatsInfo[chatId] = shareChatInfo
} else {
@ -149,14 +154,7 @@ class TelegramSettings(private val app: TelegramApplication) {
fun getChatsShareInfo() = shareChatsInfo
fun getChatLiveMessageExpireTime(chatId: Long): Long {
val shareInfo = shareChatsInfo[chatId]
return if (shareInfo != null) {
shareInfo.userSetLivePeriod - ((System.currentTimeMillis() / 1000) - shareInfo.start)
} else {
0
}
}
fun getLastSuccessfulSendTime() = shareChatsInfo.values.maxBy { it.lastSuccessfulSendTimeMs }?.lastSuccessfulSendTimeMs ?: -1
fun stopSharingLocationToChats() {
shareChatsInfo.clear()
@ -193,9 +191,56 @@ class TelegramSettings(private val app: TelegramApplication) {
val shareChatInfo = shareChatsInfo[message.chatId]
val content = message.content
if (shareChatInfo != null && content is TdApi.MessageLocation) {
shareChatInfo.start = message.date.toLong()
shareChatInfo.currentMessageId = message.id
shareChatInfo.lastSuccessfulLocation = LatLon(content.location.latitude, content.location.longitude)
shareChatInfo.lastSuccessfulSendTime = Math.max(message.editDate, message.date).toLong()
shareChatInfo.lastSuccessfulSendTimeMs = Math.max(message.editDate, message.date) * 1000L
}
}
fun updateSharingStatusHistory() {
val newSharingStatus = SharingStatus().apply {
statusChangeTime = System.currentTimeMillis()
statusType = if (!app.isInternetConnectionAvailable) {
locationTime = getLastSuccessfulSendTime()
SharingStatusType.NO_INTERNET
} else if (app.shareLocationHelper.lastLocation == null) {
locationTime = app.shareLocationHelper.lastLocationMessageSentTime
SharingStatusType.NO_GPS
} else {
var sendChatsErrors = false
shareChatsInfo.forEach { _, shareInfo ->
if (shareInfo.hasSharingError) {
sendChatsErrors = true
locationTime = shareInfo.lastSuccessfulSendTimeMs
val title = app.telegramHelper.getChat(shareInfo.chatId)?.title
if (title != null) {
chatsTitles.add(title)
}
}
}
if (sendChatsErrors) {
SharingStatusType.NOT_POSSIBLE_TO_SENT_TO_CHATS
} else {
locationTime = getLastSuccessfulSendTime()
SharingStatusType.SUCCESSFULLY_SENT
}
}
}
if (sharingStatusChanges.isNotEmpty()) {
val lastSharingStatus = sharingStatusChanges.last()
if (lastSharingStatus.statusType != newSharingStatus.statusType) {
sharingStatusChanges.add(newSharingStatus)
} else {
lastSharingStatus.apply {
statusChangeTime = newSharingStatus.statusChangeTime
locationTime = newSharingStatus.locationTime
chatsTitles = newSharingStatus.chatsTitles
}
}
} else {
sharingStatusChanges.add(newSharingStatus)
}
}
@ -453,6 +498,43 @@ class TelegramSettings(private val app: TelegramApplication) {
fun isSortByGroup() = this == SORT_BY_GROUP
}
enum class SharingStatusType(
@DrawableRes val iconId: Int,
@ColorRes val iconColorRes: Int,
@StringRes val titleId: Int,
@StringRes val descriptionId: Int,
val canResendLocation: Boolean
) {
NO_INTERNET(
R.drawable.ic_action_wifi_off,
R.color.sharing_status_icon_error,
R.string.no_internet_connection,
R.string.last_sent_location,
true
),
SUCCESSFULLY_SENT(
R.drawable.ic_action_share_location,
R.color.sharing_status_icon_success,
R.string.sharing_success,
R.string.last_sent_location,
false
),
NOT_POSSIBLE_TO_SENT_TO_CHATS(
R.drawable.ic_action_message_send_error,
R.color.sharing_status_icon_error,
R.string.not_possible_to_send_to_chats,
R.string.last_sent_location,
true
),
NO_GPS(
R.drawable.ic_action_location_off,
R.color.sharing_status_icon_error,
R.string.no_gps_connection,
R.string.last_available_location,
false
);
}
class DeviceBot {
var id: Long = -1
var userId: Long = -1
@ -462,6 +544,30 @@ class TelegramSettings(private val app: TelegramApplication) {
var data: String = ""
}
class SharingStatus {
var locationTime: Long = -1
var statusChangeTime: Long = -1
var chatsTitles: MutableList<String> = mutableListOf()
lateinit var statusType: SharingStatusType
fun getDescription(app: TelegramApplication): CharSequence {
return if (statusType != SharingStatusType.NOT_POSSIBLE_TO_SENT_TO_CHATS || chatsTitles.isEmpty()) {
app.getString(statusType.titleId)
} else {
val spannableString = SpannableStringBuilder(app.getString(statusType.titleId))
val iterator = chatsTitles.iterator()
while (iterator.hasNext()) {
val chatTitle = iterator.next()
val start = spannableString.length
val newSpannable = if (iterator.hasNext()) " @$chatTitle," else " @$chatTitle."
spannableString.append(newSpannable)
spannableString.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), start, spannableString.length - 1, 0)
}
spannableString
}
}
}
class ShareChatInfo {
var chatId = -1L
@ -472,9 +578,10 @@ class TelegramSettings(private val app: TelegramApplication) {
var userSetLivePeriod = -1L
var userSetLivePeriodStart = -1L
var lastSuccessfulLocation: LatLon? = null
var lastSuccessfulSendTime = -1L
var lastSuccessfulSendTimeMs = -1L
var shouldDeletePreviousMessage = false
var additionalActiveTime = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
var hasSharingError = false
fun getNextAdditionalActiveTime(): Long {
var index = ADDITIONAL_ACTIVE_TIME_VALUES_SEC.indexOf(additionalActiveTime)
@ -485,6 +592,10 @@ class TelegramSettings(private val app: TelegramApplication) {
}
}
fun getChatLiveMessageExpireTime(): Long {
return userSetLivePeriod - ((System.currentTimeMillis() / 1000) - start)
}
companion object {
internal const val CHAT_ID_KEY = "chatId"

View file

@ -23,9 +23,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
var lastLocationMessageSentTime: Long = 0
private var lastTimeInMillis: Long = 0L
private var lastLocation: Location? = null
var lastLocation: Location? = null
set(value) {
if (lastTimeInMillis == 0L) {
lastTimeInMillis = System.currentTimeMillis()
@ -40,6 +38,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
field = value
}
private var lastTimeInMillis: Long = 0L
fun updateLocation(location: Location?) {
lastLocation = location
@ -54,19 +54,20 @@ class ShareLocationHelper(private val app: TelegramApplication) {
val url = "https://live.osmand.net/device/$sharingMode/send?lat=${location.latitude}&lon=${location.longitude}"
AndroidNetworkUtils.sendRequestAsync(url, null)
}
}
lastLocationMessageSentTime = System.currentTimeMillis()
}
}
app.settings.updateSharingStatusHistory()
refreshNotification()
}
fun updateSendLiveMessages() {
log.info("updateSendLiveMessages")
if (app.settings.hasAnyChatToShareLocation()) {
app.settings.getChatsShareInfo().forEach { chatId, shareInfo ->
val currentTime = System.currentTimeMillis() / 1000
when {
app.settings.getChatLiveMessageExpireTime(chatId) <= 0 ->
app.settings.shareLocationToChat(chatId, false)
shareInfo.getChatLiveMessageExpireTime() <= 0 -> app.settings.shareLocationToChat(chatId, false)
currentTime > shareInfo.currentMessageLimit -> {
shareInfo.apply {
val newLivePeriod =
@ -77,8 +78,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
livePeriod = newLivePeriod
shouldDeletePreviousMessage = true
currentMessageLimit = currentTime + Math.min(
newLivePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
currentMessageLimit = currentTime + Math.min(newLivePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
}
}
shareInfo.userSetLivePeriod != shareInfo.livePeriod
@ -86,13 +86,14 @@ class ShareLocationHelper(private val app: TelegramApplication) {
shareInfo.apply {
shouldDeletePreviousMessage = true
livePeriod = shareInfo.userSetLivePeriod
currentMessageLimit = currentTime + Math.min(
livePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong()
)
currentMessageLimit = currentTime + Math.min(livePeriod, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
}
}
}
}
} else {
stopSharingLocation()
}
}
fun startSharingLocation() {

View file

@ -28,6 +28,7 @@ class TelegramHelper private constructor() {
private val log = PlatformUtil.getLog(TelegramHelper::class.java)
private const val CHATS_LIMIT = 100
private const val IGNORED_ERROR_CODE = 406
private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5
private const val DEVICE_PREFIX = "Device: "
private const val LOCATION_PREFIX = "Location: "
@ -97,7 +98,6 @@ class TelegramHelper private constructor() {
private var haveAuthorization = false
private val defaultHandler = DefaultHandler()
private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler()
private var updateLiveMessagesExecutor: ScheduledExecutorService? = null
@ -234,7 +234,6 @@ class TelegramHelper private constructor() {
fun onTelegramChatChanged(chat: TdApi.Chat)
fun onTelegramUserChanged(user: TdApi.User)
fun onTelegramError(code: Int, message: String)
fun onSendLiveLocationError(code: Int, message: String)
}
interface TelegramIncomingMessagesListener {
@ -246,6 +245,7 @@ class TelegramHelper private constructor() {
interface TelegramOutgoingMessagesListener {
fun onUpdateMessages(messages: List<TdApi.Message>)
fun onDeleteMessages(chatId: Long, messages: List<Long>)
fun onSendLiveLocationError(code: Int, message: String)
}
interface FullInfoUpdatesListener {
@ -595,19 +595,19 @@ class TelegramHelper private constructor() {
return false
}
fun stopSendingLiveLocationToChat(chatId: Long, msgId: Long) {
if (msgId != -1L) {
fun stopSendingLiveLocationToChat(shareInfo: TelegramSettings.ShareChatInfo) {
if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) {
client?.send(
TdApi.EditMessageLiveLocation(chatId, msgId, null, null),
liveLocationMessageUpdatesHandler
)
TdApi.EditMessageLiveLocation(shareInfo.chatId, shareInfo.currentMessageId, null, null)) { obj ->
handleLiveLocationMessageUpdate(obj, shareInfo)
}
}
needRefreshActiveLiveLocationMessages = true
}
fun stopSendingLiveLocationMessages(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>) {
chatsShareInfo.forEach { (chatId, chatInfo) ->
stopSendingLiveLocationToChat(chatId, chatInfo.currentMessageId)
chatsShareInfo.forEach { (_, chatInfo) ->
stopSendingLiveLocationToChat(chatInfo)
}
}
@ -619,7 +619,9 @@ class TelegramHelper private constructor() {
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(error.code, error.message)
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(error.code, error.message)
}
}
}
TdApi.Messages.CONSTRUCTOR -> {
@ -631,27 +633,29 @@ class TelegramHelper private constructor() {
}
onComplete?.invoke()
}
else -> listener?.onSendLiveLocationError(-1, "Receive wrong response from TDLib: $obj")
else -> outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(-1, "Receive wrong response from TDLib: $obj")
}
}
requestingActiveLiveLocationMessages = false
}
}
private fun recreateLiveLocationMessage(chatId: Long, msgId: Long, content: TdApi.InputMessageLocation) {
if (msgId != -1L) {
log.info("recreateLiveLocationMessage - $msgId")
private fun recreateLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) {
if (shareInfo.currentMessageId != -1L && shareInfo.chatId != -1L) {
log.info("recreateLiveLocationMessage - $shareInfo.currentMessageId")
val array = LongArray(1)
array[0] = msgId
client?.send(TdApi.DeleteMessages(chatId, array, true)) { obj ->
array[0] = shareInfo.currentMessageId
client?.send(TdApi.DeleteMessages(shareInfo.chatId, array, true)) { obj ->
when (obj.constructor) {
TdApi.Ok.CONSTRUCTOR -> {
sendNewLiveLocationMessage(chatId, content)
}
TdApi.Ok.CONSTRUCTOR -> sendNewLiveLocationMessage(shareInfo, content)
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(error.code, error.message)
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(error.code, error.message)
}
}
}
}
@ -660,18 +664,21 @@ class TelegramHelper private constructor() {
needRefreshActiveLiveLocationMessages = true
}
private fun sendNewLiveLocationMessage(chatId: Long, content: TdApi.InputMessageLocation) {
private fun sendNewLiveLocationMessage(shareInfo: TelegramSettings.ShareChatInfo, content: TdApi.InputMessageLocation) {
needRefreshActiveLiveLocationMessages = true
log.info("sendNewLiveLocationMessage")
client?.send(
TdApi.SendMessage(chatId, 0, false, true, null, content),
liveLocationMessageUpdatesHandler
)
TdApi.SendMessage(shareInfo.chatId, 0, false, true, null, content)) { obj ->
handleLiveLocationMessageUpdate(obj, shareInfo)
}
}
private fun sendLiveLocationImpl(chatsShareInfo: Map<Long, TelegramSettings.ShareChatInfo>, latitude: Double, longitude: Double) {
val location = TdApi.Location(latitude, longitude)
chatsShareInfo.forEach { (chatId, shareInfo) ->
if (shareInfo.getChatLiveMessageExpireTime() <= 0) {
return@forEach
}
val livePeriod =
if (shareInfo.currentMessageLimit > (shareInfo.start + MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC)) {
MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC
@ -682,18 +689,51 @@ class TelegramHelper private constructor() {
val msgId = shareInfo.currentMessageId
if (msgId != -1L) {
if (shareInfo.shouldDeletePreviousMessage) {
recreateLiveLocationMessage(chatId, msgId, content)
recreateLiveLocationMessage(shareInfo, content)
shareInfo.shouldDeletePreviousMessage = false
shareInfo.currentMessageId = -1
} else {
log.info("EditMessageLiveLocation - $msgId")
client?.send(
TdApi.EditMessageLiveLocation(chatId, msgId, null, location),
liveLocationMessageUpdatesHandler
)
TdApi.EditMessageLiveLocation(chatId, msgId, null, location)) { obj ->
handleLiveLocationMessageUpdate(obj, shareInfo)
}
}
} else {
sendNewLiveLocationMessage(chatId, content)
sendNewLiveLocationMessage(shareInfo, content)
}
}
}
private fun handleLiveLocationMessageUpdate(obj: TdApi.Object, shareInfo: TelegramSettings.ShareChatInfo) {
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
needRefreshActiveLiveLocationMessages = true
if (error.code == MESSAGE_CANNOT_BE_EDITED_ERROR_CODE) {
shareInfo.shouldDeletePreviousMessage = true
} else if (error.code != IGNORED_ERROR_CODE) {
shareInfo.hasSharingError = true
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(error.code, error.message)
}
}
}
TdApi.Message.CONSTRUCTOR -> {
if (obj is TdApi.Message) {
if (obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) {
shareInfo.hasSharingError = true
needRefreshActiveLiveLocationMessages = true
outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(-1, "Live location message ${obj.id} failed to send")
}
} else {
shareInfo.hasSharingError = false
outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(obj))
}
}
}
}
}
}
@ -751,32 +791,6 @@ class TelegramHelper private constructor() {
}
}
private inner class LiveLocationMessageUpdatesHandler : ResultHandler {
override fun onResult(obj: TdApi.Object) {
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
if (error.code != IGNORED_ERROR_CODE) {
needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(error.code, error.message)
}
}
TdApi.Message.CONSTRUCTOR -> {
if (obj is TdApi.Message) {
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))
}
}
}
}
}
}
}
private fun onAuthorizationStateUpdated(authorizationState: AuthorizationState?, info: Boolean = false) {
val prevAuthState = getTelegramAuthorizationState()
if (authorizationState != null) {

View file

@ -170,8 +170,6 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
override fun onTelegramError(code: Int, message: String) {}
override fun onSendLiveLocationError(code: Int, message: String) {}
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
app.runInUIThread { updateList() }
}

View file

@ -275,14 +275,6 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
}
}
override fun onSendLiveLocationError(code: Int, message: String) {
log.error("Send live location error: $code - $message")
app.isInternetConnectionAvailable(true)
runOnUi {
listeners.forEach { it.get()?.onSendLiveLocationError(code, message) }
}
}
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
addGrayPhoto(chatId)
if (!app.showLocationHelper.showingLocation && settings.hasAnyChatToShowOnMap()) {

View file

@ -12,6 +12,8 @@ import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.*
import android.view.animation.LinearInterpolator
import android.widget.*
@ -57,6 +59,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
private lateinit var description: TextView
private lateinit var searchBox: FrameLayout
private lateinit var stopSharingSwitcher: Switch
private lateinit var sharingStatusDescription: TextView
private lateinit var startSharingBtn: View
private lateinit var searchBoxBg: GradientDrawable
@ -132,6 +135,16 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
AndroidUtils.addStatusBarPadding19v(context, this)
}
mainView.findViewById<TextView>(R.id.status_title).apply {
val sharingStatus = getString(R.string.sharing_enabled)
val spannable = SpannableString(sharingStatus)
spannable.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()),
sharingStatus.indexOf(" "), sharingStatus.length, 0)
text = spannable
}
mainView.findViewById<ImageView>(R.id.sharing_status_icon).setImageDrawable(app.uiUtils.getActiveIcon(R.drawable.ic_action_live_now))
textContainer = mainView.findViewById<LinearLayout>(R.id.text_container).apply {
if (Build.VERSION.SDK_INT >= 16) {
layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
@ -181,8 +194,16 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
}
mainView.findViewById<View>(R.id.sharing_status_container).setOnClickListener {
fragmentManager?.also { fm ->
SharingStatusBottomSheet.showInstance(fm, this)
}
}
stopSharingSwitcher = mainView.findViewById(R.id.stop_all_sharing_switcher)
sharingStatusDescription = mainView.findViewById(R.id.sharing_status_description)
startSharingBtn = mainView.findViewById<View>(R.id.start_sharing_btn).apply {
visibility = if (sharingMode) View.VISIBLE else View.GONE
setOnClickListener {
@ -227,6 +248,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
app.stopSharingLocation()
updateContent()
}
SharingStatusBottomSheet.SHARING_STATUS_REQUEST_CODE -> {
updateSharingStatus()
}
}
}
@ -269,9 +293,6 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
override fun onTelegramError(code: Int, message: String) {
}
override fun onSendLiveLocationError(code: Int, message: String) {
}
fun onPrimaryBtnClick() {
if (selectedChats.isNotEmpty()) {
val fm = fragmentManager ?: return
@ -394,6 +415,8 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
private fun updateContent() {
sharingMode = sharingMode && settings.hasAnyChatToShareLocation()
updateSharingStatus()
updateSharingMode()
updateList()
}
@ -409,10 +432,16 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
appBarScrollRange = -1
}
private fun updateSharingStatus() {
if (sharingMode && settings.sharingStatusChanges.isNotEmpty()) {
sharingStatusDescription.text = settings.sharingStatusChanges.last().getDescription(app)
}
}
private fun updateList() {
val chats: MutableList<TdApi.Chat> = mutableListOf()
val currentUser = telegramHelper.getCurrentUser()
val chatList = if (sharingMode && settings.hasAnyChatToShareLocation()) {
val chatList = if (sharingMode) {
settings.getShareLocationChats()
} else {
telegramHelper.getChatListIds()
@ -520,10 +549,9 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
isChecked = live
setOnCheckedChangeListener { _, isChecked ->
if (!isChecked) {
val currentMessageId = shareInfo?.currentMessageId
settings.shareLocationToChat(chat.id, false)
if (currentMessageId != null) {
telegramHelper.stopSendingLiveLocationToChat(chat.id, currentMessageId)
if (shareInfo != null) {
telegramHelper.stopSendingLiveLocationToChat(shareInfo)
}
removeItem(chat)
}
@ -539,14 +567,15 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
}
}
val expiresIn = settings.getChatLiveMessageExpireTime(chat.id)
val expiresIn = shareInfo?.getChatLiveMessageExpireTime() ?: 0
holder.textInArea?.apply {
val time = shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
visibility = View.VISIBLE
text = "${getText(R.string.plus)} ${OsmandFormatter.getFormattedDuration(context!!, time)}"
setOnClickListener {
val newLivePeriod = settings.getChatLiveMessageExpireTime(chat.id) + (shareInfo?.additionalActiveTime ?: ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0])
val expireTime = shareInfo?.getChatLiveMessageExpireTime() ?: 0
val newLivePeriod = expireTime + (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)

View file

@ -0,0 +1,100 @@
package net.osmand.telegram.ui
import android.os.Bundle
import android.support.design.widget.BottomSheetBehavior
import android.support.v4.app.DialogFragment
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.ui.views.BottomSheetDialog
import net.osmand.telegram.utils.OsmandFormatter
class SharingStatusBottomSheet : DialogFragment() {
private val app: TelegramApplication
get() = activity?.application as TelegramApplication
private val settings get() = app.settings
private val uiUtils get() = app.uiUtils
override fun onCreateDialog(savedInstanceState: Bundle?) = BottomSheetDialog(context!!)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val mainView = inflater.inflate(R.layout.bottom_sheet_sharing_status, container, false)
mainView.findViewById<View>(R.id.scroll_view_container).setOnClickListener { dismiss() }
BottomSheetBehavior.from(mainView.findViewById<View>(R.id.scroll_view))
.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
targetFragment?.also { target ->
target.onActivityResult(targetRequestCode, SHARING_STATUS_REQUEST_CODE, null)
}
dismiss()
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
})
val itemsCont = mainView.findViewById<ViewGroup>(R.id.items_container)
settings.sharingStatusChanges.reversed().forEach { sharingStatus ->
inflater.inflate(R.layout.item_with_three_text_lines, itemsCont, false).apply {
val sharingStatusType = sharingStatus.statusType
val time = sharingStatus.locationTime
findViewById<ImageView>(R.id.icon).setImageDrawable(uiUtils.getIcon(sharingStatusType.iconId, sharingStatusType.iconColorRes))
findViewById<TextView>(R.id.title).text = sharingStatus.getDescription(app)
findViewById<TextView>(R.id.status_change_time).text = OsmandFormatter.getFormattedTime(sharingStatus.statusChangeTime, false)
findViewById<TextView>(R.id.last_location_line).text = getString(sharingStatusType.descriptionId)
if (time > 0) {
findViewById<TextView>(R.id.last_location_line_time).text = OsmandFormatter.getFormattedTime(time, false)
} else {
findViewById<LinearLayout>(R.id.description_container).visibility = View.INVISIBLE
}
if (sharingStatusType.canResendLocation) {
findViewById<TextView>(R.id.re_send_location).apply {
setOnClickListener {
app.forceUpdateMyLocation()
dismiss()
}
}
} else {
findViewById<TextView>(R.id.re_send_location).visibility = View.GONE
}
itemsCont.addView(this)
}
}
mainView.findViewById<TextView>(R.id.secondary_btn).apply {
setText(R.string.shared_string_close)
setOnClickListener {
targetFragment?.also { target ->
target.onActivityResult(targetRequestCode, SHARING_STATUS_REQUEST_CODE, null)
}
dismiss()
}
}
return mainView
}
companion object {
const val SHARING_STATUS_REQUEST_CODE = 5
private const val TAG = "SharingStatusBottomSheet"
fun showInstance(fm: FragmentManager, target: Fragment): Boolean {
return try {
SharingStatusBottomSheet().apply {
setTargetFragment(target, SHARING_STATUS_REQUEST_CODE)
show(fm, TAG)
}
true
} catch (e: RuntimeException) {
false
}
}
}
}

View file

@ -62,9 +62,13 @@ object OsmandFormatter {
}
}
fun getFormattedTime(seconds: Long): String {
fun getFormattedTime(milliseconds: Long, useCurrentTime: Boolean = true): String {
val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis() + (seconds * 1000)
if (useCurrentTime) {
calendar.timeInMillis = System.currentTimeMillis() + milliseconds
} else {
calendar.timeInMillis = milliseconds
}
return if (isSameDay(calendar, Calendar.getInstance())) {
SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time)
} else {

View file

@ -107,6 +107,10 @@ class UiUtils(private val app: TelegramApplication) {
return getDrawable(id, if (isLightContent) R.color.ctrl_active_light else 0)
}
fun getActiveColor():Int {
return ContextCompat.getColor(app, if (isLightContent) R.color.ctrl_active_light else 0)
}
fun getIcon(@DrawableRes id: Int): Drawable? {
return getDrawable(id, 0)
}