Telegram sharing status init

This commit is contained in:
Chumva 2018-10-12 19:17:38 +03:00
parent 519827262c
commit 0ff05fd0a6
15 changed files with 595 additions and 74 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

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -16,6 +15,7 @@
android:id="@+id/image_container" android:id="@+id/image_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/my_location_image_height" android:layout_height="@dimen/my_location_image_height"
android:visibility="gone"
app:layout_scrollFlags="scroll"> app:layout_scrollFlags="scroll">
<ImageView <ImageView
@ -50,11 +50,12 @@
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"> android:visibility="gone">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -157,6 +158,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,23 +168,21 @@
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"/>
</LinearLayout> </LinearLayout>
@ -192,38 +192,106 @@
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginLeft="@dimen/content_padding_standard" android:layout_marginLeft="@dimen/content_padding_standard"
android:layout_marginStart="@dimen/content_padding_standard" android:layout_marginStart="@dimen/content_padding_standard"
android:background="?attr/card_divider_color" /> android:background="?attr/card_divider_color"/>
<LinearLayout <LinearLayout
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"
android:src="@drawable/ic_action_live_now"
android: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 +299,11 @@
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_height="match_parent"> tools:visibility="gone">
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
@ -258,10 +326,10 @@
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"
app:typeface="@string/font_roboto_medium"
tools:visibility="visible"/> tools:visibility="visible"/>
</FrameLayout> </FrameLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,106 @@
<?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">
<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: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>
<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:ellipsize="end"
android:maxLines="1"
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:background="?attr/card_divider_color"/>
</LinearLayout>
</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

@ -45,6 +45,9 @@ class TelegramLocationProvider(private val app: TelegramApplication) : SensorEve
var lastKnownLocation: net.osmand.Location? = null var lastKnownLocation: net.osmand.Location? = null
private set private set
var lastKnownLocationTime: Long? = null
private set
val gpsInfo = GPSInfo() val gpsInfo = GPSInfo()
private val locationListeners = ArrayList<TelegramLocationListener>() private val locationListeners = ArrayList<TelegramLocationListener>()
@ -432,6 +435,7 @@ class TelegramLocationProvider(private val app: TelegramApplication) : SensorEve
updateGPSInfo(null) updateGPSInfo(null)
} }
if (location != null) { if (location != null) {
lastKnownLocationTime = location.time
if (gpsSignalLost) { if (gpsSignalLost) {
gpsSignalLost = false gpsSignalLost = false
} }

View file

@ -180,6 +180,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
updateShareInfoHandler?.postDelayed({ updateShareInfoHandler?.postDelayed({
if (isUsedByMyLocation(usedBy)) { if (isUsedByMyLocation(usedBy)) {
app().shareLocationHelper.updateSendLiveMessages() app().shareLocationHelper.updateSendLiveMessages()
app().settings.updateSharingStatusHistory()
startShareInfoUpdates() startShareInfoUpdates()
} }
}, UPDATE_LIVE_MESSAGES_INTERVAL_MS) }, UPDATE_LIVE_MESSAGES_INTERVAL_MS)
@ -296,6 +297,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,9 @@ 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,6 +130,7 @@ 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
@ -149,6 +157,8 @@ class TelegramSettings(private val app: TelegramApplication) {
fun getChatsShareInfo() = shareChatsInfo fun getChatsShareInfo() = shareChatsInfo
fun getLastSuccessfulSendTime() = shareChatsInfo.values.maxBy { it.lastSuccessfulSendTimeMs }?.lastSuccessfulSendTimeMs ?: -1
fun getChatLiveMessageExpireTime(chatId: Long): Long { fun getChatLiveMessageExpireTime(chatId: Long): Long {
val shareInfo = shareChatsInfo[chatId] val shareInfo = shareChatsInfo[chatId]
return if (shareInfo != null) { return if (shareInfo != null) {
@ -195,7 +205,56 @@ class TelegramSettings(private val app: TelegramApplication) {
if (shareChatInfo != null && content is TdApi.MessageLocation) { if (shareChatInfo != null && content is TdApi.MessageLocation) {
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) {
SharingStatusType.NO_INTERNET
} else if (app.locationProvider.lastKnownLocation == null || !app.locationProvider.gpsInfo.fixed) {
SharingStatusType.NO_GPS
} else {
var sendChatErrors = false
shareChatsInfo.forEach { _, shareInfo ->
if ((statusChangeTime - shareInfo.lastSuccessfulSendTimeMs) > (sendMyLocInterval * 2) * 1000) {
sendChatErrors = true
locationTime = shareInfo.lastSuccessfulSendTimeMs
val title = app.telegramHelper.getChat(shareInfo.chatId)?.title
if (title != null) {
chatsTitles.add(title)
}
}
}
if (sendChatErrors) {
SharingStatusType.NOT_POSSIBLE_TO_SENT_TO_CHATS
} else {
SharingStatusType.SUCCESSFULLY_SENT
}
}
if (statusType == SharingStatusType.NO_INTERNET || statusType == SharingStatusType.SUCCESSFULLY_SENT) {
locationTime = getLastSuccessfulSendTime()
} else if (statusType == SharingStatusType.NO_GPS) {
locationTime = app.locationProvider.lastKnownLocationTime ?: -1
}
}
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 +512,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 +558,35 @@ 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,7 +597,7 @@ 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]

View file

@ -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 {
@ -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,7 +633,9 @@ 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
} }
@ -651,7 +655,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)
}
} }
} }
} }
@ -758,14 +764,18 @@ 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.Message.CONSTRUCTOR -> { TdApi.Message.CONSTRUCTOR -> {
if (obj is TdApi.Message) { if (obj is TdApi.Message) {
if (obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) { if (obj.sendingState?.constructor == TdApi.MessageSendingStateFailed.CONSTRUCTOR) {
needRefreshActiveLiveLocationMessages = true needRefreshActiveLiveLocationMessages = true
listener?.onSendLiveLocationError(-1, "Live location message ${obj.id} failed to send") outgoingMessagesListeners.forEach {
it.onSendLiveLocationError(-1, "Live location message ${obj.id} failed to send")
}
} else { } else {
outgoingMessagesListeners.forEach { outgoingMessagesListeners.forEach {
it.onUpdateMessages(listOf(obj)) it.onUpdateMessages(listOf(obj))

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
@ -71,7 +74,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
private var actionButtonsListener: ActionButtonsListener? = null private var actionButtonsListener: ActionButtonsListener? = null
private var sharingMode = false private var sharingMode = false
private var updateEnable: Boolean = false private var updateEnable: Boolean = false
override fun onCreateView( override fun onCreateView(
@ -90,7 +93,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
searchBoxSidesMargin = resources.getDimensionPixelSize(R.dimen.content_padding_half) searchBoxSidesMargin = resources.getDimensionPixelSize(R.dimen.content_padding_half)
sharingMode = settings.hasAnyChatToShareLocation() sharingMode = settings.hasAnyChatToShareLocation()
savedInstanceState?.apply { savedInstanceState?.apply {
selectedChats.addAll(getLongArray(SELECTED_CHATS_KEY).toSet()) selectedChats.addAll(getLongArray(SELECTED_CHATS_KEY).toSet())
if (selectedChats.isNotEmpty()) { if (selectedChats.isNotEmpty()) {
@ -132,6 +135,14 @@ 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
}
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 +192,17 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
} }
} }
mainView.findViewById<View>(R.id.sharing_status_container).setOnClickListener {
settings.updateSharingStatusHistory()
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 {
@ -191,7 +211,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
updateContent() updateContent()
} }
} }
return mainView return mainView
} }
@ -208,7 +228,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
super.onPause() super.onPause()
updateEnable = false updateEnable = false
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray()) outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray())
@ -269,9 +289,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
@ -306,7 +323,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
} }
}, ADAPTER_UPDATE_INTERVAL_MIL) }, ADAPTER_UPDATE_INTERVAL_MIL)
} }
private fun animateStartSharingBtn(show: Boolean) { private fun animateStartSharingBtn(show: Boolean) {
if (startSharingBtn.visibility == View.VISIBLE) { if (startSharingBtn.visibility == View.VISIBLE) {
val scale = if (show) 1f else 0f val scale = if (show) 1f else 0f
@ -318,7 +335,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
.start() .start()
} }
} }
private fun clearSelection() { private fun clearSelection() {
selectedChats.clear() selectedChats.clear()
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
@ -394,6 +411,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
} }
private fun updateContent() { private fun updateContent() {
updateSharingStatus()
updateSharingMode() updateSharingMode()
updateList() updateList()
} }
@ -409,6 +427,15 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
appBarScrollRange = -1 appBarScrollRange = -1
} }
private fun updateSharingStatus() {
if (sharingMode) {
if (settings.sharingStatusChanges.isEmpty()) {
settings.updateSharingStatusHistory()
}
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()
@ -441,7 +468,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
list.sortWith(Comparator<TdApi.Chat> { o1, o2 -> o1.title.compareTo(o2.title) }) list.sortWith(Comparator<TdApi.Chat> { o1, o2 -> o1.title.compareTo(o2.title) })
return list return list
} }
inner class MyLocationListAdapter : RecyclerView.Adapter<MyLocationListAdapter.BaseViewHolder>() { inner class MyLocationListAdapter : RecyclerView.Adapter<MyLocationListAdapter.BaseViewHolder>() {
var chats = mutableListOf<TdApi.Chat>() var chats = mutableListOf<TdApi.Chat>()
set(value) { set(value) {
@ -540,7 +567,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
} }
val expiresIn = settings.getChatLiveMessageExpireTime(chat.id) val expiresIn = settings.getChatLiveMessageExpireTime(chat.id)
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

View file

@ -0,0 +1,97 @@
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.TextView
import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.TelegramSettings
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) {
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_four_text_lines, itemsCont, false).apply {
val sharingStatusType = sharingStatus.statusType
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)
val time = sharingStatus.locationTime
findViewById<TextView>(R.id.last_location_line).text = getString(sharingStatusType.descriptionId)
if (time > 0) {
val sentTime = OsmandFormatter.getFormattedTime(time, false)
findViewById<TextView>(R.id.last_location_line_time).text = sentTime
} else {
findViewById<TextView>(R.id.last_location_line_time).text = "-"
}
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 { 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

@ -21,7 +21,7 @@ object OsmandFormatter {
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:ss"
private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss" private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss"
private const val MIN_DURATION_FOR_DATE_FORMAT = 48 * 60 * 60 private const val MIN_DURATION_FOR_DATE_FORMAT = 48 * 60 * 60
@ -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)
} }