Merge pull request #5978 from osmandapp/LiveNowSorting

Live now sorting
This commit is contained in:
Alexander Sytnyk 2018-09-05 11:39:14 +03:00 committed by GitHub
commit 561814cf90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 507 additions and 36 deletions

View file

@ -0,0 +1,65 @@
<?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"
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.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="@dimen/list_item_height_min"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/shared_string_sort_by"
android:textColor="?android:textColorSecondary"
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: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>
</LinearLayout>

View file

@ -59,6 +59,58 @@
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/descr_text_size" /> android:textSize="@dimen/descr_text_size" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/app_bar_divider" />
<LinearLayout
android:id="@+id/sort_by_container"
android:layout_width="match_parent"
android:layout_height="@dimen/list_header_height"
android:background="@color/screen_bg_light"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard">
<net.osmand.telegram.ui.views.TextViewEx
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:layout_weight="0.5"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="@string/shared_string_sort"
android:textColor="@color/ctrl_active_light"
android:textSize="@dimen/descr_text_size"
app:typeface="@string/font_roboto_medium" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/sort_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:layout_weight="0.5"
android:drawablePadding="@dimen/content_padding_standard"
android:ellipsize="end"
android:gravity="center|end"
android:maxLines="1"
android:textColor="@color/ctrl_active_light"
android:textSize="@dimen/descr_text_size"
app:typeface="@string/font_roboto_medium"
tools:text="@string/by_group" />
</LinearLayout>
</LinearLayout>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout

View file

@ -7,7 +7,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/list_item_divider"/> <include
android:id="@+id/top_divider"
layout="@layout/list_item_divider"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -18,7 +20,7 @@
<LinearLayout <LinearLayout
android:id="@+id/user_row" android:id="@+id/user_row"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"> android:gravity="center_vertical">
@ -48,12 +50,45 @@
android:maxLines="1" android:maxLines="1"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size" android:textSize="@dimen/list_item_title_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height_big"
app:typeface="@string/font_roboto_regular" app:typeface="@string/font_roboto_regular"
tools:text="Share location"/> tools:text="Share location"/>
<LinearLayout
android:id="@+id/group_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/group_icon"
android:layout_width="@dimen/list_item_icon_size_small"
android:layout_height="@dimen/list_item_icon_size_small"
android:layout_gravity="bottom"
android:layout_marginEnd="@dimen/content_padding_small"
android:layout_marginRight="@dimen/content_padding_small"
tools:src="@drawable/img_group_picture" />
<net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/group_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height"
app:typeface="@string/font_roboto_regular"
tools:text="@string/shared_string_group" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:paddingBottom="@dimen/content_padding_half">
<LinearLayout <LinearLayout
android:id="@+id/location_view_container" android:id="@+id/location_view_container"
@ -67,8 +102,9 @@
android:id="@+id/direction_icon" android:id="@+id/direction_icon"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="4dp" android:layout_gravity="bottom"
android:layout_marginRight="4dp" android:layout_marginEnd="@dimen/content_padding_small"
android:layout_marginRight="@dimen/content_padding_small"
tools:src="@drawable/ic_direction_arrow" tools:src="@drawable/ic_direction_arrow"
tools:tint="@color/ctrl_active_light"/> tools:tint="@color/ctrl_active_light"/>
@ -78,11 +114,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:textSize="@dimen/list_item_description_text_size" android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height_small"
app:typeface="@string/font_roboto_medium" app:typeface="@string/font_roboto_medium"
tools:text="213 m" tools:text="213 m"
tools:textColor="@color/ctrl_active_light"/> tools:textColor="@color/ctrl_active_light"/>
<TextView <net.osmand.telegram.ui.views.TextViewEx
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="4dp" android:layout_marginLeft="4dp"
@ -90,7 +127,8 @@
android:text="•" android:text="•"
android:textColor="?attr/android:textColorSecondary" android:textColor="?attr/android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" android:textSize="@dimen/list_item_description_text_size"
android:visibility="visible" /> android:visibility="visible"
app:firstBaselineToTopHeight="@dimen/list_item_icon_margin_right" />
</LinearLayout> </LinearLayout>
@ -103,6 +141,7 @@
android:maxLines="1" android:maxLines="1"
android:textColor="?attr/android:textColorSecondary" android:textColor="?attr/android:textColorSecondary"
android:textSize="@dimen/list_item_description_text_size" android:textSize="@dimen/list_item_description_text_size"
app:firstBaselineToTopHeight="@dimen/list_item_icon_margin_right"
app:typeface="@string/font_roboto_regular" app:typeface="@string/font_roboto_regular"
tools:text="Live: 1 • All: 36"/> tools:text="Live: 1 • All: 36"/>
@ -136,11 +175,12 @@
<net.osmand.telegram.ui.views.TextViewEx <net.osmand.telegram.ui.views.TextViewEx
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_marginEnd="@dimen/content_padding_standard"
android:layout_marginRight="@dimen/content_padding_standard" android:layout_marginRight="@dimen/content_padding_standard"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/show_on_map" android:text="@string/show_on_map"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height_big"
android:textAppearance="?attr/textAppearanceListItemSecondary" android:textAppearance="?attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:typeface="@string/font_roboto_medium"/> app:typeface="@string/font_roboto_medium"/>
@ -148,7 +188,8 @@
<net.osmand.telegram.ui.views.TextViewEx <net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/show_on_map_state" android:id="@+id/show_on_map_state"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
app:firstBaselineToTopHeight="@dimen/list_item_baseline_to_top_height_big"
android:textAppearance="?attr/textAppearanceListItemSecondary" android:textAppearance="?attr/textAppearanceListItemSecondary"
android:textColor="?attr/ctrl_active_color" android:textColor="?attr/ctrl_active_color"
app:typeface="@string/font_roboto_medium" app:typeface="@string/font_roboto_medium"
@ -160,7 +201,6 @@
android:id="@+id/bottom_divider" android:id="@+id/bottom_divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginBottom="@dimen/card_divider_bottom_margin"
android:layout_marginLeft="@dimen/list_item_content_margin" android:layout_marginLeft="@dimen/list_item_content_margin"
android:layout_marginStart="@dimen/list_item_content_margin" android:layout_marginStart="@dimen/list_item_content_margin"
android:background="?attr/card_divider_color"/> android:background="?attr/card_divider_color"/>

View file

@ -30,6 +30,8 @@
<color name="card_divider_light">#f0f0f0</color> <color name="card_divider_light">#f0f0f0</color>
<color name="card_divider_dark">#2d3133</color> <color name="card_divider_dark">#2d3133</color>
<color name="app_bar_divider">#e2e2e2</color>
<color name="primary_btn_text_light">#ffffff</color> <color name="primary_btn_text_light">#ffffff</color>
<color name="primary_btn_text_dark">#cccccc</color> <color name="primary_btn_text_dark">#cccccc</color>

View file

@ -29,12 +29,17 @@
<dimen name="list_item_height_min">48dp</dimen> <dimen name="list_item_height_min">48dp</dimen>
<dimen name="list_item_height_big">64dp</dimen> <dimen name="list_item_height_big">64dp</dimen>
<dimen name="list_item_icon_size_small">14dp</dimen>
<dimen name="list_item_icon_size">40dp</dimen> <dimen name="list_item_icon_size">40dp</dimen>
<dimen name="list_item_icon_margin_left">12dp</dimen> <dimen name="list_item_icon_margin_left">12dp</dimen>
<dimen name="list_item_icon_margin_right">20dp</dimen> <dimen name="list_item_icon_margin_right">20dp</dimen>
<dimen name="list_item_content_margin">72dp</dimen> <dimen name="list_item_content_margin">72dp</dimen>
<dimen name="list_item_bottom_margin">3dp</dimen> <dimen name="list_item_bottom_margin">3dp</dimen>
<dimen name="list_item_baseline_to_top_height_small">20dp</dimen>
<dimen name="list_item_baseline_to_top_height">24dp</dimen>
<dimen name="list_item_baseline_to_top_height_big">28dp</dimen>
<dimen name="list_view_bottom_padding">52dp</dimen> <dimen name="list_view_bottom_padding">52dp</dimen>

View file

@ -1,4 +1,10 @@
<resources> <resources>
<string name="shared_string_name">Name</string>
<string name="by_distance">By distance</string>
<string name="by_name">By name</string>
<string name="by_group">By group</string>
<string name="shared_string_sort">Sort</string>
<string name="shared_string_sort_by">Sort by</string>
<string name="choose_osmand_desc">Select OsmAnd version where contacts will be displayed on the map.</string> <string name="choose_osmand_desc">Select OsmAnd version where contacts will be displayed on the map.</string>
<string name="choose_osmand">Select OsmAnd version to use</string> <string name="choose_osmand">Select OsmAnd version to use</string>
<string name="disable_all_sharing_desc">It will disable sharing your location to all selected chats (%1$d).</string> <string name="disable_all_sharing_desc">It will disable sharing your location to all selected chats (%1$d).</string>

View file

@ -49,6 +49,8 @@ private const val DEFAULT_VISIBLE_TIME_SECONDS = 60 * 60L // 1 hour
private const val TITLES_REPLACED_WITH_IDS = "changed_to_chat_id" private const val TITLES_REPLACED_WITH_IDS = "changed_to_chat_id"
private const val LIVE_NOW_SORT_TYPE_KEY = "live_now_sort_type"
class TelegramSettings(private val app: TelegramApplication) { class TelegramSettings(private val app: TelegramApplication) {
private var chatLivePeriods = mutableMapOf<Long, Long>() private var chatLivePeriods = mutableMapOf<Long, Long>()
@ -67,6 +69,8 @@ class TelegramSettings(private val app: TelegramApplication) {
var appToConnectPackage = "" var appToConnectPackage = ""
private set private set
var liveNowSortType = LiveNowSortType.SORT_BY_GROUP
val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref()) val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref())
init { init {
@ -210,6 +214,8 @@ class TelegramSettings(private val app: TelegramApplication) {
edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime) edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime)
edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage) edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage)
edit.putString(LIVE_NOW_SORT_TYPE_KEY, liveNowSortType.name)
edit.apply() edit.apply()
} }
@ -246,6 +252,8 @@ class TelegramSettings(private val app: TelegramApplication) {
locHistoryTime = prefs.getLong(LOC_HISTORY_TIME_KEY, locHistoryDef) locHistoryTime = prefs.getLong(LOC_HISTORY_TIME_KEY, locHistoryDef)
appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "") appToConnectPackage = prefs.getString(APP_TO_CONNECT_PACKAGE_KEY, "")
liveNowSortType = LiveNowSortType.valueOf(prefs.getString(LIVE_NOW_SORT_TYPE_KEY, LiveNowSortType.SORT_BY_GROUP.name))
} }
private fun updatePrefs() { private fun updatePrefs() {
@ -369,4 +377,24 @@ class TelegramSettings(private val app: TelegramApplication) {
values().filter { AndroidUtils.isAppInstalled(context, it.appPackage) } values().filter { AndroidUtils.isAppInstalled(context, it.appPackage) }
} }
} }
enum class LiveNowSortType(@DrawableRes val iconId: Int, @StringRes val titleId: Int, @StringRes val shortTitleId: Int) {
SORT_BY_GROUP(
R.drawable.ic_action_sort_by_group,
R.string.shared_string_group,
R.string.by_group
),
SORT_BY_NAME(
R.drawable.ic_action_sort_by_name,
R.string.shared_string_name,
R.string.by_name
),
SORT_BY_DISTANCE(
R.drawable.ic_action_sort_by_distance,
R.string.shared_string_distance,
R.string.by_distance
);
fun isSortByGroup() = this == SORT_BY_GROUP
}
} }

View file

@ -76,11 +76,11 @@ class ShowLocationHelper(private val app: TelegramApplication) {
val chatId = message.chatId val chatId = message.chatId
val chatTitle = telegramHelper.getChat(message.chatId)?.title val chatTitle = telegramHelper.getChat(message.chatId)?.title
val content = message.content val content = message.content
val date = telegramHelper.getLastUpdatedTime(message)
val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime
if (chatTitle != null && content is TdApi.MessageLocation) { if (chatTitle != null && content is TdApi.MessageLocation) {
var userName = "" var userName = ""
var photoPath: String? = null var photoPath: String? = null
val date = telegramHelper.getLastUpdatedTime(message)
val stale = System.currentTimeMillis() / 1000 - date > app.settings.staleLocTime
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
if (user != null) { if (user != null) {
userName = "${user.firstName} ${user.lastName}".trim() userName = "${user.firstName} ${user.lastName}".trim()
@ -113,10 +113,10 @@ class ShowLocationHelper(private val app: TelegramApplication) {
setupMapLayer() setupMapLayer()
if (update) { if (update) {
osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_$name", name, name, osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, "${chatId}_$name", name, name,
chatTitle, Color.WHITE, ALatLon(content.lat, content.lon), null, null) chatTitle, Color.WHITE, ALatLon(content.lat, content.lon), null, generatePhotoParams(null, stale))
} else { } else {
osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_$name", name, name, osmandAidlHelper.addMapPoint(MAP_LAYER_ID, "${chatId}_$name", name, name,
chatTitle, Color.WHITE, ALatLon(content.lat, content.lon), null, null) chatTitle, Color.WHITE, ALatLon(content.lat, content.lon), null, generatePhotoParams(null, stale))
} }
} }
} }

View file

@ -48,6 +48,7 @@ object TelegramUiHelper {
val res = ChatItem().apply { val res = ChatItem().apply {
chatId = chat.id chatId = chat.id
chatTitle = chat.title chatTitle = chat.title
name = chat.title
photoPath = chat.photo?.small?.local?.path photoPath = chat.photo?.small?.local?.path
placeholderId = R.drawable.img_user_picture placeholderId = R.drawable.img_user_picture
} }
@ -109,6 +110,19 @@ object TelegramUiHelper {
else -> null else -> null
} }
} }
fun messageToChatItem(
helper: TelegramHelper,
chat: TdApi.Chat,
message: TdApi.Message
): ChatItem? {
val content = message.content
return when (content) {
is MessageOsmAndBotLocation -> botMessageToChatItem(helper, chat, content)
is TdApi.MessageLocation -> locationMessageToChatItem(helper, chat, message)
else -> null
}
}
private fun botMessageToLocationItem( private fun botMessageToLocationItem(
chat: TdApi.Chat, chat: TdApi.Chat,
@ -148,12 +162,63 @@ object TelegramUiHelper {
} }
} }
private fun botMessageToChatItem(
helper: TelegramHelper,
chat: TdApi.Chat,
content: MessageOsmAndBotLocation
): ChatItem? {
return if (content.isValid()) {
ChatItem().apply {
chatId = chat.id
chatTitle = chat.title
name = content.name
latLon = LatLon(content.lat, content.lon)
photoPath = chat.photo?.small?.local?.path
placeholderId = R.drawable.img_user_picture
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
lastUpdated = content.lastUpdated
chatWithBot = true
}
} else {
null
}
}
private fun locationMessageToChatItem(
helper: TelegramHelper,
chat: TdApi.Chat,
message: TdApi.Message
): ChatItem? {
val user = helper.getUser(message.senderUserId) ?: return null
val content = message.content as TdApi.MessageLocation
return ChatItem().apply {
chatId = chat.id
chatTitle = chat.title
name = TelegramUiHelper.getUserName(user)
latLon = LatLon(content.location.latitude, content.location.longitude)
if (helper.isGroup(chat)) {
photoPath = helper.getUserPhotoPath(user)
groupPhotoPath = chat.photo?.small?.local?.path
} else {
photoPath = chat.photo?.small?.local?.path
}
grayscalePhotoPath = helper.getUserGreyPhotoPath(user)
placeholderId = R.drawable.img_user_picture
userId = message.senderUserId
privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat)
chatWithBot = helper.isBot(userId)
lastUpdated = helper.getLastUpdatedTime(message)
}
}
abstract class ListItem { abstract class ListItem {
var chatId: Long = 0 var chatId: Long = 0
internal set internal set
var chatTitle: String = "" var chatTitle: String = ""
internal set internal set
var name: String = ""
internal set
var latLon: LatLon? = null var latLon: LatLon? = null
internal set internal set
var photoPath: String? = null var photoPath: String? = null
@ -176,6 +241,8 @@ object TelegramUiHelper {
class ChatItem : ListItem() { class ChatItem : ListItem() {
var groupPhotoPath: String? = null
internal set
var privateChat: Boolean = false var privateChat: Boolean = false
internal set internal set
var chatWithBot: Boolean = false var chatWithBot: Boolean = false
@ -185,7 +252,7 @@ object TelegramUiHelper {
var liveMembersCount: Int = 0 var liveMembersCount: Int = 0
internal set internal set
override fun canBeOpenedOnMap() = latLon != null && !chatWithBot override fun canBeOpenedOnMap() = latLon != null
override fun getMapPointId() = "${chatId}_$userId" override fun getMapPointId() = "${chatId}_$userId"
@ -194,9 +261,6 @@ object TelegramUiHelper {
class LocationItem : ListItem() { class LocationItem : ListItem() {
var name: String = ""
internal set
override fun canBeOpenedOnMap() = latLon != null override fun canBeOpenedOnMap() = latLon != null
override fun getMapPointId(): String { override fun getMapPointId(): String {

View file

@ -13,8 +13,10 @@ import android.view.ViewGroup
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import net.osmand.Location import net.osmand.Location
import net.osmand.data.LatLon
import net.osmand.telegram.R import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
@ -49,6 +51,7 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
private lateinit var locationViewCache: UpdateLocationViewCache private lateinit var locationViewCache: UpdateLocationViewCache
private lateinit var openOsmAndBtn: TextView private lateinit var openOsmAndBtn: TextView
private lateinit var sortByBtn: TextView
private var location: Location? = null private var location: Location? = null
private var heading: Float? = null private var heading: Float? = null
@ -80,6 +83,15 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
(activity as MainActivity).setupOptionsBtn(mainView.findViewById<ImageView>(R.id.options)) (activity as MainActivity).setupOptionsBtn(mainView.findViewById<ImageView>(R.id.options))
sortByBtn = mainView.findViewById<TextView>(R.id.sort_button)
updateSortBtn()
mainView.findViewById<LinearLayout>(R.id.sort_by_container).setOnClickListener {
fragmentManager?.also { fm ->
SortByBottomSheet.showInstance(fm, this@LiveNowTabFragment)
}
}
openOsmAndBtn = mainView.findViewById<TextView>(R.id.open_osmand_btn).apply { openOsmAndBtn = mainView.findViewById<TextView>(R.id.open_osmand_btn).apply {
setOnClickListener { setOnClickListener {
val pack = settings.appToConnectPackage val pack = settings.appToConnectPackage
@ -114,8 +126,12 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ChooseOsmAndBottomSheet.OSMAND_CHOSEN_REQUEST_CODE) { when (requestCode) {
updateOpenOsmAndIcon() ChooseOsmAndBottomSheet.OSMAND_CHOSEN_REQUEST_CODE -> updateOpenOsmAndIcon()
SortByBottomSheet.SORTING_CHANGED_REQUEST_CODE -> {
updateSortBtn()
updateList()
}
} }
} }
@ -248,20 +264,52 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
val res = mutableListOf<ListItem>() val res = mutableListOf<ListItem>()
for ((id, messages) in telegramHelper.getMessagesByChatIds(settings.locHistoryTime)) { for ((id, messages) in telegramHelper.getMessagesByChatIds(settings.locHistoryTime)) {
telegramHelper.getChat(id)?.also { chat -> telegramHelper.getChat(id)?.also { chat ->
res.add(TelegramUiHelper.chatToChatItem(telegramHelper, chat, messages)) if (settings.liveNowSortType.isSortByGroup()) {
res.add(TelegramUiHelper.chatToChatItem(telegramHelper, chat, messages))
}
val type = chat.type val type = chat.type
if (type is TdApi.ChatTypeBasicGroup || type is TdApi.ChatTypeSupergroup) { if (type is TdApi.ChatTypeBasicGroup || type is TdApi.ChatTypeSupergroup) {
res.addAll(convertToLocationItems(chat, messages)) res.addAll(convertToListItems(chat, messages))
} else if (type is TdApi.ChatTypePrivate) { } else if (type is TdApi.ChatTypePrivate) {
if (telegramHelper.isOsmAndBot(type.userId)) { when {
res.addAll(convertToLocationItems(chat, messages)) telegramHelper.isOsmAndBot(type.userId) -> res.addAll(convertToListItems(chat, messages))
} else if (messages.firstOrNull { it.viaBotUserId != 0 } != null) { messages.firstOrNull { it.viaBotUserId != 0 } != null -> res.addAll(convertToListItems(chat, messages, true))
res.addAll(convertToLocationItems(chat, messages, true)) !settings.liveNowSortType.isSortByGroup() -> res.add(TelegramUiHelper.chatToChatItem(telegramHelper, chat, messages))
} }
} }
} }
} }
adapter.items = res
adapter.items = sortAdapterItems(res)
}
private fun sortAdapterItems(list: MutableList<ListItem>): MutableList<ListItem> {
if (settings.liveNowSortType == TelegramSettings.LiveNowSortType.SORT_BY_DISTANCE) {
list.sortWith(java.util.Comparator<ListItem> { lhs, rhs ->
if (location == null) {
return@Comparator 0
}
val loc = LatLon(location!!.latitude, location!!.longitude)
val ld = MapUtils.getDistance(loc, lhs.latLon!!.latitude, lhs.latLon!!.longitude)
val rd = MapUtils.getDistance(loc, rhs.latLon!!.latitude, rhs.latLon!!.longitude)
java.lang.Double.compare(ld, rd)
})
} else if (settings.liveNowSortType == TelegramSettings.LiveNowSortType.SORT_BY_NAME) {
list.sortWith(Comparator<ListItem> { o1, o2 -> o1.name.compareTo(o2.name) })
}
return list
}
private fun convertToListItems(
chat: TdApi.Chat,
messages: List<TdApi.Message>,
addOnlyViaBotMessages: Boolean = false
): List<ListItem> {
return if (settings.liveNowSortType.isSortByGroup()) {
convertToLocationItems(chat, messages, addOnlyViaBotMessages)
} else {
convertToChatItems(chat, messages, addOnlyViaBotMessages)
}
} }
private fun convertToLocationItems( private fun convertToLocationItems(
@ -280,6 +328,22 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
return res return res
} }
private fun convertToChatItems(
chat: TdApi.Chat,
messages: List<TdApi.Message>,
addOnlyViaBotMessages: Boolean = false
): List<ChatItem> {
val res = mutableListOf<ChatItem>()
messages.forEach { message ->
if (!addOnlyViaBotMessages || message.viaBotUserId != 0) {
TelegramUiHelper.messageToChatItem(telegramHelper, chat, message)?.also {
res.add(it)
}
}
}
return res
}
private fun showOsmAndMissingDialog() { private fun showOsmAndMissingDialog() {
activity?.let { activity?.let {
MainActivity.OsmandMissingDialogFragment().show(it.supportFragmentManager, null) MainActivity.OsmandMissingDialogFragment().show(it.supportFragmentManager, null)
@ -301,6 +365,16 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
return AndroidUtils.isAppInstalled(ctx, settings.appToConnectPackage) return AndroidUtils.isAppInstalled(ctx, settings.appToConnectPackage)
} }
private fun updateSortBtn() {
sortByBtn.text = getString(settings.liveNowSortType.shortTitleId)
sortByBtn.setCompoundDrawablesWithIntrinsicBounds(
null,
null,
app.uiUtils.getActiveIcon(settings.liveNowSortType.iconId),
null
)
}
inner class LiveNowListAdapter : RecyclerView.Adapter<BaseViewHolder>() { inner class LiveNowListAdapter : RecyclerView.Adapter<BaseViewHolder>() {
private var lastResponseStr = getString(R.string.last_response) + ": " private var lastResponseStr = getString(R.string.last_response) + ": "
@ -336,17 +410,23 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
val lastItem = position == itemCount - 1 val lastItem = position == itemCount - 1
val item = items[position] val item = items[position]
val sortByGroup = settings.liveNowSortType.isSortByGroup()
val canBeOpenedOnMap = item.canBeOpenedOnMap() val canBeOpenedOnMap = item.canBeOpenedOnMap()
val openOnMapView = holder.getOpenOnMapClickView() val openOnMapView = holder.getOpenOnMapClickView()
val staleLocation = System.currentTimeMillis() / 1000 - item.lastUpdated > settings.staleLocTime val staleLocation = System.currentTimeMillis() / 1000 - item.lastUpdated > settings.staleLocTime
if (staleLocation) { if (staleLocation) {
TelegramUiHelper.setupPhoto(app, holder.icon, item.grayscalePhotoPath, item.placeholderId, false) val photoPath = if (sortByGroup && item is ChatItem && !item.privateChat) {
item.photoPath
} else {
item.grayscalePhotoPath
}
TelegramUiHelper.setupPhoto(app, holder.icon, photoPath, item.placeholderId, false)
} else { } else {
TelegramUiHelper.setupPhoto(app, holder.icon, item.photoPath, R.drawable.img_user_picture_active, false) TelegramUiHelper.setupPhoto(app, holder.icon, item.photoPath, R.drawable.img_user_picture_active, false)
} }
holder.title?.text = item.getVisibleName() holder.title?.text = if (sortByGroup) item.getVisibleName() else item.name
openOnMapView?.isEnabled = canBeOpenedOnMap openOnMapView?.isEnabled = canBeOpenedOnMap
if (canBeOpenedOnMap) { if (canBeOpenedOnMap) {
openOnMapView?.setOnClickListener { openOnMapView?.setOnClickListener {
@ -374,15 +454,26 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
if (item is ChatItem && holder is ChatViewHolder) { if (item is ChatItem && holder is ChatViewHolder) {
val nextIsLocation = !lastItem && items[position + 1] is LocationItem val nextIsLocation = !lastItem && (items[position + 1] is LocationItem || !sortByGroup)
val chatId = item.chatId val chatId = item.chatId
val stateTextInd = if (settings.isShowingChatOnMap(chatId)) 1 else 0 val stateTextInd = if (settings.isShowingChatOnMap(chatId)) 1 else 0
val groupDescrRowVisible = !sortByGroup
&& (!item.privateChat || item.chatWithBot)
if (groupDescrRowVisible) {
holder.groupDescrContainer?.visibility = View.VISIBLE
holder.groupTitle?.text = item.getVisibleName()
TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false)
} else {
holder.groupDescrContainer?.visibility = View.GONE
}
holder.description?.text = getChatItemDescription(item) holder.description?.text = getChatItemDescription(item)
holder.imageButton?.visibility = View.GONE holder.imageButton?.visibility = View.GONE
holder.showOnMapRow?.setOnClickListener { showPopupMenu(holder, chatId) } holder.showOnMapRow?.setOnClickListener { showPopupMenu(holder, chatId) }
holder.showOnMapState?.text = menuList[stateTextInd] holder.showOnMapState?.text = menuList[stateTextInd]
holder.bottomDivider?.visibility = if (nextIsLocation) View.VISIBLE else View.GONE holder.bottomDivider?.visibility = if (nextIsLocation) View.VISIBLE else View.GONE
holder.topDivider?.visibility = if (!sortByGroup && position != 0) View.GONE else View.VISIBLE
} else if (item is LocationItem && holder is ContactViewHolder) { } else if (item is LocationItem && holder is ContactViewHolder) {
holder.description?.text = OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr) holder.description?.text = OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr)
} }
@ -392,13 +483,23 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
private fun getChatItemDescription(item: ChatItem): String { private fun getChatItemDescription(item: ChatItem): String {
return when { return when {
item.chatWithBot -> getString(R.string.shared_string_bot) item.chatWithBot -> {
item.privateChat -> { OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr) } if (settings.liveNowSortType.isSortByGroup()) {
getString(R.string.shared_string_bot)
} else {
OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr)
}
}
item.privateChat -> OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr)
else -> { else -> {
val live = getString(R.string.shared_string_live) if (settings.liveNowSortType.isSortByGroup()) {
val all = getString(R.string.shared_string_all) val live = getString(R.string.shared_string_live)
val liveStr = "$live ${item.liveMembersCount}" val all = getString(R.string.shared_string_all)
if (item.membersCount > 0) "$liveStr$all ${item.membersCount}" else liveStr val liveStr = "$live ${item.liveMembersCount}"
if (item.membersCount > 0) "$liveStr$all ${item.membersCount}" else liveStr
} else {
OsmandFormatter.getListItemLiveTimeDescr(app, item.lastUpdated, lastResponseStr)
}
} }
} }
} }
@ -463,9 +564,13 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
inner class ChatViewHolder(view: View) : BaseViewHolder(view) { inner class ChatViewHolder(view: View) : BaseViewHolder(view) {
val userRow: View? = view.findViewById(R.id.user_row) val userRow: View? = view.findViewById(R.id.user_row)
val groupDescrContainer: View? = view.findViewById(R.id.group_container)
val groupImage: ImageView? = view.findViewById(R.id.group_icon)
val groupTitle: TextView? = view.findViewById(R.id.group_title)
val imageButton: ImageView? = view.findViewById(R.id.image_button) val imageButton: ImageView? = view.findViewById(R.id.image_button)
val showOnMapRow: View? = view.findViewById(R.id.show_on_map_row) val showOnMapRow: View? = view.findViewById(R.id.show_on_map_row)
val showOnMapState: TextView? = view.findViewById(R.id.show_on_map_state) val showOnMapState: TextView? = view.findViewById(R.id.show_on_map_state)
val topDivider: View? = view.findViewById(R.id.top_divider)
val bottomDivider: View? = view.findViewById(R.id.bottom_divider) val bottomDivider: View? = view.findViewById(R.id.bottom_divider)
override fun getOpenOnMapClickView() = userRow override fun getOpenOnMapClickView() = userRow

View file

@ -0,0 +1,104 @@
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.support.v4.content.ContextCompat
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
class SortByBottomSheet : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?) = BottomSheetDialog(context!!)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val app = activity?.application as TelegramApplication
val mainView = inflater.inflate(R.layout.bottom_sheet_sort_by, 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)
for (sortType in TelegramSettings.LiveNowSortType.values()) {
inflater.inflate(R.layout.item_with_rb_and_btn, itemsCont, false).apply {
val currentType = sortType == app.settings.liveNowSortType
val image = if (currentType) {
app.uiUtils.getActiveIcon(sortType.iconId)
} else {
app.uiUtils.getThemedIcon(sortType.iconId)
}
findViewById<ImageView>(R.id.icon).setImageDrawable(image)
findViewById<TextView>(R.id.title)?.apply {
text = getText(sortType.titleId)
val colorId = if (currentType) R.color.ctrl_active_light else R.color.primary_text_light
setTextColor(ContextCompat.getColor(app, colorId))
}
findViewById<View>(R.id.primary_btn_container).visibility = View.GONE
findViewById<View>(R.id.radio_button).visibility = View.GONE
setOnClickListener {
app.settings.liveNowSortType = sortType
targetFragment?.also { target ->
target.onActivityResult(targetRequestCode, SORTING_CHANGED_REQUEST_CODE, null)
}
dismiss()
}
itemsCont.addView(this)
}
}
mainView.findViewById<TextView>(R.id.secondary_btn).apply {
setText(R.string.shared_string_cancel)
setOnClickListener { dismiss() }
}
return mainView
}
companion object {
const val SORTING_CHANGED_REQUEST_CODE = 3
private const val TAG = "SortByBottomSheet"
fun showInstance(
fm: FragmentManager,
target: Fragment
): Boolean {
return try {
SortByBottomSheet().apply {
setTargetFragment(target, SORTING_CHANGED_REQUEST_CODE)
show(fm, TAG)
}
true
} catch (e: RuntimeException) {
false
}
}
}
}