Merge remote-tracking branch 'origin/master'

This commit is contained in:
Alex Sytnyk 2018-10-17 15:18:47 +03:00
commit 04f079381a
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:id="@+id/text_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/content_padding_standard"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="@dimen/my_location_text_sides_margin" android:paddingLeft="@dimen/my_location_text_sides_margin"
android:layout_marginBottom="@dimen/content_padding_standard"
android:paddingRight="@dimen/my_location_text_sides_margin"> android:paddingRight="@dimen/my_location_text_sides_margin">
<LinearLayout <LinearLayout
@ -157,6 +157,7 @@
android:gravity="center_vertical"> android:gravity="center_vertical">
<net.osmand.telegram.ui.views.TextViewEx <net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/status_title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="@dimen/action_bar_height" android:layout_height="@dimen/action_bar_height"
android:layout_weight="1" android:layout_weight="1"
@ -166,20 +167,18 @@
android:maxLines="1" android:maxLines="1"
android:paddingLeft="@dimen/content_padding_standard" android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard" android:paddingRight="@dimen/content_padding_standard"
android:text="@string/my_location"
android:textColor="@color/app_bar_title_light" android:textColor="@color/app_bar_title_light"
android:textSize="@dimen/title_text_size" 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 <ImageView
android:id="@+id/options_title" android:id="@+id/options_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
android:paddingLeft="@dimen/content_padding_half" android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_half" android:paddingRight="@dimen/content_padding_standard"
tools:src="@drawable/ic_action_other_menu" tools:src="@drawable/ic_action_other_menu"
tools:tint="@color/icon_light" tools:tint="@color/icon_light"
tools:visibility="visible" /> tools:visibility="visible" />
@ -198,32 +197,100 @@
android:id="@+id/stop_all_sharing_row" android:id="@+id/stop_all_sharing_row"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/action_bar_height" android:layout_height="@dimen/action_bar_height"
android:background="?attr/selectableItemBackground" android:gravity="center_vertical">
android:gravity="center_vertical"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<net.osmand.telegram.ui.views.TextViewEx <LinearLayout
android:id="@+id/stop_all_sharing_title" android:id="@+id/stop_all_sharing_container"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginEnd="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard"
android:layout_weight="1" android:layout_weight="1"
android:ellipsize="end" android:background="?attr/selectableItemBackground"
android:maxLines="1" android:orientation="horizontal"
android:text="@string/stop_sharing_all" android:paddingLeft="@dimen/content_padding_standard"
android:textColor="?attr/ctrl_active_color" android:paddingRight="@dimen/content_padding_standard">
android:textSize="@dimen/descr_text_size"
app:typeface="@string/font_roboto_medium" />
<Switch <net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/stop_all_sharing_switcher" android:id="@+id/stop_all_sharing_title"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@null" android:layout_gravity="bottom"
android:clickable="false" android:layout_marginEnd="@dimen/content_padding_standard"
android:focusable="false"/> android:layout_marginRight="@dimen/content_padding_standard"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/shared_string_disable"
android:textColor="?attr/ctrl_active_color"
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" />
</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> </LinearLayout>
@ -231,11 +298,10 @@
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
android:layout_width="match_parent" 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.support.v7.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
@ -258,9 +324,9 @@
android:paddingRight="32dp" android:paddingRight="32dp"
android:text="@string/share_location" android:text="@string/share_location"
android:textColor="@color/white" android:textColor="@color/white"
app:typeface="@string/font_roboto_medium"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> app:typeface="@string/font_roboto_medium"
tools:visibility="visible" />
</FrameLayout> </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="app_bar_title_light">#333333</color>
<color name="sharing_status_icon_error">#ee5622</color>
<color name="sharing_status_icon_success">#78cc5c</color>
</resources> </resources>

View file

@ -1,4 +1,15 @@
<resources> <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="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="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> <string name="device_id">Device id</string>

View file

@ -232,26 +232,24 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
} }
override fun onLocationChanged(l: Location?) { override fun onLocationChanged(l: Location?) {
if (l != null) { val location = convertLocation(l)
val location = convertLocation(l) if (!isContinuous()) {
if (!isContinuous()) { // unregister listener and wait next time
// unregister listener and wait next time val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager try {
try { locationManager.removeUpdates(this)
locationManager.removeUpdates(this) } catch (e: Throwable) {
} catch (e: Throwable) { Log.d(PlatformUtil.TAG, "Location service permission not granted") //$NON-NLS-1$
Log.d(PlatformUtil.TAG, "Location service permission not granted") //$NON-NLS-1$
}
val lock = getLock(this)
if (lock.isHeld) {
lock.release()
}
app().shareLocationHelper.updateLocation(location)
} else if (System.currentTimeMillis() - lastLocationSentTime > sendLocationInterval * 1000) {
lastLocationSentTime = System.currentTimeMillis()
app().shareLocationHelper.updateLocation(location)
} }
val lock = getLock(this)
if (lock.isHeld) {
lock.release()
}
app().shareLocationHelper.updateLocation(location)
} else if (System.currentTimeMillis() - lastLocationSentTime > sendLocationInterval * 1000) {
lastLocationSentTime = System.currentTimeMillis()
app().shareLocationHelper.updateLocation(location)
} }
} }
@ -296,6 +294,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app().settings.onDeleteLiveMessages(chatId, messages) app().settings.onDeleteLiveMessages(chatId, messages)
} }
override fun onSendLiveLocationError(code: Int, message: String) {
Log.d(PlatformUtil.TAG, "Send live location error: $code - $message")
}
companion object { companion object {
const val USED_BY_MY_LOCATION: Int = 1 const val USED_BY_MY_LOCATION: Int = 1

View file

@ -1,8 +1,11 @@
package net.osmand.telegram package net.osmand.telegram
import android.content.Context import android.content.Context
import android.support.annotation.ColorRes
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import android.support.annotation.StringRes import android.support.annotation.StringRes
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import net.osmand.data.LatLon import net.osmand.data.LatLon
import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.OsmandAidlHelper
import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.helpers.TelegramHelper
@ -14,6 +17,8 @@ import org.drinkless.td.libcore.telegram.TdApi
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject 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) 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) { class TelegramSettings(private val app: TelegramApplication) {
private var shareChatsInfo = mutableMapOf<Long, ShareChatInfo>() private var shareChatsInfo = ConcurrentHashMap<Long, ShareChatInfo>()
private var hiddenOnMapChats: Set<Long> = emptySet() private var hiddenOnMapChats: Set<Long> = emptySet()
var sharingStatusChanges = ConcurrentLinkedQueue<SharingStatus>()
var shareDevicesIds = mutableMapOf<String, String>() var shareDevicesIds = mutableMapOf<String, String>()
var currentSharingMode = "" var currentSharingMode = ""
@ -91,7 +98,7 @@ class TelegramSettings(private val app: TelegramApplication) {
fun hasAnyChatToShareLocation() = shareChatsInfo.isNotEmpty() fun hasAnyChatToShareLocation() = shareChatsInfo.isNotEmpty()
fun isSharingLocationToChat(chatId: Long) = shareChatsInfo.contains(chatId) fun isSharingLocationToChat(chatId: Long) = shareChatsInfo.containsKey(chatId)
fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats()) fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats())
@ -102,9 +109,7 @@ class TelegramSettings(private val app: TelegramApplication) {
hiddenChats.intersect(presentChatIds) hiddenChats.intersect(presentChatIds)
hiddenOnMapChats = hiddenChats.toHashSet() hiddenOnMapChats = hiddenChats.toHashSet()
shareChatsInfo = shareChatsInfo.filter { (key, _) -> shareChatsInfo = ConcurrentHashMap(shareChatsInfo.filter { (key, _) -> presentChatIds.contains(key) })
presentChatIds.contains(key)
}.toMutableMap()
} }
fun shareLocationToChat( fun shareLocationToChat(
@ -123,14 +128,14 @@ class TelegramSettings(private val app: TelegramApplication) {
shareChatInfo = ShareChatInfo() shareChatInfo = ShareChatInfo()
} }
val currentTime = System.currentTimeMillis() / 1000 val currentTime = System.currentTimeMillis() / 1000
shareChatInfo.chatId = chatId
shareChatInfo.start = currentTime shareChatInfo.start = currentTime
if (shareChatInfo.livePeriod == -1L) { if (shareChatInfo.livePeriod == -1L) {
shareChatInfo.livePeriod = lp shareChatInfo.livePeriod = lp
} }
shareChatInfo.userSetLivePeriod = lp shareChatInfo.userSetLivePeriod = lp
shareChatInfo.userSetLivePeriodStart = currentTime shareChatInfo.userSetLivePeriodStart = currentTime
shareChatInfo.currentMessageLimit = currentTime + shareChatInfo.currentMessageLimit = currentTime + Math.min(lp, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
Math.min(lp, TelegramHelper.MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC.toLong())
shareChatInfo.additionalActiveTime = addActiveTime shareChatInfo.additionalActiveTime = addActiveTime
shareChatsInfo[chatId] = shareChatInfo shareChatsInfo[chatId] = shareChatInfo
} else { } else {
@ -149,14 +154,7 @@ class TelegramSettings(private val app: TelegramApplication) {
fun getChatsShareInfo() = shareChatsInfo fun getChatsShareInfo() = shareChatsInfo
fun getChatLiveMessageExpireTime(chatId: Long): Long { fun getLastSuccessfulSendTime() = shareChatsInfo.values.maxBy { it.lastSuccessfulSendTimeMs }?.lastSuccessfulSendTimeMs ?: -1
val shareInfo = shareChatsInfo[chatId]
return if (shareInfo != null) {
shareInfo.userSetLivePeriod - ((System.currentTimeMillis() / 1000) - shareInfo.start)
} else {
0
}
}
fun stopSharingLocationToChats() { fun stopSharingLocationToChats() {
shareChatsInfo.clear() shareChatsInfo.clear()
@ -193,9 +191,56 @@ class TelegramSettings(private val app: TelegramApplication) {
val shareChatInfo = shareChatsInfo[message.chatId] val shareChatInfo = shareChatsInfo[message.chatId]
val content = message.content val content = message.content
if (shareChatInfo != null && content is TdApi.MessageLocation) { if (shareChatInfo != null && content is TdApi.MessageLocation) {
shareChatInfo.start = message.date.toLong()
shareChatInfo.currentMessageId = message.id shareChatInfo.currentMessageId = message.id
shareChatInfo.lastSuccessfulLocation = LatLon(content.location.latitude, content.location.longitude) 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 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 { class DeviceBot {
var id: Long = -1 var id: Long = -1
var userId: Long = -1 var userId: Long = -1
@ -462,6 +544,30 @@ class TelegramSettings(private val app: TelegramApplication) {
var data: String = "" 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 { class ShareChatInfo {
var chatId = -1L var chatId = -1L
@ -472,10 +578,11 @@ class TelegramSettings(private val app: TelegramApplication) {
var userSetLivePeriod = -1L var userSetLivePeriod = -1L
var userSetLivePeriodStart = -1L var userSetLivePeriodStart = -1L
var lastSuccessfulLocation: LatLon? = null var lastSuccessfulLocation: LatLon? = null
var lastSuccessfulSendTime = -1L var lastSuccessfulSendTimeMs = -1L
var shouldDeletePreviousMessage = false var shouldDeletePreviousMessage = false
var additionalActiveTime = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0] var additionalActiveTime = ADDITIONAL_ACTIVE_TIME_VALUES_SEC[0]
var hasSharingError = false
fun getNextAdditionalActiveTime(): Long { fun getNextAdditionalActiveTime(): Long {
var index = ADDITIONAL_ACTIVE_TIME_VALUES_SEC.indexOf(additionalActiveTime) var index = ADDITIONAL_ACTIVE_TIME_VALUES_SEC.indexOf(additionalActiveTime)
return if (ADDITIONAL_ACTIVE_TIME_VALUES_SEC.lastIndex > index) { return if (ADDITIONAL_ACTIVE_TIME_VALUES_SEC.lastIndex > index) {
@ -484,7 +591,11 @@ class TelegramSettings(private val app: TelegramApplication) {
ADDITIONAL_ACTIVE_TIME_VALUES_SEC[index] ADDITIONAL_ACTIVE_TIME_VALUES_SEC[index]
} }
} }
fun getChatLiveMessageExpireTime(): Long {
return userSetLivePeriod - ((System.currentTimeMillis() / 1000) - start)
}
companion object { companion object {
internal const val CHAT_ID_KEY = "chatId" internal const val CHAT_ID_KEY = "chatId"

View file

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

View file

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

View file

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

@ -19,7 +19,7 @@ object OsmandFormatter {
val FEET_IN_ONE_METER = YARDS_IN_ONE_METER * 3f val FEET_IN_ONE_METER = YARDS_IN_ONE_METER * 3f
private val fixed2 = DecimalFormat("0.00") private val fixed2 = DecimalFormat("0.00")
private val fixed1 = DecimalFormat("0.0") private val fixed1 = DecimalFormat("0.0")
private const val SHORT_TIME_FORMAT = "%02d:%02d" private const val SHORT_TIME_FORMAT = "%02d:%02d"
private const val SIMPLE_TIME_OF_DAY_FORMAT = "HH:mm" private const val SIMPLE_TIME_OF_DAY_FORMAT = "HH:mm"
private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss" private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss"
@ -62,9 +62,13 @@ object OsmandFormatter {
} }
} }
fun getFormattedTime(seconds: Long): String { fun getFormattedTime(milliseconds: Long, useCurrentTime: Boolean = true): String {
val calendar = Calendar.getInstance() 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())) { return if (isSameDay(calendar, Calendar.getInstance())) {
SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time) SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time)
} else { } 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) 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? { fun getIcon(@DrawableRes id: Int): Drawable? {
return getDrawable(id, 0) return getDrawable(id, 0)
} }