Merge pull request #5811 from osmandapp/MyLocationSharingMode
My location sharing mode
This commit is contained in:
commit
ee0d79d59a
11 changed files with 572 additions and 63 deletions
12
OsmAnd-telegram/res/drawable/chat_card_bg_light.xml
Normal file
12
OsmAnd-telegram/res/drawable/chat_card_bg_light.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/bg_card_shadow_cr3dp" />
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@color/card_bg_light" />
|
||||
<corners android:radius="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -13,6 +13,7 @@
|
|||
android:background="?attr/card_bg_color">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/image_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/my_location_image_height"
|
||||
app:layout_scrollFlags="scroll">
|
||||
|
@ -131,6 +132,87 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/title_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/title_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/action_bar_height"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/action_bar_height"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:letterSpacing="@dimen/title_letter_spacing"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="@dimen/content_padding_standard"
|
||||
android:paddingRight="@dimen/content_padding_standard"
|
||||
android:text="@string/my_location"
|
||||
android:textColor="@color/app_bar_title_light"
|
||||
android:textSize="@dimen/title_text_size"
|
||||
app:typeface="@string/font_roboto_mono_bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/options_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="@dimen/content_padding_standard"
|
||||
android:layout_marginRight="@dimen/content_padding_standard"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:paddingLeft="@dimen/content_padding_half"
|
||||
android:paddingRight="@dimen/content_padding_half"
|
||||
tools:src="@drawable/ic_action_other_menu"
|
||||
tools:tint="@color/icon_light"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/appbar_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginLeft="@dimen/content_padding_standard"
|
||||
android:layout_marginStart="@dimen/content_padding_standard"
|
||||
android:background="?attr/card_divider_color" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/stop_all_sharing_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/action_bar_height"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="@dimen/content_padding_standard"
|
||||
android:paddingRight="@dimen/content_padding_standard">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/stop_all_sharing_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/content_padding_standard"
|
||||
android:layout_marginRight="@dimen/content_padding_standard"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/stop_sharing_all"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/descr_text_size"
|
||||
app:typeface="@string/font_roboto_medium" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/stop_all_sharing_switcher"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
|
|
185
OsmAnd-telegram/res/layout/my_location_sharing_chat.xml
Normal file
185
OsmAnd-telegram/res/layout/my_location_sharing_chat.xml
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?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:layout_marginTop="@dimen/content_padding_half"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/shared_chat_card_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/user_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/list_item_height_big"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/list_item_icon_size"
|
||||
android:layout_height="@dimen/list_item_icon_size"
|
||||
android:layout_marginLeft="@dimen/list_item_icon_margin_left"
|
||||
android:layout_marginRight="@dimen/list_item_icon_margin_left"
|
||||
tools:src="@drawable/img_user_picture" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/content_padding_standard"
|
||||
android:layout_marginRight="@dimen/content_padding_standard"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="@dimen/list_item_title_text_size"
|
||||
app:typeface="@string/font_roboto_regular"
|
||||
tools:text="Share location" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/duration_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
tools:text="Sharing:" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_medium"
|
||||
tools:text="1 h" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/text_in_area"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dialog_button_height"
|
||||
android:layout_marginEnd="@dimen/image_button_padding"
|
||||
android:layout_marginRight="@dimen/image_button_padding"
|
||||
android:background="?attr/secondary_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="@dimen/content_padding_half"
|
||||
android:paddingRight="@dimen/content_padding_half"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
android:visibility="gone"
|
||||
app:typeface="@string/font_roboto_medium"
|
||||
tools:text="+ 30 min"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/card_divider_color" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/show_on_map_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/list_item_height_big"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/content_padding_standard"
|
||||
android:paddingRight="@dimen/content_padding_standard">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="@dimen/content_padding_standard"
|
||||
android:layout_marginRight="@dimen/content_padding_standard"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/stop_translation_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="@dimen/content_padding_standard"
|
||||
android:layout_marginRight="@dimen/content_padding_standard"
|
||||
android:text="@string/stop_sharing_chat"
|
||||
android:textColor="?attr/ctrl_active_color"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_medium" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/stop_in"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_regular"
|
||||
tools:text="@string/stop_at" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/ending_in_first_part"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_mono_bold" />
|
||||
|
||||
<net.osmand.telegram.ui.views.TextViewEx
|
||||
android:id="@+id/ending_in_second_part"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/hint_text_size"
|
||||
app:typeface="@string/font_roboto_regular" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/switcher"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -11,6 +11,7 @@
|
|||
<attr name="primary_btn_bg" format="reference" />
|
||||
<attr name="primary_btn_text_color" format="reference" />
|
||||
<attr name="secondary_btn_bg" format="reference" />
|
||||
<attr name="shared_chat_card_bg" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TextViewEx">
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
<dimen name="list_item_height">56dp</dimen>
|
||||
<dimen name="list_item_height_min">48dp</dimen>
|
||||
<dimen name="list_item_height_big">64dp</dimen>
|
||||
|
||||
<dimen name="list_item_icon_size">40dp</dimen>
|
||||
<dimen name="list_item_icon_margin_left">12dp</dimen>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<string name="logout_help_desc">How to disconnect OsmAnd Location Sharing from Telegram</string>
|
||||
<string name="connected_account">Connected account</string>
|
||||
<string name="shared_string_account">Account</string>
|
||||
<string name="in_time">in %1$s</string>
|
||||
<string name="osmand_connect">OsmAnd connect</string>
|
||||
<string name="location_history_desc">Hide contacts that are not updated their location after the specified period of time.</string>
|
||||
<string name="location_history">Location history</string>
|
||||
|
@ -10,6 +11,10 @@
|
|||
<string name="send_my_location_desc">Set minimum interval for location sharing.</string>
|
||||
<string name="send_my_location">Send my location</string>
|
||||
<string name="gps_and_location">GPS & location</string>
|
||||
<string name="sharing_time">Sharing time</string>
|
||||
<string name="stop_at">Stop at</string>
|
||||
<string name="stop_sharing_all">Sharing is enabled (disable)</string>
|
||||
<string name="stop_sharing_chat">Stop chat sharing</string>
|
||||
<string name="open_osmand">Open OsmAnd</string>
|
||||
<string name="shared_string_live">Live</string>
|
||||
<string name="shared_string_bot">Bot</string>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="bottom_nav_shadow">@drawable/bg_bottom_bar_shadow_with_line_day</item>
|
||||
<item name="shared_chat_card_bg">@drawable/chat_card_bg_light</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionbar">
|
||||
|
|
|
@ -21,6 +21,8 @@ class TelegramHelper private constructor() {
|
|||
|
||||
companion object {
|
||||
const val OSMAND_BOT_USERNAME = "osmand_bot"
|
||||
|
||||
const val MESSAGE_ADD_ACTIVE_TIME_SEC = 30 * 60 // 30 min
|
||||
|
||||
private val log = PlatformUtil.getLog(TelegramHelper::class.java)
|
||||
private const val CHATS_LIMIT = 100
|
||||
|
@ -66,7 +68,7 @@ class TelegramHelper private constructor() {
|
|||
|
||||
private val chats = ConcurrentHashMap<Long, TdApi.Chat>()
|
||||
private val chatList = TreeSet<OrderedChat>()
|
||||
private val chatLiveMessages = ConcurrentHashMap<Long, Long>()
|
||||
private val chatLiveMessages = ConcurrentHashMap<Long, TdApi.Message>()
|
||||
|
||||
private val downloadChatFilesMap = ConcurrentHashMap<String, TdApi.Chat>()
|
||||
private val downloadUserFilesMap = ConcurrentHashMap<String, TdApi.User>()
|
||||
|
@ -100,6 +102,7 @@ class TelegramHelper private constructor() {
|
|||
var listener: TelegramListener? = null
|
||||
private val incomingMessagesListeners = HashSet<TelegramIncomingMessagesListener>()
|
||||
private val fullInfoUpdatesListeners = HashSet<FullInfoUpdatesListener>()
|
||||
private val chatLiveMessagesListeners = HashSet<ChatLiveMessagesListener>()
|
||||
|
||||
fun addIncomingMessagesListener(listener: TelegramIncomingMessagesListener) {
|
||||
incomingMessagesListeners.add(listener)
|
||||
|
@ -116,6 +119,14 @@ class TelegramHelper private constructor() {
|
|||
fun removeFullInfoUpdatesListener(listener: FullInfoUpdatesListener) {
|
||||
fullInfoUpdatesListeners.remove(listener)
|
||||
}
|
||||
|
||||
fun addChatLiveMessagesListener(listener: ChatLiveMessagesListener) {
|
||||
chatLiveMessagesListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeChatLiveMessagesListener(listener: ChatLiveMessagesListener) {
|
||||
chatLiveMessagesListeners.remove(listener)
|
||||
}
|
||||
|
||||
fun getChatList(): TreeSet<OrderedChat> {
|
||||
synchronized(chatList) {
|
||||
|
@ -123,6 +134,8 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
fun getChatListIds() = getChatList().map { it.chatId }
|
||||
|
||||
fun getChatIds() = chats.keys().toList()
|
||||
|
||||
fun getChat(id: Long) = chats[id]
|
||||
|
@ -138,6 +151,8 @@ class TelegramHelper private constructor() {
|
|||
usersLocationMessages.values.filter { it.chatId == chatId }
|
||||
|
||||
fun getMessages() = usersLocationMessages.values.toList()
|
||||
|
||||
fun getChatLiveMessages() = chatLiveMessages
|
||||
|
||||
fun getMessagesByChatIds(): Map<Long, List<TdApi.Message>> {
|
||||
val res = mutableMapOf<Long, MutableList<TdApi.Message>>()
|
||||
|
@ -219,6 +234,10 @@ class TelegramHelper private constructor() {
|
|||
fun onBasicGroupFullInfoUpdated(groupId: Int, info: TdApi.BasicGroupFullInfo)
|
||||
fun onSupergroupFullInfoUpdated(groupId: Int, info: TdApi.SupergroupFullInfo)
|
||||
}
|
||||
|
||||
interface ChatLiveMessagesListener {
|
||||
fun onChatLiveMessagesUpdated(messages: List<TdApi.Message>)
|
||||
}
|
||||
|
||||
interface TelegramAuthorizationRequestListener {
|
||||
fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType)
|
||||
|
@ -535,9 +554,10 @@ class TelegramHelper private constructor() {
|
|||
if (messages.isNotEmpty()) {
|
||||
for (msg in messages) {
|
||||
val chatId = msg.chatId
|
||||
chatLiveMessages[chatId] = msg.id
|
||||
chatLiveMessages[chatId] = msg
|
||||
}
|
||||
}
|
||||
chatLiveMessagesListeners.forEach { it.onChatLiveMessagesUpdated(messages.toList()) }
|
||||
onComplete?.invoke()
|
||||
}
|
||||
else -> listener?.onSendLiveLocationError(-1, "Receive wrong response from TDLib: $obj")
|
||||
|
@ -550,7 +570,7 @@ class TelegramHelper private constructor() {
|
|||
val location = TdApi.Location(latitude, longitude)
|
||||
chatLivePeriods.forEach { chatId, livePeriod ->
|
||||
val content = TdApi.InputMessageLocation(location, livePeriod.toInt())
|
||||
val msgId = chatLiveMessages[chatId]
|
||||
val msgId = chatLiveMessages[chatId]?.id
|
||||
if (msgId != null) {
|
||||
if (msgId != 0L) {
|
||||
client?.send(
|
||||
|
@ -559,7 +579,6 @@ class TelegramHelper private constructor() {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
chatLiveMessages[chatId] = 0L
|
||||
client?.send(
|
||||
TdApi.SendMessage(chatId, 0, false, true, null, content),
|
||||
liveLocationMessageUpdatesHandler
|
||||
|
@ -1067,7 +1086,7 @@ class TelegramHelper private constructor() {
|
|||
TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> {
|
||||
val updateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded
|
||||
val message = updateMessageSendSucceeded.message
|
||||
chatLiveMessages[message.chatId] = message.id
|
||||
chatLiveMessages[message.chatId] = message
|
||||
}
|
||||
TdApi.UpdateDeleteMessages.CONSTRUCTOR -> {
|
||||
val updateDeleteMessages = obj as TdApi.UpdateDeleteMessages
|
||||
|
@ -1075,10 +1094,11 @@ class TelegramHelper private constructor() {
|
|||
val chatId = updateDeleteMessages.chatId
|
||||
val deletedMessages = mutableListOf<TdApi.Message>()
|
||||
for (messageId in updateDeleteMessages.messageIds) {
|
||||
if (chatLiveMessages[chatId] == messageId) {
|
||||
if (chatLiveMessages[chatId]?.id == messageId) {
|
||||
chatLiveMessages.remove(chatId)
|
||||
}
|
||||
usersLocationMessages.remove(messageId)?.also { deletedMessages.add(it) }
|
||||
usersLocationMessages.remove(messageId)
|
||||
?.also { deletedMessages.add(it) }
|
||||
}
|
||||
if (deletedMessages.isNotEmpty()) {
|
||||
incomingMessagesListeners.forEach {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.osmand.telegram.ui
|
||||
|
||||
import android.animation.*
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
|
@ -16,15 +17,18 @@ import android.widget.*
|
|||
import net.osmand.telegram.R
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import net.osmand.telegram.helpers.TelegramHelper
|
||||
import net.osmand.telegram.helpers.TelegramHelper.ChatLiveMessagesListener
|
||||
import net.osmand.telegram.helpers.TelegramHelper.TelegramListener
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||
import net.osmand.telegram.ui.MyLocationTabFragment.MyLocationListAdapter.ChatViewHolder
|
||||
import net.osmand.telegram.utils.AndroidUtils
|
||||
import net.osmand.telegram.utils.OsmandFormatter
|
||||
import org.drinkless.td.libcore.telegram.TdApi
|
||||
|
||||
private const val SELECTED_CHATS_KEY = "selected_chats"
|
||||
private const val SHARE_LOCATION_CHAT = 1
|
||||
private const val DEFAULT_CHAT = 0
|
||||
|
||||
class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||
class MyLocationTabFragment : Fragment(), TelegramListener, ChatLiveMessagesListener {
|
||||
|
||||
private var textMarginSmall: Int = 0
|
||||
private var textMarginBig: Int = 0
|
||||
|
@ -37,14 +41,18 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
get() = activity?.application as TelegramApplication
|
||||
|
||||
private val telegramHelper get() = app.telegramHelper
|
||||
private val settings get() = app.settings
|
||||
|
||||
private lateinit var appBarLayout: AppBarLayout
|
||||
private lateinit var userImage: ImageView
|
||||
private lateinit var optionsBtn: ImageView
|
||||
private lateinit var imageContainer: FrameLayout
|
||||
private lateinit var textContainer: LinearLayout
|
||||
private lateinit var titleContainer: LinearLayout
|
||||
private lateinit var optionsBtn: ImageView
|
||||
private lateinit var title: TextView
|
||||
private lateinit var description: TextView
|
||||
private lateinit var searchBox: FrameLayout
|
||||
private lateinit var stopSharingSwitcher: Switch
|
||||
|
||||
private lateinit var searchBoxBg: GradientDrawable
|
||||
|
||||
|
@ -106,7 +114,17 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
|
||||
optionsBtn = mainView.findViewById<ImageView>(R.id.options).apply {
|
||||
setImageDrawable(app.uiUtils.getThemedIcon(R.drawable.ic_action_other_menu))
|
||||
setOnClickListener { showPopupMenu() }
|
||||
setOnClickListener { showPopupMenu(this) }
|
||||
}
|
||||
|
||||
mainView.findViewById<ImageView>(R.id.options_title).apply {
|
||||
setImageDrawable(app.uiUtils.getThemedIcon(R.drawable.ic_action_other_menu))
|
||||
setOnClickListener { showPopupMenu(this) }
|
||||
}
|
||||
|
||||
imageContainer = mainView.findViewById<FrameLayout>(R.id.image_container)
|
||||
titleContainer = mainView.findViewById<LinearLayout>(R.id.title_container).apply {
|
||||
AndroidUtils.addStatusBarPadding19v(context, this)
|
||||
}
|
||||
|
||||
textContainer = mainView.findViewById<LinearLayout>(R.id.text_container).apply {
|
||||
|
@ -143,14 +161,31 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
adapter = this@MyLocationTabFragment.adapter
|
||||
}
|
||||
|
||||
stopSharingSwitcher = mainView.findViewById<Switch>(R.id.stop_all_sharing_switcher).apply {
|
||||
isChecked = settings.hasAnyChatToShareLocation()
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
if (!isChecked) {
|
||||
app.settings.stopSharingLocationToChats()
|
||||
app.shareLocationHelper.stopSharingLocation()
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainView
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateList()
|
||||
telegramHelper.addChatLiveMessagesListener(this)
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
telegramHelper.removeChatLiveMessagesListener(this)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLongArray(SELECTED_CHATS_KEY, selectedChats.toLongArray())
|
||||
|
@ -160,6 +195,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == SetTimeDialogFragment.LOCATION_SHARED_REQUEST_CODE) {
|
||||
clearSelection()
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,31 +205,31 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
) {
|
||||
when (newTelegramAuthorizationState) {
|
||||
TelegramHelper.TelegramAuthorizationState.READY -> {
|
||||
updateList()
|
||||
updateContent()
|
||||
}
|
||||
TelegramHelper.TelegramAuthorizationState.LOGGING_OUT,
|
||||
TelegramHelper.TelegramAuthorizationState.CLOSED,
|
||||
TelegramHelper.TelegramAuthorizationState.UNKNOWN -> {
|
||||
adapter.chats = emptyList()
|
||||
adapter.chats = mutableListOf()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTelegramChatsRead() {
|
||||
updateList()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun onTelegramChatsChanged() {
|
||||
updateList()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun onTelegramChatChanged(chat: TdApi.Chat) {
|
||||
updateList()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun onTelegramUserChanged(user: TdApi.User) {
|
||||
updateList()
|
||||
updateContent()
|
||||
}
|
||||
|
||||
override fun onTelegramError(code: Int, message: String) {
|
||||
|
@ -202,6 +238,10 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
override fun onSendLiveLocationError(code: Int, message: String) {
|
||||
}
|
||||
|
||||
override fun onChatLiveMessagesUpdated(messages: List<TdApi.Message>) {
|
||||
app.runInUIThread { updateContent() }
|
||||
}
|
||||
|
||||
fun onPrimaryBtnClick() {
|
||||
val fm = fragmentManager ?: return
|
||||
SetTimeDialogFragment.showInstance(fm, selectedChats, this)
|
||||
|
@ -217,7 +257,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
actionButtonsListener?.switchButtonsVisibility(false)
|
||||
}
|
||||
|
||||
private fun showPopupMenu() {
|
||||
private fun showPopupMenu(anchor: View) {
|
||||
val ctx = context ?: return
|
||||
|
||||
val menuList = ArrayList<String>()
|
||||
|
@ -234,7 +274,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
|
||||
ListPopupWindow(ctx).apply {
|
||||
isModal = true
|
||||
anchorView = optionsBtn
|
||||
anchorView = anchor
|
||||
setContentWidth(AndroidUtils.getPopupMenuWidth(ctx, menuList))
|
||||
setDropDownGravity(Gravity.END or Gravity.TOP)
|
||||
setAdapter(ArrayAdapter(ctx, R.layout.popup_list_text_item, menuList))
|
||||
|
@ -335,12 +375,32 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateContent() {
|
||||
updateSharingMode()
|
||||
updateList()
|
||||
}
|
||||
|
||||
private fun updateSharingMode() {
|
||||
val headerParams = imageContainer.layoutParams as AppBarLayout.LayoutParams
|
||||
val sharingMode = settings.hasAnyChatToShareLocation()
|
||||
imageContainer.visibility = if (sharingMode) View.GONE else View.VISIBLE
|
||||
textContainer.visibility = if (sharingMode) View.GONE else View.VISIBLE
|
||||
titleContainer.visibility = if (sharingMode) View.VISIBLE else View.GONE
|
||||
headerParams.scrollFlags = if (sharingMode) 0 else AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
|
||||
stopSharingSwitcher.isChecked = sharingMode
|
||||
appBarScrollRange = -1
|
||||
}
|
||||
|
||||
private fun updateList() {
|
||||
val chatList = telegramHelper.getChatList()
|
||||
val chats: MutableList<TdApi.Chat> = mutableListOf()
|
||||
val currentUser = telegramHelper.getCurrentUser()
|
||||
for (orderedChat in chatList) {
|
||||
val chat = telegramHelper.getChat(orderedChat.chatId)
|
||||
val chatList = if (settings.hasAnyChatToShareLocation()) {
|
||||
settings.getShareLocationChats()
|
||||
} else {
|
||||
telegramHelper.getChatListIds()
|
||||
}
|
||||
for (chatId in chatList) {
|
||||
val chat = telegramHelper.getChat(chatId)
|
||||
if (chat != null) {
|
||||
if (telegramHelper.isPrivateChat(chat)) {
|
||||
if ((chat.type as TdApi.ChatTypePrivate).userId == currentUser?.id) {
|
||||
|
@ -353,21 +413,39 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
adapter.chats = chats
|
||||
}
|
||||
|
||||
inner class MyLocationListAdapter : RecyclerView.Adapter<ChatViewHolder>() {
|
||||
|
||||
var chats: List<TdApi.Chat> = emptyList()
|
||||
inner class MyLocationListAdapter : RecyclerView.Adapter<MyLocationListAdapter.BaseViewHolder>() {
|
||||
var chats = mutableListOf<TdApi.Chat>()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.user_list_item, parent, false)
|
||||
return ChatViewHolder(view)
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (settings.isSharingLocationToChat(chats[position].id)) {
|
||||
SHARE_LOCATION_CHAT
|
||||
} else {
|
||||
DEFAULT_CHAT
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||
return when (viewType) {
|
||||
SHARE_LOCATION_CHAT -> {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.my_location_sharing_chat, parent, false)
|
||||
SharingChatViewHolder(view)
|
||||
}
|
||||
DEFAULT_CHAT -> {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.user_list_item, parent, false)
|
||||
ChatViewHolder(view)
|
||||
}
|
||||
else -> throw RuntimeException("Unsupported view type: $viewType")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
|
||||
val chat = chats[position]
|
||||
val lastItem = position == itemCount - 1
|
||||
val placeholderId = if (telegramHelper.isGroup(chat)) R.drawable.img_group_picture else R.drawable.img_user_picture
|
||||
|
@ -375,53 +453,136 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
|||
|
||||
TelegramUiHelper.setupPhoto(app, holder.icon, chat.photo?.small?.local?.path, placeholderId, false)
|
||||
holder.title?.text = chat.title
|
||||
holder.description?.visibility = View.GONE
|
||||
if (live) {
|
||||
holder.checkBox?.visibility = View.GONE
|
||||
holder.textInArea?.visibility = View.VISIBLE
|
||||
holder.textInArea?.text = getString(R.string.shared_string_live)
|
||||
} else {
|
||||
holder.textInArea?.visibility = View.GONE
|
||||
holder.checkBox?.apply {
|
||||
visibility = View.VISIBLE
|
||||
setOnCheckedChangeListener(null)
|
||||
isChecked = selectedChats.contains(chat.id)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
selectedChats.add(chat.id)
|
||||
} else {
|
||||
selectedChats.remove(chat.id)
|
||||
}
|
||||
actionButtonsListener?.switchButtonsVisibility(selectedChats.isNotEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||
holder.itemView.setOnClickListener {
|
||||
|
||||
if (holder is ChatViewHolder) {
|
||||
holder.description?.visibility = View.GONE
|
||||
if (live) {
|
||||
app.settings.shareLocationToChat(chat.id, false)
|
||||
if (!app.settings.hasAnyChatToShareLocation()) {
|
||||
app.shareLocationHelper.stopSharingLocation()
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
holder.checkBox?.visibility = View.GONE
|
||||
} else {
|
||||
holder.checkBox?.apply {
|
||||
isChecked = !isChecked
|
||||
visibility = View.VISIBLE
|
||||
setOnCheckedChangeListener(null)
|
||||
isChecked = selectedChats.contains(chat.id)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
selectedChats.add(chat.id)
|
||||
} else {
|
||||
selectedChats.remove(chat.id)
|
||||
}
|
||||
actionButtonsListener?.switchButtonsVisibility(selectedChats.isNotEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||
holder.itemView.setOnClickListener {
|
||||
if (live) {
|
||||
app.settings.shareLocationToChat(chat.id, false)
|
||||
app.shareLocationHelper.stopSharingLocation()
|
||||
notifyItemChanged(position)
|
||||
} else {
|
||||
holder.checkBox?.apply {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (holder is SharingChatViewHolder) {
|
||||
holder.switcher?.apply {
|
||||
isChecked = live
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
if (!isChecked) {
|
||||
app.settings.shareLocationToChat(chat.id, false)
|
||||
app.shareLocationHelper.stopSharingLocation()
|
||||
removeItem(chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val duration = settings.getChatLivePeriod(chat.id)?.toInt()
|
||||
if (duration != null && duration > 0) {
|
||||
holder.descriptionDuration?.text = OsmandFormatter.getFormattedDuration(context!!, duration)
|
||||
holder.description?.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "${getText(R.string.sharing_time)}:"
|
||||
}
|
||||
}
|
||||
val message = telegramHelper.getChatLiveMessages()[chat.id]
|
||||
if (message != null) {
|
||||
val content = message.content
|
||||
if (content is TdApi.MessageLocation) {
|
||||
val expiresIn = content.expiresIn
|
||||
holder.textInArea?.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "${getText(R.string.plus)} ${OsmandFormatter.getFormattedDuration(context!!,
|
||||
TelegramHelper.MESSAGE_ADD_ACTIVE_TIME_SEC)}"
|
||||
setOnClickListener {
|
||||
var newLivePeriod = app.settings.getChatLivePeriod(chat.id)
|
||||
if (newLivePeriod != null) {
|
||||
newLivePeriod += TelegramHelper.MESSAGE_ADD_ACTIVE_TIME_SEC
|
||||
} else {
|
||||
newLivePeriod = TelegramHelper.MESSAGE_ADD_ACTIVE_TIME_SEC.toLong()
|
||||
}
|
||||
app.settings.shareLocationToChat(chat.id, true, newLivePeriod)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
holder.stopSharingDescr?.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "${getText(R.string.stop_at)}:"
|
||||
}
|
||||
|
||||
holder.stopSharingFirstPart?.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = OsmandFormatter.getFormattedTime(expiresIn)
|
||||
}
|
||||
|
||||
holder.stopSharingSecondPart?.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "(${getString(R.string.in_time,
|
||||
OsmandFormatter.getFormattedDuration(context!!, expiresIn, true))})"
|
||||
}
|
||||
if (expiresIn == 0) {
|
||||
removeItem(chat)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
holder.textInArea?.visibility = View.INVISIBLE
|
||||
holder.stopSharingDescr?.visibility = View.INVISIBLE
|
||||
holder.stopSharingFirstPart?.visibility = View.INVISIBLE
|
||||
holder.stopSharingSecondPart?.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeItem(chat: TdApi.Chat) {
|
||||
chats.remove(chat)
|
||||
if (chats.isEmpty()) {
|
||||
updateContent()
|
||||
} else {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = chats.size
|
||||
|
||||
inner class ChatViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||
abstract inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val icon: ImageView? = view.findViewById(R.id.icon)
|
||||
val title: TextView? = view.findViewById(R.id.title)
|
||||
val description: TextView? = view.findViewById(R.id.description)
|
||||
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
||||
}
|
||||
|
||||
inner class ChatViewHolder(val view: View) : BaseViewHolder(view) {
|
||||
val checkBox: CheckBox? = view.findViewById(R.id.check_box)
|
||||
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||
}
|
||||
|
||||
inner class SharingChatViewHolder(val view: View) : BaseViewHolder(view) {
|
||||
val descriptionDuration: TextView? = view.findViewById(R.id.duration)
|
||||
val switcher: Switch? = view.findViewById(R.id.switcher)
|
||||
val stopSharingDescr: TextView? = view.findViewById(R.id.stop_in)
|
||||
val stopSharingFirstPart: TextView? = view.findViewById(R.id.ending_in_first_part)
|
||||
val stopSharingSecondPart: TextView? = view.findViewById(R.id.ending_in_second_part)
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionButtonsListener {
|
||||
|
|
|
@ -216,7 +216,7 @@ class SetTimeDialogFragment : DialogFragment() {
|
|||
|
||||
TelegramUiHelper.setupPhoto(app, holder.icon, chat.photo?.small?.local?.path, placeholderId, false)
|
||||
holder.title?.text = chat.title
|
||||
holder.description?.text = "Some description" // FIXME
|
||||
holder.description?.visibility = View.INVISIBLE
|
||||
holder.textInArea?.apply {
|
||||
visibility = View.VISIBLE
|
||||
chatLivePeriods[chat.id]?.also { text = formatLivePeriod(it) }
|
||||
|
|
|
@ -3,8 +3,11 @@ package net.osmand.telegram.utils
|
|||
import android.content.Context
|
||||
import net.osmand.telegram.R
|
||||
import net.osmand.telegram.TelegramApplication
|
||||
import java.text.DateFormatSymbols
|
||||
import java.text.DecimalFormat
|
||||
import java.text.MessageFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
object OsmandFormatter {
|
||||
|
||||
|
@ -16,6 +19,12 @@ object OsmandFormatter {
|
|||
val FEET_IN_ONE_METER = YARDS_IN_ONE_METER * 3f
|
||||
private val fixed2 = DecimalFormat("0.00")
|
||||
private val fixed1 = DecimalFormat("0.0")
|
||||
|
||||
private const val SHORT_TIME_FORMAT = "%02d:%02d"
|
||||
private const val SIMPLE_TIME_OF_DAY_FORMAT = "HH:mm"
|
||||
|
||||
private val dateFormatSymbols = DateFormatSymbols.getInstance()
|
||||
private val localDaysStr = getLettersStringArray(dateFormatSymbols.shortWeekdays, 2)
|
||||
|
||||
init {
|
||||
fixed2.minimumFractionDigits = 2
|
||||
|
@ -24,9 +33,12 @@ object OsmandFormatter {
|
|||
fixed2.minimumIntegerDigits = 1
|
||||
}
|
||||
|
||||
fun getFormattedDuration(ctx: Context, seconds: Int): String {
|
||||
fun getFormattedDuration(ctx: Context, seconds: Int, short: Boolean = false): String {
|
||||
val hours = seconds / (60 * 60)
|
||||
val minutes = seconds / 60 % 60
|
||||
if (short) {
|
||||
return String.format(SHORT_TIME_FORMAT, hours, minutes)
|
||||
}
|
||||
return when {
|
||||
hours > 0 -> {
|
||||
var res = "$hours ${ctx.getString(R.string.shared_string_hour_short)}"
|
||||
|
@ -40,6 +52,17 @@ object OsmandFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
fun getFormattedTime(seconds: Int): String {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = System.currentTimeMillis() + (seconds * 1000)
|
||||
return if (isSameDay(calendar, Calendar.getInstance())) {
|
||||
SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time)
|
||||
} else {
|
||||
SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time) +
|
||||
" " + localDaysStr[calendar.get(Calendar.DAY_OF_WEEK)]
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateRoundedDist(distInMeters: Double, ctx: TelegramApplication): Double {
|
||||
val mc = ctx.settings.metricsConstants
|
||||
var mainUnitInMeter = 1.0
|
||||
|
@ -222,6 +245,24 @@ object OsmandFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isSameDay(cal1: Calendar, cal2: Calendar): Boolean {
|
||||
return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
|
||||
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
|
||||
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
|
||||
}
|
||||
|
||||
private fun getLettersStringArray(strings: Array<String>, letters: Int): Array<String?> {
|
||||
val newStrings = arrayOfNulls<String>(strings.size)
|
||||
for (i in strings.indices) {
|
||||
if (strings[i].length > letters) {
|
||||
newStrings[i] = (strings[i].substring(0, letters)).capitalize()
|
||||
} else {
|
||||
newStrings[i] = strings[i].capitalize()
|
||||
}
|
||||
}
|
||||
return newStrings
|
||||
}
|
||||
|
||||
enum class MetricsConstants private constructor(private val key: Int) {
|
||||
KILOMETERS_AND_METERS(R.string.si_km_m),
|
||||
MILES_AND_FEET(R.string.si_mi_feet),
|
||||
|
|
Loading…
Reference in a new issue