Search initial commit
This commit is contained in:
parent
688d193803
commit
fca39521cc
15 changed files with 946 additions and 74 deletions
21
OsmAnd-telegram/res/drawable/gradient_both_sides_light.xml
Normal file
21
OsmAnd-telegram/res/drawable/gradient_both_sides_light.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="0"
|
||||||
|
android:centerColor="#00ffffff"
|
||||||
|
android:centerX="0.2"
|
||||||
|
android:startColor="@color/card_bg_light" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="180"
|
||||||
|
android:centerColor="#00ffffff"
|
||||||
|
android:centerX="0.2"
|
||||||
|
android:startColor="@color/card_bg_light" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
|
@ -28,34 +28,10 @@
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<include
|
||||||
android:id="@+id/buttons_bar"
|
layout="@layout/bottom_buttons_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/buttons_bottom_bar_height"
|
android:layout_height="@dimen/buttons_bottom_bar_height" />
|
||||||
android:background="?attr/card_bg_color"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingLeft="@dimen/content_padding_half"
|
|
||||||
android:paddingRight="@dimen/content_padding_half"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<include
|
|
||||||
layout="@layout/secondary_btn"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="@dimen/content_padding_half"
|
|
||||||
android:layout_height="match_parent"/>
|
|
||||||
|
|
||||||
<include
|
|
||||||
layout="@layout/primary_btn"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.BottomNavigationView
|
<android.support.design.widget.BottomNavigationView
|
||||||
android:id="@+id/bottom_navigation"
|
android:id="@+id/bottom_navigation"
|
||||||
|
|
30
OsmAnd-telegram/res/layout/bottom_buttons_bar.xml
Normal file
30
OsmAnd-telegram/res/layout/bottom_buttons_bar.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/buttons_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/buttons_bottom_bar_height"
|
||||||
|
android:background="?attr/card_bg_color"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="@dimen/content_padding_half"
|
||||||
|
android:paddingRight="@dimen/content_padding_half"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/secondary_btn"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="@dimen/content_padding_half"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/primary_btn"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
67
OsmAnd-telegram/res/layout/empty_state_search.xml
Normal file
67
OsmAnd-telegram/res/layout/empty_state_search.xml
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:osmand="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/empty_state_image_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/my_location_image_height"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:foreground="@drawable/gradient_both_sides_light">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/empty_state_background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/img_my_location_roadbg" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="@dimen/content_padding_standard">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/empty_state_placeholder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/img_my_location_user" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/empty_state_user_icon"
|
||||||
|
android:layout_width="@dimen/my_location_user_icon_size"
|
||||||
|
android:layout_height="@dimen/my_location_user_icon_size"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/img_user_placeholder" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<net.osmand.telegram.ui.views.TextViewEx
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/content_padding_half"
|
||||||
|
android:background="@null"
|
||||||
|
android:text="@string/search_contacts"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
osmand:typeface="@string/font_roboto_medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="68dp"
|
||||||
|
android:layout_marginRight="68dp"
|
||||||
|
android:background="@null"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/search_contacts_descr"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -101,49 +101,6 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/search_box"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="@dimen/content_padding_half"
|
|
||||||
android:layout_marginRight="@dimen/content_padding_half"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:background="@drawable/btn_round">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/search_button"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/search_box_height"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/search_hint"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="@dimen/content_padding_standard"
|
|
||||||
android:layout_marginRight="@dimen/content_padding_standard"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/my_location_search_hint"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="@dimen/descr_text_size"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/search_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="@dimen/content_padding_half"
|
|
||||||
android:layout_marginRight="@dimen/content_padding_half"
|
|
||||||
tools:src="@drawable/ic_action_search_dark"
|
|
||||||
tools:tint="@color/icon_light"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/title_container"
|
android:id="@+id/title_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -296,6 +253,48 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/search_box"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/content_padding_half"
|
||||||
|
android:layout_marginRight="@dimen/content_padding_half"
|
||||||
|
tools:background="@drawable/btn_round">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/search_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/search_box_height"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/search_hint"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/content_padding_standard"
|
||||||
|
android:layout_marginRight="@dimen/content_padding_standard"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/my_location_search_hint"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="@dimen/descr_text_size"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/search_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/content_padding_half"
|
||||||
|
android:layout_marginRight="@dimen/content_padding_half"
|
||||||
|
tools:src="@drawable/ic_action_search_dark"
|
||||||
|
tools:tint="@color/icon_light"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
|
92
OsmAnd-telegram/res/layout/fragment_search_dialog.xml
Normal file
92
OsmAnd-telegram/res/layout/fragment_search_dialog.xml
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/card_bg_color">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/action_bar_height">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/title_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/searchEditText"
|
||||||
|
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:background="@null"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:hint="@string/type_contact_or_group_name"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/app_bar_title_light"
|
||||||
|
android:textSize="@dimen/title_text_size" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/search_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/shared_string_search"
|
||||||
|
android:padding="@dimen/content_padding_standard"
|
||||||
|
android:src="@drawable/ic_action_search_dark"
|
||||||
|
android:tint="@color/icon_light" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.Toolbar>
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<net.osmand.telegram.ui.views.EmptyStateRecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="@dimen/buttons_bottom_bar_height" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
layout="@layout/empty_state_search" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:src="?attr/bottom_nav_shadow" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/bottom_buttons_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/buttons_bottom_bar_height"
|
||||||
|
android:layout_gravity="bottom" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -7,6 +7,10 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/top_divider"
|
||||||
|
layout="@layout/list_item_divider"/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="search_contacts">Search contacts</string>
|
||||||
|
<string name="search_contacts_descr">Search across all of your groups and contacts.</string>
|
||||||
|
<string name="type_contact_or_group_name">Type contact or group name</string>
|
||||||
|
<string name="shared_string_search">Search</string>
|
||||||
<string name="shared_string_ok">OK</string>
|
<string name="shared_string_ok">OK</string>
|
||||||
<string name="timeline_available_for_free_now">Timeline is a feature available now for free.</string>
|
<string name="timeline_available_for_free_now">Timeline is a feature available now for free.</string>
|
||||||
<string name="disable_monitoring">Disable monitoring</string>
|
<string name="disable_monitoring">Disable monitoring</string>
|
||||||
|
|
|
@ -35,6 +35,8 @@ class TelegramHelper private constructor() {
|
||||||
private const val IGNORED_ERROR_CODE = 406
|
private const val IGNORED_ERROR_CODE = 406
|
||||||
private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5
|
private const val MESSAGE_CANNOT_BE_EDITED_ERROR_CODE = 5
|
||||||
|
|
||||||
|
private const val MAX_SEARCH_ITEMS = 100
|
||||||
|
|
||||||
// min and max values for the Telegram API
|
// min and max values for the Telegram API
|
||||||
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
|
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
|
||||||
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day
|
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day
|
||||||
|
@ -98,6 +100,7 @@ class TelegramHelper private constructor() {
|
||||||
private val incomingMessagesListeners = HashSet<TelegramIncomingMessagesListener>()
|
private val incomingMessagesListeners = HashSet<TelegramIncomingMessagesListener>()
|
||||||
private val outgoingMessagesListeners = HashSet<TelegramOutgoingMessagesListener>()
|
private val outgoingMessagesListeners = HashSet<TelegramOutgoingMessagesListener>()
|
||||||
private val fullInfoUpdatesListeners = HashSet<FullInfoUpdatesListener>()
|
private val fullInfoUpdatesListeners = HashSet<FullInfoUpdatesListener>()
|
||||||
|
private val searchListeners = HashSet<TelegramSearchListener>()
|
||||||
|
|
||||||
fun addIncomingMessagesListener(listener: TelegramIncomingMessagesListener) {
|
fun addIncomingMessagesListener(listener: TelegramIncomingMessagesListener) {
|
||||||
incomingMessagesListeners.add(listener)
|
incomingMessagesListeners.add(listener)
|
||||||
|
@ -123,6 +126,14 @@ class TelegramHelper private constructor() {
|
||||||
fullInfoUpdatesListeners.remove(listener)
|
fullInfoUpdatesListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addSearchListener(listener: TelegramSearchListener) {
|
||||||
|
searchListeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSearchListener(listener: TelegramSearchListener) {
|
||||||
|
searchListeners.remove(listener)
|
||||||
|
}
|
||||||
|
|
||||||
fun getChatList(): TreeSet<OrderedChat> {
|
fun getChatList(): TreeSet<OrderedChat> {
|
||||||
synchronized(chatList) {
|
synchronized(chatList) {
|
||||||
return TreeSet(chatList.filter { !it.isChannel })
|
return TreeSet(chatList.filter { !it.isChannel })
|
||||||
|
@ -193,7 +204,7 @@ class TelegramHelper private constructor() {
|
||||||
|
|
||||||
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
|
fun isSecretChat(chat: TdApi.Chat): Boolean = chat.type is TdApi.ChatTypeSecret
|
||||||
|
|
||||||
private fun isChannel(chat: TdApi.Chat): Boolean {
|
fun isChannel(chat: TdApi.Chat): Boolean {
|
||||||
return chat.type is TdApi.ChatTypeSupergroup && (chat.type as TdApi.ChatTypeSupergroup).isChannel
|
return chat.type is TdApi.ChatTypeSupergroup && (chat.type as TdApi.ChatTypeSupergroup).isChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +260,12 @@ class TelegramHelper private constructor() {
|
||||||
fun onTelegramAuthorizationRequestError(code: Int, message: String)
|
fun onTelegramAuthorizationRequestError(code: Int, message: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TelegramSearchListener {
|
||||||
|
fun onSearchChatsFinished(obj: TdApi.Chats)
|
||||||
|
fun onSearchPublicChatsFinished(obj: TdApi.Chats)
|
||||||
|
fun onSearchContactsFinished(obj: TdApi.Users)
|
||||||
|
}
|
||||||
|
|
||||||
inner class TelegramAuthorizationRequestHandler(val telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener) {
|
inner class TelegramAuthorizationRequestHandler(val telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener) {
|
||||||
|
|
||||||
fun applyAuthenticationParameter(parameterType: TelegramAuthenticationParameterType, parameterValue: String) {
|
fun applyAuthenticationParameter(parameterType: TelegramAuthenticationParameterType, parameterValue: String) {
|
||||||
|
@ -628,7 +645,7 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestUser(id: Int) {
|
fun requestUser(id: Int) {
|
||||||
client?.send(TdApi.GetUser(id)) { obj ->
|
client?.send(TdApi.GetUser(id)) { obj ->
|
||||||
when (obj.constructor) {
|
when (obj.constructor) {
|
||||||
TdApi.Error.CONSTRUCTOR -> {
|
TdApi.Error.CONSTRUCTOR -> {
|
||||||
|
@ -648,6 +665,24 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestChat(id: Long) {
|
||||||
|
client?.send(TdApi.GetChat(id)) { obj ->
|
||||||
|
when (obj.constructor) {
|
||||||
|
TdApi.Error.CONSTRUCTOR -> {
|
||||||
|
val error = obj as TdApi.Error
|
||||||
|
if (error.code != IGNORED_ERROR_CODE) {
|
||||||
|
listener?.onTelegramError(error.code, error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TdApi.Chat.CONSTRUCTOR -> {
|
||||||
|
val chat = obj as TdApi.Chat
|
||||||
|
chats[chat.id] = chat
|
||||||
|
listener?.onTelegramChatChanged(chat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createPrivateChatWithUser(
|
fun createPrivateChatWithUser(
|
||||||
userId: Int,
|
userId: Int,
|
||||||
shareInfo: TelegramSettings.ShareChatInfo,
|
shareInfo: TelegramSettings.ShareChatInfo,
|
||||||
|
@ -965,6 +1000,53 @@ class TelegramHelper private constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun searchChats(searchTerm: String) {
|
||||||
|
client?.send(TdApi.SearchChats(searchTerm, MAX_SEARCH_ITEMS)) { obj ->
|
||||||
|
checkChatsAndUsersSearch(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchChatsOnServer(searchTerm: String) {
|
||||||
|
client?.send(TdApi.SearchChatsOnServer(searchTerm, MAX_SEARCH_ITEMS)) { obj ->
|
||||||
|
checkChatsAndUsersSearch(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchPublicChats(searchTerm: String) {
|
||||||
|
client?.send(TdApi.SearchPublicChats(searchTerm)) { obj ->
|
||||||
|
checkChatsAndUsersSearch(obj, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchContacts(searchTerm: String) {
|
||||||
|
client?.send(TdApi.SearchContacts(searchTerm, MAX_SEARCH_ITEMS)) { obj ->
|
||||||
|
checkChatsAndUsersSearch(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkChatsAndUsersSearch(obj: TdApi.Object, publicChats: Boolean = false) {
|
||||||
|
when (obj.constructor) {
|
||||||
|
TdApi.Error.CONSTRUCTOR -> {
|
||||||
|
val error = obj as TdApi.Error
|
||||||
|
if (error.code != IGNORED_ERROR_CODE) {
|
||||||
|
listener?.onTelegramError(error.code, error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TdApi.Chats.CONSTRUCTOR -> {
|
||||||
|
val chats = obj as TdApi.Chats
|
||||||
|
if (publicChats) {
|
||||||
|
searchListeners.forEach { it.onSearchPublicChatsFinished(chats) }
|
||||||
|
} else {
|
||||||
|
searchListeners.forEach { it.onSearchChatsFinished(chats) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TdApi.Users.CONSTRUCTOR -> {
|
||||||
|
val users = obj as TdApi.Users
|
||||||
|
searchListeners.forEach { it.onSearchContactsFinished(users) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun logout(): Boolean {
|
fun logout(): Boolean {
|
||||||
return if (libraryLoaded) {
|
return if (libraryLoaded) {
|
||||||
haveAuthorization = false
|
haveAuthorization = false
|
||||||
|
|
|
@ -510,6 +510,7 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
||||||
holder.topDivider?.visibility = if (!sortByGroup && position != 0) View.GONE else View.VISIBLE
|
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)
|
||||||
|
holder.topShadowDivider?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,6 +602,7 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
||||||
val description: TextView? = view.findViewById(R.id.description)
|
val description: TextView? = view.findViewById(R.id.description)
|
||||||
val receivedGpxPointsContainer: View? = view.findViewById(R.id.received_gps_points_container)
|
val receivedGpxPointsContainer: View? = view.findViewById(R.id.received_gps_points_container)
|
||||||
val receivedGpxPointsDescr: TextView? = view.findViewById(R.id.received_gps_points_description)
|
val receivedGpxPointsDescr: TextView? = view.findViewById(R.id.received_gps_points_description)
|
||||||
|
val topShadowDivider: View? = view.findViewById(R.id.top_divider)
|
||||||
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||||
val lastTelegramUpdateTime: TextView? = view.findViewById(R.id.last_telegram_update_time)
|
val lastTelegramUpdateTime: TextView? = view.findViewById(R.id.last_telegram_update_time)
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
appBarCollapsed = collapsed
|
appBarCollapsed = collapsed
|
||||||
adjustText()
|
adjustText()
|
||||||
adjustAppbar()
|
adjustAppbar()
|
||||||
|
adjustSearchBox()
|
||||||
optionsBtn.visibility = if (collapsed) View.VISIBLE else View.GONE
|
optionsBtn.visibility = if (collapsed) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -183,7 +184,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
setBackgroundDrawable(searchBoxBg)
|
setBackgroundDrawable(searchBoxBg)
|
||||||
}
|
}
|
||||||
findViewById<View>(R.id.search_button).setOnClickListener {
|
findViewById<View>(R.id.search_button).setOnClickListener {
|
||||||
Toast.makeText(context, "Search", Toast.LENGTH_SHORT).show()
|
activity.supportFragmentManager?.also { SearchDialogFragment.showInstance(it, this@MyLocationTabFragment) }
|
||||||
}
|
}
|
||||||
findViewById<ImageView>(R.id.search_icon)
|
findViewById<ImageView>(R.id.search_icon)
|
||||||
.setImageDrawable(app.uiUtils.getThemedIcon(R.drawable.ic_action_search_dark))
|
.setImageDrawable(app.uiUtils.getThemedIcon(R.drawable.ic_action_search_dark))
|
||||||
|
@ -635,6 +636,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
holder.topShadowDivider?.visibility = View.GONE
|
||||||
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
if (live) {
|
if (live) {
|
||||||
|
@ -754,6 +756,7 @@ class MyLocationTabFragment : Fragment(), TelegramListener {
|
||||||
|
|
||||||
inner class ChatViewHolder(val view: View) : BaseViewHolder(view) {
|
inner class ChatViewHolder(val view: View) : BaseViewHolder(view) {
|
||||||
val checkBox: CheckBox? = view.findViewById(R.id.check_box)
|
val checkBox: CheckBox? = view.findViewById(R.id.check_box)
|
||||||
|
val topShadowDivider: View? = view.findViewById(R.id.top_divider)
|
||||||
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,527 @@
|
||||||
|
package net.osmand.telegram.ui
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.ColorMatrix
|
||||||
|
import android.graphics.ColorMatrixColorFilter
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.v4.app.Fragment
|
||||||
|
import android.support.v4.app.FragmentManager
|
||||||
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.support.v7.widget.Toolbar
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.*
|
||||||
|
import net.osmand.Location
|
||||||
|
import net.osmand.PlatformUtil
|
||||||
|
import net.osmand.data.LatLon
|
||||||
|
import net.osmand.telegram.R
|
||||||
|
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
||||||
|
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
|
||||||
|
import net.osmand.telegram.helpers.TelegramHelper
|
||||||
|
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||||
|
import net.osmand.telegram.ui.views.EmptyStateRecyclerView
|
||||||
|
import net.osmand.telegram.utils.AndroidUtils
|
||||||
|
import net.osmand.telegram.utils.OsmandFormatter
|
||||||
|
import net.osmand.telegram.utils.OsmandLocationUtils
|
||||||
|
import net.osmand.telegram.utils.UiUtils
|
||||||
|
import net.osmand.util.MapUtils
|
||||||
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
|
|
||||||
|
class SearchDialogFragment : BaseDialogFragment(), TelegramHelper.TelegramSearchListener,
|
||||||
|
TelegramLocationListener, TelegramCompassListener {
|
||||||
|
|
||||||
|
private val log = PlatformUtil.getLog(SearchDialogFragment::class.java)
|
||||||
|
|
||||||
|
private val uiUtils get() = app.uiUtils
|
||||||
|
|
||||||
|
private val adapter = SearchAdapter()
|
||||||
|
|
||||||
|
private lateinit var locationViewCache: UiUtils.UpdateLocationViewCache
|
||||||
|
|
||||||
|
private lateinit var searchBox: EditText
|
||||||
|
private lateinit var buttonsBar: LinearLayout
|
||||||
|
|
||||||
|
private val searchedChatsIds = mutableSetOf<Long>()
|
||||||
|
private val searchedPublicChatsIds = mutableSetOf<Long>()
|
||||||
|
private val searchedContactsIds = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
private val selectedChats = HashSet<Long>()
|
||||||
|
private val selectedUsers = HashSet<Long>()
|
||||||
|
|
||||||
|
private var searchQuery: String = ""
|
||||||
|
|
||||||
|
private var location: Location? = null
|
||||||
|
private var heading: Float? = null
|
||||||
|
private var locationUiUpdateAllowed: Boolean = true
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
val mainView = inflater.inflate(R.layout.fragment_search_dialog, parent)
|
||||||
|
|
||||||
|
val appBarLayout = mainView.findViewById<View>(R.id.app_bar_layout)
|
||||||
|
AndroidUtils.addStatusBarPadding19v(context!!, appBarLayout)
|
||||||
|
|
||||||
|
mainView.findViewById<Toolbar>(R.id.toolbar).apply {
|
||||||
|
navigationIcon = uiUtils.getThemedIcon(R.drawable.ic_arrow_back)
|
||||||
|
setNavigationOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
searchBox = mainView.findViewById<EditText>(R.id.searchEditText).apply {
|
||||||
|
addTextChangedListener(object : TextWatcher {
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
val newQueryText = s.toString()
|
||||||
|
if (!searchQuery.equals(newQueryText, true)) {
|
||||||
|
searchQuery = newQueryText
|
||||||
|
clearSearchedItems()
|
||||||
|
if (searchQuery.isNotBlank()) {
|
||||||
|
runSearch()
|
||||||
|
} else {
|
||||||
|
updateList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mainView.findViewById<ImageView>(R.id.search_icon).setOnClickListener {
|
||||||
|
runSearch()
|
||||||
|
}
|
||||||
|
val emptyView = mainView.findViewById<LinearLayout>(R.id.empty_view)
|
||||||
|
mainView.findViewById<EmptyStateRecyclerView>(R.id.recycler_view).apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = this@SearchDialogFragment.adapter
|
||||||
|
setEmptyView(emptyView)
|
||||||
|
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
super.onScrollStateChanged(recyclerView, newState)
|
||||||
|
locationUiUpdateAllowed = newState == RecyclerView.SCROLL_STATE_IDLE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
val matrix = ColorMatrix()
|
||||||
|
matrix.setSaturation(0f)
|
||||||
|
val filter = ColorMatrixColorFilter(matrix)
|
||||||
|
|
||||||
|
mainView.findViewById<ImageView>(R.id.empty_state_background).colorFilter = filter
|
||||||
|
mainView.findViewById<ImageView>(R.id.empty_state_placeholder).colorFilter = filter
|
||||||
|
|
||||||
|
buttonsBar = mainView.findViewById<LinearLayout>(R.id.buttons_bar).apply {
|
||||||
|
findViewById<TextView>(R.id.primary_btn).apply {
|
||||||
|
text = getString(R.string.shared_string_continue)
|
||||||
|
setOnClickListener {
|
||||||
|
onPrimaryBtnClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
findViewById<TextView>(R.id.secondary_btn).apply {
|
||||||
|
text = getString(R.string.shared_string_cancel)
|
||||||
|
setOnClickListener {
|
||||||
|
onSecondaryBtnClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainView
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearSearchedItems() {
|
||||||
|
searchedChatsIds.clear()
|
||||||
|
searchedPublicChatsIds.clear()
|
||||||
|
searchedContactsIds.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runSearch() {
|
||||||
|
if (searchQuery.isNotBlank()) {
|
||||||
|
runSearch(searchQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runSearch(text: String) {
|
||||||
|
telegramHelper.searchChats(text)
|
||||||
|
telegramHelper.searchChatsOnServer(text)
|
||||||
|
telegramHelper.searchContacts(text)
|
||||||
|
if (text.length > 4) {
|
||||||
|
telegramHelper.searchPublicChats(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
telegramHelper.addSearchListener(this)
|
||||||
|
locationViewCache = app.uiUtils.getUpdateLocationViewCache()
|
||||||
|
startLocationUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
telegramHelper.removeSearchListener(this)
|
||||||
|
stopLocationUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateLocation(location: Location?) {
|
||||||
|
val loc = this.location
|
||||||
|
val newLocation = loc == null && location != null
|
||||||
|
val locationChanged = loc != null && location != null
|
||||||
|
&& loc.latitude != location.latitude
|
||||||
|
&& loc.longitude != location.longitude
|
||||||
|
if (newLocation || locationChanged) {
|
||||||
|
this.location = location
|
||||||
|
updateLocationUi()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateCompassValue(value: Float) {
|
||||||
|
// 99 in next line used to one-time initialize arrows (with reference vs. fixed-north direction)
|
||||||
|
// on non-compass devices
|
||||||
|
val lastHeading = heading ?: 99f
|
||||||
|
heading = value
|
||||||
|
if (Math.abs(MapUtils.degreesDiff(lastHeading.toDouble(), value.toDouble())) > 5) {
|
||||||
|
updateLocationUi()
|
||||||
|
} else {
|
||||||
|
heading = lastHeading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLocationUpdate() {
|
||||||
|
app.locationProvider.addLocationListener(this)
|
||||||
|
app.locationProvider.addCompassListener(this)
|
||||||
|
updateLocationUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLocationUpdate() {
|
||||||
|
app.locationProvider.removeLocationListener(this)
|
||||||
|
app.locationProvider.removeCompassListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateLocationUi() {
|
||||||
|
if (locationUiUpdateAllowed) {
|
||||||
|
app.runInUIThread { updateList() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList() {
|
||||||
|
val items: MutableList<TdApi.Object> = mutableListOf()
|
||||||
|
val currentUserId = telegramHelper.getCurrentUserId()
|
||||||
|
|
||||||
|
val chats: MutableList<TdApi.Chat> = getChats(currentUserId)
|
||||||
|
items.addAll(chats)
|
||||||
|
|
||||||
|
val users: MutableList<TdApi.User> = getContacts(currentUserId, chats)
|
||||||
|
items.addAll(sortUsers(users))
|
||||||
|
|
||||||
|
items.addAll(getChats(currentUserId, chats))
|
||||||
|
|
||||||
|
adapter.items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getChats(
|
||||||
|
currentUserId: Int,
|
||||||
|
addedChats: List<TdApi.Chat>? = null
|
||||||
|
): MutableList<TdApi.Chat> {
|
||||||
|
val chats: MutableList<TdApi.Chat> = mutableListOf()
|
||||||
|
searchedChatsIds.forEach {
|
||||||
|
val chat = telegramHelper.getChat(it)
|
||||||
|
if (chat != null) {
|
||||||
|
if (!telegramHelper.isChannel(chat)
|
||||||
|
&& telegramHelper.getUserIdFromChatType(chat.type) != currentUserId
|
||||||
|
&& (addedChats == null || (!addedChats.contains(chat)))
|
||||||
|
) {
|
||||||
|
chats.add(chat)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
telegramHelper.requestChat(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chats
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContacts(currentUserId: Int, chats: List<TdApi.Chat>): MutableList<TdApi.User> {
|
||||||
|
val users: MutableList<TdApi.User> = mutableListOf()
|
||||||
|
searchedContactsIds.forEach { userId ->
|
||||||
|
val user = telegramHelper.getUser(userId)
|
||||||
|
if (user != null) {
|
||||||
|
if (user.id != currentUserId && !chats.any { telegramHelper.getUserIdFromChatType(it.type) == user.id })
|
||||||
|
users.add(user)
|
||||||
|
} else {
|
||||||
|
telegramHelper.requestUser(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sortUsers(list: MutableList<TdApi.User>): MutableList<TdApi.User> {
|
||||||
|
list.sortWith(Comparator { o1, o2 ->
|
||||||
|
val title1 = TelegramUiHelper.getUserName(o1)
|
||||||
|
val title2 = TelegramUiHelper.getUserName(o2)
|
||||||
|
title1.compareTo(title2)
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSearchContactsFinished(obj: TdApi.Users) {
|
||||||
|
log.debug("searchContactsFinished $obj")
|
||||||
|
val ids = obj.userIds
|
||||||
|
if (ids.isNotEmpty()) {
|
||||||
|
searchedContactsIds.addAll(ids.toList())
|
||||||
|
app.runInUIThread { updateList() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSearchChatsFinished(obj: TdApi.Chats) {
|
||||||
|
log.debug("searchChatsFinished $obj")
|
||||||
|
val ids = obj.chatIds
|
||||||
|
if (ids.isNotEmpty()) {
|
||||||
|
searchedChatsIds.addAll(ids.toList())
|
||||||
|
app.runInUIThread { updateList() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSearchPublicChatsFinished(obj: TdApi.Chats) {
|
||||||
|
log.debug("onSearchPublicChatsFinished $obj")
|
||||||
|
val ids = obj.chatIds
|
||||||
|
if (ids.isNotEmpty()) {
|
||||||
|
searchedPublicChatsIds.addAll(ids.toList())
|
||||||
|
app.runInUIThread { updateList() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
LogoutBottomSheet.LOGOUT_REQUEST_CODE -> {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
SetTimeDialogFragment.LOCATION_SHARED_REQUEST_CODE -> {
|
||||||
|
if (resultCode == SetTimeDialogFragment.LOCATION_SHARED_REQUEST_CODE) {
|
||||||
|
targetFragment?.also {
|
||||||
|
it.onActivityResult(targetRequestCode, resultCode, null)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class SearchAdapter : RecyclerView.Adapter<SearchAdapter.ChatViewHolder>() {
|
||||||
|
|
||||||
|
var items = mutableListOf<TdApi.Object>()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
val isChat = item is TdApi.Chat
|
||||||
|
val itemId = if (isChat) {
|
||||||
|
(item as TdApi.Chat).id
|
||||||
|
} else {
|
||||||
|
(item as TdApi.User).id.toLong()
|
||||||
|
}
|
||||||
|
val latLon = getItemLastLocation(item)
|
||||||
|
val lastUpdate = getLastUpdateTime(item)
|
||||||
|
|
||||||
|
val lastItem = position == itemCount - 1
|
||||||
|
val placeholderId = if (isChat && telegramHelper.isGroup(item as TdApi.Chat)) R.drawable.img_group_picture else R.drawable.img_user_picture
|
||||||
|
val live = (isChat && settings.isSharingLocationToChat(itemId))
|
||||||
|
val shareInfo = if (isChat) settings.getChatsShareInfo()[itemId] else null
|
||||||
|
|
||||||
|
val photoPath = when (item) {
|
||||||
|
is TdApi.Chat -> item.photo?.small?.local?.path
|
||||||
|
is TdApi.User -> item.profilePhoto?.small?.local?.path
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
TelegramUiHelper.setupPhoto(app, holder.icon, photoPath, placeholderId, false)
|
||||||
|
|
||||||
|
val title = when (item) {
|
||||||
|
is TdApi.Chat -> item.title
|
||||||
|
is TdApi.User -> TelegramUiHelper.getUserName(item)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.title?.text = title
|
||||||
|
|
||||||
|
holder.checkBox?.apply {
|
||||||
|
visibility = if (live) View.GONE else View.VISIBLE
|
||||||
|
setOnCheckedChangeListener(null)
|
||||||
|
isChecked = if (isChat) {
|
||||||
|
selectedChats.contains(itemId)
|
||||||
|
} else {
|
||||||
|
selectedUsers.contains(itemId)
|
||||||
|
}
|
||||||
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (isChecked) {
|
||||||
|
if (isChat) {
|
||||||
|
selectedChats.add(itemId)
|
||||||
|
} else {
|
||||||
|
selectedUsers.add(itemId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isChat) {
|
||||||
|
selectedChats.remove(itemId)
|
||||||
|
} else {
|
||||||
|
selectedUsers.remove(itemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switchButtonsVisibility(selectedChats.isNotEmpty() || selectedUsers.isNotEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder.topShadowDivider?.visibility = if (position == 0) View.VISIBLE else View.GONE
|
||||||
|
holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
if (!live) {
|
||||||
|
holder.checkBox?.apply {
|
||||||
|
isChecked = !isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location != null && latLon != null && lastUpdate != null) {
|
||||||
|
val staleLocation = System.currentTimeMillis() / 1000 - lastUpdate > settings.staleLocTime
|
||||||
|
|
||||||
|
holder.locationViewContainer?.visibility = if (lastUpdate > 0) View.VISIBLE else View.GONE
|
||||||
|
locationViewCache.outdatedLocation = staleLocation
|
||||||
|
app.uiUtils.updateLocationView(holder.directionIcon, holder.distanceText, latLon, locationViewCache)
|
||||||
|
} else {
|
||||||
|
holder.locationViewContainer?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val expiresIn = shareInfo?.getChatLiveMessageExpireTime() ?: 0
|
||||||
|
holder.textInArea?.apply {
|
||||||
|
visibility = if (live) View.VISIBLE else View.GONE
|
||||||
|
text = OsmandFormatter.getFormattedDuration(app, expiresIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.description?.apply {
|
||||||
|
val description = getItemDescription(item, lastUpdate)
|
||||||
|
text = description
|
||||||
|
visibility = if (description != null) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItemLastMessage(item: TdApi.Object): TdApi.Message? {
|
||||||
|
when (item) {
|
||||||
|
is TdApi.User -> {
|
||||||
|
return telegramHelper.getUserMessage(item)
|
||||||
|
}
|
||||||
|
is TdApi.Chat -> {
|
||||||
|
return telegramHelper.getChatMessages(item.id).firstOrNull() ?: item.lastMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItemLastLocation(item: TdApi.Object): LatLon? {
|
||||||
|
val message = getItemLastMessage(item)
|
||||||
|
if (message != null && OsmandLocationUtils.getSenderMessageId(message) != telegramHelper.getCurrentUserId()) {
|
||||||
|
val messageLocation = OsmandLocationUtils.parseMessageContent(message, telegramHelper)
|
||||||
|
if (messageLocation != null) {
|
||||||
|
return LatLon(messageLocation.lat, messageLocation.lon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastUpdateTime(item: TdApi.Object): Int? {
|
||||||
|
val message = getItemLastMessage(item)
|
||||||
|
if (message != null && OsmandLocationUtils.getSenderMessageId(message) != telegramHelper.getCurrentUserId()) {
|
||||||
|
return OsmandLocationUtils.getLastUpdatedTime(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItemDescription(item: TdApi.Object, lastUpdateTime: Int?): String? {
|
||||||
|
if (lastUpdateTime != null) {
|
||||||
|
return OsmandFormatter.getListItemLiveTimeDescr(app, lastUpdateTime)
|
||||||
|
}
|
||||||
|
if (item is TdApi.Chat && telegramHelper.isGroup(item)) {
|
||||||
|
return getString(R.string.shared_string_group)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
inner class ChatViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
val icon: ImageView? = view.findViewById(R.id.icon)
|
||||||
|
val title: TextView? = view.findViewById(R.id.title)
|
||||||
|
val locationViewContainer: View? = view.findViewById(R.id.location_view_container)
|
||||||
|
val directionIcon: ImageView? = view.findViewById(R.id.direction_icon)
|
||||||
|
val distanceText: TextView? = view.findViewById(R.id.distance_text)
|
||||||
|
val description: TextView? = view.findViewById(R.id.description)
|
||||||
|
val checkBox: CheckBox? = view.findViewById(R.id.check_box)
|
||||||
|
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
||||||
|
val topShadowDivider: View? = view.findViewById(R.id.top_divider)
|
||||||
|
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPrimaryBtnClick() {
|
||||||
|
if (selectedChats.isNotEmpty() || selectedUsers.isNotEmpty()) {
|
||||||
|
fragmentManager?.also {
|
||||||
|
SetTimeDialogFragment.showInstance(it, selectedChats, selectedUsers, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSecondaryBtnClick() {
|
||||||
|
clearSelection()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
switchButtonsVisibility(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearSelection() {
|
||||||
|
selectedChats.clear()
|
||||||
|
selectedUsers.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun switchButtonsVisibility(visible: Boolean) {
|
||||||
|
val buttonsVisibility = if (visible) View.VISIBLE else View.GONE
|
||||||
|
if (buttonsBar.visibility != buttonsVisibility) {
|
||||||
|
buttonsBar.visibility = buttonsVisibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val TAG = "SearchDialogFragment"
|
||||||
|
|
||||||
|
private val SEARCH_ITEM = 1
|
||||||
|
|
||||||
|
fun showInstance(fm: FragmentManager, target: Fragment?): Boolean {
|
||||||
|
return try {
|
||||||
|
SearchDialogFragment().apply {
|
||||||
|
if (target != null) {
|
||||||
|
setTargetFragment(target, SetTimeDialogFragment.LOCATION_SHARED_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
show(fm, TAG)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,6 +79,9 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
||||||
view.findViewById<TextView>(R.id.secondary_btn).apply {
|
view.findViewById<TextView>(R.id.secondary_btn).apply {
|
||||||
text = getString(R.string.shared_string_back)
|
text = getString(R.string.shared_string_back)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
targetFragment?.also {
|
||||||
|
it.onActivityResult(targetRequestCode, LOCATION_SHARING_CANCELED_CODE, null)
|
||||||
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,6 +360,7 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
||||||
userLivePeriods[itemId]?.also { text = formatLivePeriod(it) }
|
userLivePeriods[itemId]?.also { text = formatLivePeriod(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
holder.topShadowDivider?.visibility = View.GONE
|
||||||
holder.bottomShadow?.visibility = View.GONE
|
holder.bottomShadow?.visibility = View.GONE
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
selectDuration(itemId, isChat)
|
selectDuration(itemId, isChat)
|
||||||
|
@ -373,6 +377,7 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
||||||
val locationViewContainer: View? = view.findViewById(R.id.location_view_container)
|
val locationViewContainer: View? = view.findViewById(R.id.location_view_container)
|
||||||
val description: TextView? = view.findViewById(R.id.description)
|
val description: TextView? = view.findViewById(R.id.description)
|
||||||
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
val textInArea: TextView? = view.findViewById(R.id.text_in_area)
|
||||||
|
val topShadowDivider: View? = view.findViewById(R.id.top_divider)
|
||||||
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
val bottomShadow: View? = view.findViewById(R.id.bottom_shadow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,6 +385,7 @@ class SetTimeDialogFragment : BaseDialogFragment(), TelegramLocationListener, Te
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val LOCATION_SHARED_REQUEST_CODE = 0
|
const val LOCATION_SHARED_REQUEST_CODE = 0
|
||||||
|
const val LOCATION_SHARING_CANCELED_CODE = 1
|
||||||
|
|
||||||
private const val TAG = "SetTimeDialogFragment"
|
private const val TAG = "SetTimeDialogFragment"
|
||||||
private const val CHATS_KEY = "chats_key"
|
private const val CHATS_KEY = "chats_key"
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package net.osmand.telegram.ui.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
class EmptyStateRecyclerView : RecyclerView {
|
||||||
|
|
||||||
|
private var emptyView: View? = null
|
||||||
|
|
||||||
|
private val emptyStateObserver = object : RecyclerView.AdapterDataObserver() {
|
||||||
|
override fun onChanged() {
|
||||||
|
checkIfEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||||
|
checkIfEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||||
|
checkIfEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context)
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
|
||||||
|
context,
|
||||||
|
attrs,
|
||||||
|
defStyle
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
|
||||||
|
val oldAdapter = getAdapter()
|
||||||
|
oldAdapter?.unregisterAdapterDataObserver(emptyStateObserver)
|
||||||
|
super.setAdapter(adapter)
|
||||||
|
adapter?.registerAdapterDataObserver(emptyStateObserver)
|
||||||
|
checkIfEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEmptyView(emptyView: View) {
|
||||||
|
this.emptyView = emptyView
|
||||||
|
checkIfEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIfEmpty() {
|
||||||
|
adapter?.apply {
|
||||||
|
val empty = itemCount == 0
|
||||||
|
visibility = if (empty) View.GONE else View.VISIBLE
|
||||||
|
emptyView?.visibility = if (empty) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -169,7 +169,10 @@ object OsmandLocationUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseTextLocation(text: TdApi.FormattedText, botLocation: Boolean): MessageLocation {
|
fun parseTextLocation(text: TdApi.FormattedText, botLocation: Boolean): MessageLocation? {
|
||||||
|
if (botLocation && !text.text.startsWith(DEVICE_PREFIX) || !botLocation && !text.text.startsWith(USER_TEXT_LOCATION_TITLE)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserLocation()
|
val res = if (botLocation) MessageOsmAndBotLocation() else MessageUserLocation()
|
||||||
res.type = LocationMessages.TYPE_TEXT
|
res.type = LocationMessages.TYPE_TEXT
|
||||||
var locationNA = false
|
var locationNA = false
|
||||||
|
|
Loading…
Reference in a new issue