Unverified Commit 5da48006 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge branch 'beta' into fix#1938

parents a9996101 aede1acb
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Rocket.Chat Android native application # Rocket.Chat Android native application
[![CircleCI](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop.svg?style=shield)](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop) [![Build Status](https://travis-ci.org/RocketChat/Rocket.Chat.Android.svg?branch=develop)](https://travis-ci.org/RocketChat/Rocket.Chat.Android) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a81156a8682e4649994270d3670c3c83)](https://www.codacy.com/app/matheusjardimb/Rocket.Chat.Android) [![CircleCI](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop.svg?style=shield)](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a81156a8682e4649994270d3670c3c83)](https://www.codacy.com/app/matheusjardimb/Rocket.Chat.Android)
## Get it from the stores ## Get it from the stores
...@@ -14,19 +14,21 @@ This repository contains all the code related to the Android native application ...@@ -14,19 +14,21 @@ This repository contains all the code related to the Android native application
## How to build ## How to build
- Make sure that you have the latest **gradle** and the **android plugin** versions installed. Go to `File > Project Structure > Project` and make sure that you have the latest versions installed. Refer [this](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle) to see the compatible versions. - Make sure that you have the latest **Gradle** and the **Android plugin** versions installed. Go to `File > Project Structure > Project` and make sure that you have the latest versions installed. Refer [this](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle) to see the compatible versions.
- Kotlin is already configured in the project. To check, go to `Tools > Kotlin > Configure Kotlin in project`. A message saying kotlin is already configured in the project pops up. You can update kotlin to the latest version by going to `Tools > Kotlin > Configure Kotlin updates` and download the latest version of kotlin. - Kotlin is already configured in the project. To check, go to `Tools > Kotlin > Configure Kotlin in project`. A message saying kotlin is already configured in the project pops up. You can update kotlin to the latest version by going to `Tools > Kotlin > Configure Kotlin updates` and download the latest version of kotlin.
### SDK Instructions ### SDK Instructions
- This version requires the [Kotlin SDK](https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK) for Rocket.Chat. Clone the Kotlin SDK in by running `git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git`. - This version requires the [Kotlin SDK](https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK) for Rocket.Chat. Clone the Kotlin SDK in by running `git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git`.
- First, a build is required for the SDK, so that required jar files are generated. Make sure that the android repository and the kotlin sdk have the same immediate parent directory. Change the current directory to `Rocket.Chat.Android/app` and run the `build-sdk.sh` which will result in creating of the required jar file `core*.jar` and `common*.jar` in `Rocket.Chat.Android/app/libs`,by the following steps in your terminal window: - First, a build is required for the SDK, so that required jar files are generated. Make sure that the Android repository and the Kotlin SDK have the same immediate parent directory. Change the current directory to `Rocket.Chat.Android/app` and run the `build-sdk.sh` which will result in creating of the required jar file `core*.jar` and `common*.jar` in `Rocket.Chat.Android/app/libs`, by the following steps in your terminal window:
``` ```
cd Rocket.Chat.Android/app cd Rocket.Chat.Android/app
./build-sdk.sh ./build-sdk.sh
``` ```
**Note:** *You need to have Java 8 as default Java for the system (project won't build when using a Java 9+ version).*
## How to run ## How to run
### Command Line ### Command Line
...@@ -37,7 +39,7 @@ cd Rocket.Chat.Android/app ...@@ -37,7 +39,7 @@ cd Rocket.Chat.Android/app
### Android Studio ### Android Studio
- After importing the project in android studio, go to `Run > Run app` and then select your device, or create a new virtual device by following the wizard. - After importing the project in Android Studio, go to `Run > Run app` and then select your device, or create a new virtual device by following the wizard.
## Bug report & Feature request ## Bug report & Feature request
...@@ -45,4 +47,4 @@ Are you having a technical issue trying to compile the app, or setting up Push N ...@@ -45,4 +47,4 @@ Are you having a technical issue trying to compile the app, or setting up Push N
## Coding Style ## Coding Style
Please follow our [coding style](https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md) when contributing. Please follow the official [Kotlin coding convections](https://kotlinlang.org/docs/reference/coding-conventions.html) when contributing.
...@@ -16,7 +16,7 @@ android { ...@@ -16,7 +16,7 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion versions.minSdk minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2055 versionCode 2056
versionName "3.2.0" versionName "3.2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
...@@ -140,6 +140,7 @@ dependencies { ...@@ -140,6 +140,7 @@ dependencies {
implementation libraries.frescoAnimatedWebP implementation libraries.frescoAnimatedWebP
implementation libraries.glide implementation libraries.glide
implementation libraries.glideTransformations
kapt libraries.kotshiCompiler kapt libraries.kotshiCompiler
implementation libraries.kotshiApi implementation libraries.kotshiApi
......
...@@ -78,11 +78,6 @@ ...@@ -78,11 +78,6 @@
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".chatdetails.ui.ChatDetailsActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".chatinformation.ui.MessageInfoActivity" android:name=".chatinformation.ui.MessageInfoActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
...@@ -93,16 +88,6 @@ ...@@ -93,16 +88,6 @@
android:name=".settings.password.ui.PasswordActivity" android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity
android:name=".userdetails.ui.UserDetailsActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".chatroom.ui.ChatRoomActivity" />
</activity>
<receiver <receiver
android:name=".push.DirectReplyReceiver" android:name=".push.DirectReplyReceiver"
android:enabled="true" android:enabled="true"
......
...@@ -16,9 +16,9 @@ sealed class ScreenViewEvent(val screenName: String) { ...@@ -16,9 +16,9 @@ sealed class ScreenViewEvent(val screenName: String) {
object ChatRoom : ScreenViewEvent("ChatRoomFragment") object ChatRoom : ScreenViewEvent("ChatRoomFragment")
object ChatRooms : ScreenViewEvent("ChatRoomsFragment") object ChatRooms : ScreenViewEvent("ChatRoomsFragment")
object CreateChannel : ScreenViewEvent("CreateChannelFragment") object CreateChannel : ScreenViewEvent("CreateChannelFragment")
object UserDetails : ScreenViewEvent("UserDetailsFragment")
object FavoriteMessages : ScreenViewEvent("FavoriteMessagesFragment") object FavoriteMessages : ScreenViewEvent("FavoriteMessagesFragment")
object Files : ScreenViewEvent("FilesFragment") object Files : ScreenViewEvent("FilesFragment")
object MemberBottomSheet : ScreenViewEvent("MemberBottomSheetFragment")
object Members : ScreenViewEvent("MembersFragment") object Members : ScreenViewEvent("MembersFragment")
object Mentions : ScreenViewEvent("MentionsFragment") object Mentions : ScreenViewEvent("MentionsFragment")
object MessageInfo : ScreenViewEvent("MessageInfoFragment") object MessageInfo : ScreenViewEvent("MessageInfoFragment")
......
...@@ -5,7 +5,8 @@ import chat.rocket.android.dagger.scope.PerFragment ...@@ -5,7 +5,8 @@ import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider { @Module
abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = [TwoFAFragmentModule::class]) @ContributesAndroidInjector(modules = [TwoFAFragmentModule::class])
@PerFragment @PerFragment
......
package chat.rocket.android.chatdetails.di package chat.rocket.android.chatdetails.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatdetails.presentation.ChatDetailsView import chat.rocket.android.chatdetails.presentation.ChatDetailsView
import chat.rocket.android.chatdetails.ui.ChatDetailsFragment import chat.rocket.android.chatdetails.ui.ChatDetailsFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class ChatDetailsFragmentModule { class ChatDetailsFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides @Provides
@PerFragment @PerFragment
fun chatDetailsView(frag: ChatDetailsFragment): ChatDetailsView { fun chatDetailsView(frag: ChatDetailsFragment): ChatDetailsView {
...@@ -27,16 +20,4 @@ class ChatDetailsFragmentModule { ...@@ -27,16 +20,4 @@ class ChatDetailsFragmentModule {
@Provides @Provides
@PerFragment @PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao() fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: ChatDetailsFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
package chat.rocket.android.chatdetails.di
import chat.rocket.android.chatdetails.presentation.ChatDetailsNavigator
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
@Module
class ChatDetailsModule {
@Provides
@PerActivity
fun providesNavigator(activity: ChatDetailsActivity) = ChatDetailsNavigator(activity)
}
\ No newline at end of file
package chat.rocket.android.chatdetails.presentation
import chat.rocket.android.R
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.favoritemessages.ui.TAG_FAVORITE_MESSAGES_FRAGMENT
import chat.rocket.android.files.ui.TAG_FILES_FRAGMENT
import chat.rocket.android.members.ui.TAG_MEMBERS_FRAGMENT
import chat.rocket.android.mentions.ui.TAG_MENTIONS_FRAGMENT
import chat.rocket.android.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT
import chat.rocket.android.util.extensions.addFragmentBackStack
class ChatDetailsNavigator(internal val activity: ChatDetailsActivity) {
fun toMembersList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_MEMBERS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.members.ui.newInstance(chatRoomId)
}
}
fun toMentions(chatRoomId: String) {
activity.addFragmentBackStack(TAG_MENTIONS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.mentions.ui.newInstance(chatRoomId)
}
}
fun toPinnedMessageList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_PINNED_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId)
}
}
fun toFavoriteMessageList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_FAVORITE_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.favoritemessages.ui.newInstance(chatRoomId)
}
}
fun toFileList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_FILES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.files.ui.newInstance(chatRoomId)
}
}
}
package chat.rocket.android.chatdetails.presentation package chat.rocket.android.chatdetails.presentation
import chat.rocket.android.chatdetails.domain.ChatDetails import chat.rocket.android.chatdetails.domain.ChatDetails
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
...@@ -10,11 +11,12 @@ import chat.rocket.common.model.roomTypeOf ...@@ -10,11 +11,12 @@ import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getInfo import chat.rocket.core.internal.rest.getInfo
import chat.rocket.core.model.Room import chat.rocket.core.model.Room
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class ChatDetailsPresenter @Inject constructor( class ChatDetailsPresenter @Inject constructor(
private val view: ChatDetailsView, private val view: ChatDetailsView,
private val navigator: ChatDetailsNavigator, private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory factory: ConnectionManagerFactory
...@@ -29,11 +31,11 @@ class ChatDetailsPresenter @Inject constructor( ...@@ -29,11 +31,11 @@ class ChatDetailsPresenter @Inject constructor(
val room = retryIO("getInfo($chatRoomId, null, $chatRoomType") { val room = retryIO("getInfo($chatRoomId, null, $chatRoomType") {
client.getInfo(chatRoomId, null, roomTypeOf(chatRoomType)) client.getInfo(chatRoomId, null, roomTypeOf(chatRoomType))
} }
view.displayDetails(roomToChatDetails(room)) view.displayDetails(roomToChatDetails(room))
} catch(e: Exception) { } catch(exception: Exception) {
e.message.let { Timber.e(exception)
view.showMessage(it!!) exception.message?.let {
view.showMessage(it)
}.ifNull { }.ifNull {
view.showGenericErrorMessage() view.showGenericErrorMessage()
} }
......
package chat.rocket.android.chatdetails.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.util.extensions.addFragment
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_details.*
import javax.inject.Inject
fun Context.chatDetailsIntent(
chatRoomId: String,
chatRoomType: String,
isSubscribed: Boolean = true,
isMenuDisabled: Boolean = false
): Intent {
return Intent(this, ChatDetailsActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_CHAT_IS_SUBSCRIBED, isSubscribed)
putExtra(INTENT_CHAT_DISABLED_MENU, isMenuDisabled)
}
}
private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
private const val INTENT_CHAT_ROOM_TYPE = "chat_room_type"
private const val INTENT_CHAT_IS_SUBSCRIBED = "is_chat_room_subscribed"
private const val INTENT_CHAT_DISABLED_MENU = "is_menu_disabled"
class ChatDetailsActivity: AppCompatActivity(), HasSupportFragmentInjector {
@Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_details)
setupToolbar()
val chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
val isSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true)
val disableMenu = intent.getBooleanExtra(INTENT_CHAT_DISABLED_MENU, false)
if (supportFragmentManager.findFragmentByTag(TAG_CHAT_DETAILS_FRAGMENT) == null) {
addFragment(TAG_CHAT_DETAILS_FRAGMENT, R.id.fragment_container) {
newInstance(chatRoomId, chatRoomType, isSubscribed, disableMenu)
}
}
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
fragmentDispatchingAndroidInjector
override fun onBackPressed() {
super.onBackPressed()
overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
}
fun setNavigationIcon(resource: Int) {
toolbar.setNavigationIcon(resource)
}
fun setToolbarTitle(title: String) {
toolbar_title.text = title
}
private fun setupToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
setToolbarTitle(getString(R.string.title_channel_details))
setNavigationIcon(R.drawable.ic_close_white_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
}
}
\ No newline at end of file
package chat.rocket.android.chatdetails.ui package chat.rocket.android.chatdetails.ui
import DrawableHelper
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
...@@ -15,6 +16,7 @@ import chat.rocket.android.chatdetails.presentation.ChatDetailsPresenter ...@@ -15,6 +16,7 @@ import chat.rocket.android.chatdetails.presentation.ChatDetailsPresenter
import chat.rocket.android.chatdetails.presentation.ChatDetailsView import chat.rocket.android.chatdetails.presentation.ChatDetailsView
import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModel import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModel
import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModelFactory import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModelFactory
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui import chat.rocket.android.util.extensions.ui
...@@ -32,7 +34,7 @@ fun newInstance( ...@@ -32,7 +34,7 @@ fun newInstance(
disableMenu: Boolean disableMenu: Boolean
): ChatDetailsFragment { ): ChatDetailsFragment {
return ChatDetailsFragment().apply { return ChatDetailsFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(4).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType) putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed) putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed)
...@@ -48,14 +50,12 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "BUNDLE_CHAT_ROOM_TYPE" ...@@ -48,14 +50,12 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "BUNDLE_CHAT_ROOM_TYPE"
private const val BUNDLE_IS_SUBSCRIBED = "BUNDLE_IS_SUBSCRIBED" private const val BUNDLE_IS_SUBSCRIBED = "BUNDLE_IS_SUBSCRIBED"
private const val BUNDLE_DISABLE_MENU = "BUNDLE_DISABLE_MENU" private const val BUNDLE_DISABLE_MENU = "BUNDLE_DISABLE_MENU"
class ChatDetailsFragment: Fragment(), ChatDetailsView { class ChatDetailsFragment : Fragment(), ChatDetailsView {
@Inject @Inject
lateinit var presenter: ChatDetailsPresenter lateinit var presenter: ChatDetailsPresenter
@Inject @Inject
lateinit var factory: ChatDetailsViewModelFactory lateinit var factory: ChatDetailsViewModelFactory
private var adapter: ChatDetailsAdapter? = null private var adapter: ChatDetailsAdapter? = null
private lateinit var viewModel: ChatDetailsViewModel private lateinit var viewModel: ChatDetailsViewModel
private var chatRoomId: String? = null private var chatRoomId: String? = null
...@@ -72,6 +72,8 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView { ...@@ -72,6 +72,8 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE) chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED) isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED)
disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU) disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} }
} }
...@@ -94,9 +96,12 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView { ...@@ -94,9 +96,12 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
val text = room.name val text = room.name
name.text = text name.text = text
bindImage(chatRoomType!!) bindImage(chatRoomType!!)
content_topic.text = if (room.topic.isNullOrEmpty()) getString(R.string.msg_no_topic) else room.topic content_topic.text =
content_announcement.text = if (room.announcement.isNullOrEmpty()) getString(R.string.msg_no_announcement) else room.announcement if (room.topic.isNullOrEmpty()) getString(R.string.msg_no_topic) else room.topic
content_description.text = if (room.description.isNullOrEmpty()) getString(R.string.msg_no_description) else room.description content_announcement.text =
if (room.announcement.isNullOrEmpty()) getString(R.string.msg_no_announcement) else room.announcement
content_description.text =
if (room.description.isNullOrEmpty()) getString(R.string.msg_no_description) else room.description
} }
} }
...@@ -126,15 +131,24 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView { ...@@ -126,15 +131,24 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
it.addOption(getString(R.string.msg_mentions), R.drawable.ic_at_black_20dp) { it.addOption(getString(R.string.msg_mentions), R.drawable.ic_at_black_20dp) {
presenter.toMentions(chatRoomId!!) presenter.toMentions(chatRoomId!!)
} }
it.addOption(getString(R.string.title_members), R.drawable.ic_people_outline_black_24dp) { it.addOption(
getString(R.string.title_members),
R.drawable.ic_people_outline_black_24dp
) {
presenter.toMembers(chatRoomId!!) presenter.toMembers(chatRoomId!!)
} }
} }
it.addOption(getString(R.string.title_favorite_messages), R.drawable.ic_star_border_white_24dp) { it.addOption(
getString(R.string.title_favorite_messages),
R.drawable.ic_star_border_white_24dp
) {
presenter.toFavorites(chatRoomId!!) presenter.toFavorites(chatRoomId!!)
} }
it.addOption(getString(R.string.title_pinned_messages), R.drawable.ic_action_message_pin_24dp) { it.addOption(
getString(R.string.title_pinned_messages),
R.drawable.ic_action_message_pin_24dp
) {
presenter.toPinned(chatRoomId!!) presenter.toPinned(chatRoomId!!)
} }
} }
...@@ -192,9 +206,9 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView { ...@@ -192,9 +206,9 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as ChatDetailsActivity).let { with((activity as ChatRoomActivity)) {
it.setNavigationIcon(R.drawable.ic_close_white_24dp) hideToolbarChatRoomIcon()
it.setToolbarTitle(getString(R.string.title_channel_details)) showToolbarTitle(getString(R.string.title_channel_details))
} }
} }
} }
\ No newline at end of file
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.* import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.chatroom.uimodel.AttachmentUiModel
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.uimodel.MessageReplyUiModel
import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.chatroom.uimodel.toViewType
import chat.rocket.android.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.openTabbedUrl import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.actions.Action import chat.rocket.core.model.attachment.actions.Action
import chat.rocket.core.model.attachment.actions.ButtonAction import chat.rocket.core.model.attachment.actions.ButtonAction
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import timber.log.Timber import timber.log.Timber
import java.security.InvalidParameterException import java.security.InvalidParameterException
...@@ -22,7 +28,8 @@ class ChatRoomAdapter( ...@@ -22,7 +28,8 @@ class ChatRoomAdapter(
private val roomName: String? = null, private val roomName: String? = null,
private val actionSelectListener: OnActionSelected? = null, private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true, private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null private val reactionListener: EmojiReactionListener? = null,
private val navigator: ChatRoomNavigator? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() { ) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseUiModel<*>>() private val dataSet = ArrayList<BaseUiModel<*>>()
...@@ -34,7 +41,11 @@ class ChatRoomAdapter( ...@@ -34,7 +41,11 @@ class ChatRoomAdapter(
return when (viewType.toViewType()) { return when (viewType.toViewType()) {
BaseUiModel.ViewType.MESSAGE -> { BaseUiModel.ViewType.MESSAGE -> {
val view = parent.inflate(R.layout.item_message) val view = parent.inflate(R.layout.item_message)
MessageViewHolder(view, actionsListener, reactionListener) MessageViewHolder(
view,
actionsListener,
reactionListener
) { userId -> navigator?.toUserDetails(userId) }
} }
BaseUiModel.ViewType.URL_PREVIEW -> { BaseUiModel.ViewType.URL_PREVIEW -> {
val view = parent.inflate(R.layout.message_url_preview) val view = parent.inflate(R.layout.message_url_preview)
...@@ -42,11 +53,20 @@ class ChatRoomAdapter( ...@@ -42,11 +53,20 @@ class ChatRoomAdapter(
} }
BaseUiModel.ViewType.ATTACHMENT -> { BaseUiModel.ViewType.ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment) val view = parent.inflate(R.layout.item_message_attachment)
AttachmentViewHolder(view, actionsListener, reactionListener, actionAttachmentOnClickListener) AttachmentViewHolder(
view,
actionsListener,
reactionListener,
actionAttachmentOnClickListener
)
} }
BaseUiModel.ViewType.MESSAGE_REPLY -> { BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply) val view = parent.inflate(R.layout.item_message_reply)
MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink -> MessageReplyViewHolder(
view,
actionsListener,
reactionListener
) { roomName, permalink ->
actionSelectListener?.openDirectMessage(roomName, permalink) actionSelectListener?.openDirectMessage(roomName, permalink)
} }
} }
...@@ -117,7 +137,8 @@ class ChatRoomAdapter( ...@@ -117,7 +137,8 @@ class ChatRoomAdapter(
fun prependData(dataSet: List<BaseUiModel<*>>) { fun prependData(dataSet: List<BaseUiModel<*>>) {
//---At first we will update all already saved elements with received updated ones //---At first we will update all already saved elements with received updated ones
val filteredDataSet = dataSet.filter { newItem -> val filteredDataSet = dataSet.filter { newItem ->
val matchedIndex = this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType } val matchedIndex =
this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType }
if (matchedIndex > -1) { if (matchedIndex > -1) {
this.dataSet[matchedIndex] = newItem this.dataSet[matchedIndex] = newItem
notifyItemChanged(matchedIndex) notifyItemChanged(matchedIndex)
...@@ -266,7 +287,12 @@ class ChatRoomAdapter( ...@@ -266,7 +287,12 @@ class ChatRoomAdapter(
fun showMessageInfo(id: String) fun showMessageInfo(id: String)
fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean) fun citeMessage(
roomName: String,
roomType: String,
messageId: String,
mentionAuthor: Boolean
)
fun copyMessage(id: String) fun copyMessage(id: String)
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.text.Spannable import android.text.Spannable
...@@ -11,7 +10,6 @@ import androidx.core.view.isVisible ...@@ -11,7 +10,6 @@ import androidx.core.view.isVisible
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.MessageUiModel import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.userdetails.ui.userDetailsIntent
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import com.bumptech.glide.load.resource.gif.GifDrawable import com.bumptech.glide.load.resource.gif.GifDrawable
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
...@@ -20,7 +18,8 @@ import kotlinx.android.synthetic.main.item_message.view.* ...@@ -20,7 +18,8 @@ import kotlinx.android.synthetic.main.item_message.view.*
class MessageViewHolder( class MessageViewHolder(
itemView: View, itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null reactionListener: EmojiReactionListener? = null,
private val avatarListener: (String) -> Unit
) : BaseViewHolder<MessageUiModel>(itemView, listener, reactionListener), Drawable.Callback { ) : BaseViewHolder<MessageUiModel>(itemView, listener, reactionListener), Drawable.Callback {
init { init {
...@@ -73,22 +72,12 @@ class MessageViewHolder( ...@@ -73,22 +72,12 @@ class MessageViewHolder(
read_receipt_view.isVisible = true read_receipt_view.isVisible = true
} }
val senderId = data.message.sender?.id
val subscriptionId = data.subscriptionId
text_sender.setOnClickListener {
toUserDetails(context, senderId, subscriptionId)
}
image_avatar.setOnClickListener { image_avatar.setOnClickListener {
toUserDetails(context, senderId, subscriptionId) data.message.sender?.id?.let { userId ->
avatarListener(userId)
} }
} }
} }
private fun toUserDetails(context: Context, userId: String?, subscriptionId: String) {
userId?.let { context.startActivity(context.userDetailsIntent(it, subscriptionId)) }
} }
override fun unscheduleDrawable(who: Drawable?, what: Runnable?) { override fun unscheduleDrawable(who: Drawable?, what: Runnable?) {
......
...@@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleOwner ...@@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.chatrooms.adapter.RoomUiModelMapper import chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
...@@ -16,16 +15,11 @@ import chat.rocket.android.server.domain.SettingsRepository ...@@ -16,16 +15,11 @@ import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named import javax.inject.Named
@Module @Module
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides @Provides
@PerFragment @PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
...@@ -38,12 +32,6 @@ class ChatRoomFragmentModule { ...@@ -38,12 +32,6 @@ class ChatRoomFragmentModule {
return frag return frag
} }
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
@Provides @Provides
@PerFragment @PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao() fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
...@@ -71,6 +59,12 @@ class ChatRoomFragmentModule { ...@@ -71,6 +59,12 @@ class ChatRoomFragmentModule {
@Named("currentServer") serverUrl: String, @Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor permissionsInteractor: PermissionsInteractor
): RoomUiModelMapper { ): RoomUiModelMapper {
return RoomUiModelMapper(context, repository.get(serverUrl), userInteractor, serverUrl, permissionsInteractor) return RoomUiModelMapper(
context,
repository.get(serverUrl),
userInteractor,
serverUrl,
permissionsInteractor
)
} }
} }
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class ChatRoomModule { class ChatRoomModule {
@Provides @Provides
@PerActivity @PerActivity
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity) fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
@Provides
@PerActivity
fun provideJob() = Job()
@Provides
@PerActivity
fun provideLifecycleOwner(activity: ChatRoomActivity): LifecycleOwner = activity
@Provides
@PerActivity
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
...@@ -6,6 +6,7 @@ import dagger.Module ...@@ -6,6 +6,7 @@ import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module abstract class MessageServiceProvider { @Module abstract class MessageServiceProvider {
@ContributesAndroidInjector(modules = [AppModule::class]) @ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideMessageService(): MessageService abstract fun provideMessageService(): MessageService
} }
\ No newline at end of file
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatdetails.ui.chatDetailsIntent import chat.rocket.android.chatdetails.ui.TAG_CHAT_DETAILS_FRAGMENT
import chat.rocket.android.chatinformation.ui.messageInformationIntent import chat.rocket.android.chatinformation.ui.messageInformationIntent
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.chatRoomIntent import chat.rocket.android.chatroom.ui.chatRoomIntent
...@@ -11,23 +11,92 @@ import chat.rocket.android.members.ui.TAG_MEMBERS_FRAGMENT ...@@ -11,23 +11,92 @@ import chat.rocket.android.members.ui.TAG_MEMBERS_FRAGMENT
import chat.rocket.android.mentions.ui.TAG_MENTIONS_FRAGMENT import chat.rocket.android.mentions.ui.TAG_MENTIONS_FRAGMENT
import chat.rocket.android.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT import chat.rocket.android.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT
import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.userdetails.ui.TAG_USER_DETAILS_FRAGMENT
import chat.rocket.android.util.extensions.addFragmentBackStack import chat.rocket.android.util.extensions.addFragmentBackStack
class ChatRoomNavigator(internal val activity: ChatRoomActivity) { class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
fun toUserDetails(userId: String) {
activity.addFragmentBackStack(TAG_USER_DETAILS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.userdetails.ui.newInstance(userId)
}
}
fun toChatRoom(
chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isReadOnly: Boolean,
chatRoomLastSeen: Long,
isSubscribed: Boolean,
isCreator: Boolean,
isFavorite: Boolean
) {
activity.startActivity(
activity.chatRoomIntent(
chatRoomId,
chatRoomName,
chatRoomType,
isReadOnly,
chatRoomLastSeen,
isSubscribed,
isCreator,
isFavorite
)
)
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
}
fun toChatDetails( fun toChatDetails(
chatRoomId: String, chatRoomId: String,
chatRoomType: String, chatRoomType: String,
isChatRoomSubscribed: Boolean, isChatRoomSubscribed: Boolean,
isMenuDisabled: Boolean isMenuDisabled: Boolean
) { ) {
activity.startActivity( activity.addFragmentBackStack(TAG_CHAT_DETAILS_FRAGMENT, R.id.fragment_container) {
activity.chatDetailsIntent( chat.rocket.android.chatdetails.ui.newInstance(
chatRoomId, chatRoomId,
chatRoomType, chatRoomType,
isChatRoomSubscribed, isChatRoomSubscribed,
isMenuDisabled isMenuDisabled
) )
) }
}
fun toMembersList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_MEMBERS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.members.ui.newInstance(chatRoomId)
}
}
fun toMemberDetails(userId: String) {
activity.addFragmentBackStack(TAG_USER_DETAILS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.userdetails.ui.newInstance(userId)
}
}
fun toMentions(chatRoomId: String) {
activity.addFragmentBackStack(TAG_MENTIONS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.mentions.ui.newInstance(chatRoomId)
}
}
fun toPinnedMessageList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_PINNED_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId)
}
}
fun toFavoriteMessageList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_FAVORITE_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.favoritemessages.ui.newInstance(chatRoomId)
}
}
fun toFileList(chatRoomId: String) {
activity.addFragmentBackStack(TAG_FILES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.files.ui.newInstance(chatRoomId)
}
} }
fun toNewServer() { fun toNewServer() {
......
...@@ -40,6 +40,7 @@ import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter ...@@ -40,6 +40,7 @@ import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
...@@ -68,6 +69,7 @@ import chat.rocket.android.helper.MessageParser ...@@ -68,6 +69,7 @@ import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extension.asObservable import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.createImageFile import chat.rocket.android.util.extension.createImageFile
import chat.rocket.android.util.extensions.circularRevealOrUnreveal import chat.rocket.android.util.extensions.circularRevealOrUnreveal
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.getBitmpap import chat.rocket.android.util.extensions.getBitmpap
...@@ -85,6 +87,7 @@ import dagger.android.support.AndroidSupportInjection ...@@ -85,6 +87,7 @@ import dagger.android.support.AndroidSupportInjection
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.emoji_image_row_item.view.* import kotlinx.android.synthetic.main.emoji_image_row_item.view.*
import kotlinx.android.synthetic.main.emoji_row_item.view.* import kotlinx.android.synthetic.main.emoji_row_item.view.*
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
...@@ -145,13 +148,14 @@ internal const val MENU_ACTION_SHOW_DETAILS = 2 ...@@ -145,13 +148,14 @@ internal const val MENU_ACTION_SHOW_DETAILS = 2
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener, class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener,
ChatRoomAdapter.OnActionSelected, Drawable.Callback { ChatRoomAdapter.OnActionSelected, Drawable.Callback {
@Inject @Inject
lateinit var presenter: ChatRoomPresenter lateinit var presenter: ChatRoomPresenter
@Inject @Inject
lateinit var parser: MessageParser lateinit var parser: MessageParser
@Inject @Inject
lateinit var analyticsManager: AnalyticsManager lateinit var analyticsManager: AnalyticsManager
@Inject
lateinit var navigator: ChatRoomNavigator
private lateinit var adapter: ChatRoomAdapter private lateinit var adapter: ChatRoomAdapter
internal lateinit var chatRoomId: String internal lateinit var chatRoomId: String
private lateinit var chatRoomName: String private lateinit var chatRoomName: String
...@@ -284,7 +288,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -284,7 +288,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" } requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} }
adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this) adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this, navigator = navigator)
} }
override fun onCreateView( override fun onCreateView(
...@@ -308,6 +312,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -308,6 +312,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
showToolbarChatRoomIcon(chatRoomType) showToolbarChatRoomIcon(chatRoomType)
} }
getDraftMessage() getDraftMessage()
subscribeComposeTextMessage()
analyticsManager.logScreenView(ScreenViewEvent.ChatRoom) analyticsManager.logScreenView(ScreenViewEvent.ChatRoom)
} }
...@@ -844,7 +849,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -844,7 +849,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
true true
) )
subscribeComposeTextMessage()
emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this emojiKeyboardPopup.listener = this
...@@ -996,13 +1000,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -996,13 +1000,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun subscribeComposeTextMessage() { private fun subscribeComposeTextMessage() {
val editTextObservable = text_message.asObservable() text_message.asObservable().let {
compositeDisposable.addAll( compositeDisposable.addAll(
subscribeComposeButtons(editTextObservable), subscribeComposeButtons(it),
subscribeComposeTypingStatus(editTextObservable) subscribeComposeTypingStatus(it)
) )
} }
}
private fun unsubscribeComposeTextMessage() { private fun unsubscribeComposeTextMessage() {
compositeDisposable.clear() compositeDisposable.clear()
...@@ -1057,7 +1061,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -1057,7 +1061,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun setupToolbar(toolbarTitle: String) { private fun setupToolbar(toolbarTitle: String) {
(activity as ChatRoomActivity).showToolbarTitle(toolbarTitle) with(activity as ChatRoomActivity) {
this.clearLightStatusBar()
this.showToolbarTitle(toolbarTitle)
toolbar.isVisible = true
}
} }
override fun unscheduleDrawable(who: Drawable?, what: Runnable?) { override fun unscheduleDrawable(who: Drawable?, what: Runnable?) {
......
...@@ -108,7 +108,7 @@ private fun ChatRoomFragment.setupDetailsMenuItem(menu: Menu) { ...@@ -108,7 +108,7 @@ private fun ChatRoomFragment.setupDetailsMenuItem(menu: Menu) {
Menu.NONE, Menu.NONE,
MENU_ACTION_SHOW_DETAILS, MENU_ACTION_SHOW_DETAILS,
Menu.NONE, Menu.NONE,
"Channel Details" R.string.title_channel_details
).setIcon(R.drawable.ic_info_outline_white_24dp) ).setIcon(R.drawable.ic_info_outline_white_24dp)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
} }
\ No newline at end of file
...@@ -12,8 +12,6 @@ import chat.rocket.android.authentication.signup.di.SignupFragmentProvider ...@@ -12,8 +12,6 @@ import chat.rocket.android.authentication.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.chatdetails.di.ChatDetailsFragmentProvider import chat.rocket.android.chatdetails.di.ChatDetailsFragmentProvider
import chat.rocket.android.chatdetails.di.ChatDetailsModule
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.chatinformation.di.MessageInfoFragmentProvider import chat.rocket.android.chatinformation.di.MessageInfoFragmentProvider
import chat.rocket.android.chatinformation.ui.MessageInfoActivity import chat.rocket.android.chatinformation.ui.MessageInfoActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
...@@ -38,14 +36,14 @@ import chat.rocket.android.server.ui.ChangeServerActivity ...@@ -38,14 +36,14 @@ import chat.rocket.android.server.ui.ChangeServerActivity
import chat.rocket.android.settings.di.SettingsFragmentProvider import chat.rocket.android.settings.di.SettingsFragmentProvider
import chat.rocket.android.settings.password.di.PasswordFragmentProvider import chat.rocket.android.settings.password.di.PasswordFragmentProvider
import chat.rocket.android.settings.password.ui.PasswordActivity import chat.rocket.android.settings.password.ui.PasswordActivity
import chat.rocket.android.userdetails.di.UserDetailsModule import chat.rocket.android.userdetails.di.UserDetailsFragmentProvider
import chat.rocket.android.userdetails.ui.UserDetailsActivity
import chat.rocket.android.webview.adminpanel.di.AdminPanelWebViewFragmentProvider import chat.rocket.android.webview.adminpanel.di.AdminPanelWebViewFragmentProvider
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module @Module
abstract class ActivityBuilder { abstract class ActivityBuilder {
@PerActivity @PerActivity
@ContributesAndroidInjector( @ContributesAndroidInjector(
modules = [AuthenticationModule::class, modules = [AuthenticationModule::class,
...@@ -77,17 +75,9 @@ abstract class ActivityBuilder { ...@@ -77,17 +75,9 @@ abstract class ActivityBuilder {
@PerActivity @PerActivity
@ContributesAndroidInjector( @ContributesAndroidInjector(
modules = [ modules = [ChatRoomModule::class,
ChatRoomModule::class, ChatRoomFragmentProvider::class,
ChatRoomFragmentProvider::class UserDetailsFragmentProvider::class,
]
)
abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity
@ContributesAndroidInjector(
modules = [
ChatDetailsModule::class,
ChatDetailsFragmentProvider::class, ChatDetailsFragmentProvider::class,
MembersFragmentProvider::class, MembersFragmentProvider::class,
MentionsFragmentProvider::class, MentionsFragmentProvider::class,
...@@ -96,7 +86,7 @@ abstract class ActivityBuilder { ...@@ -96,7 +86,7 @@ abstract class ActivityBuilder {
FilesFragmentProvider::class FilesFragmentProvider::class
] ]
) )
abstract fun bindChatDetailsActivity(): ChatDetailsActivity abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity @PerActivity
@ContributesAndroidInjector(modules = [PasswordFragmentProvider::class]) @ContributesAndroidInjector(modules = [PasswordFragmentProvider::class])
...@@ -113,8 +103,4 @@ abstract class ActivityBuilder { ...@@ -113,8 +103,4 @@ abstract class ActivityBuilder {
@PerActivity @PerActivity
@ContributesAndroidInjector(modules = [DrawModule::class]) @ContributesAndroidInjector(modules = [DrawModule::class])
abstract fun bindDrawingActivity(): DrawingActivity abstract fun bindDrawingActivity(): DrawingActivity
@PerActivity
@ContributesAndroidInjector(modules = [UserDetailsModule::class])
abstract fun bindUserDetailsActivity(): UserDetailsActivity
} }
...@@ -102,6 +102,18 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -102,6 +102,18 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
suspend fun insertOrReplaceRoom(chatRoomEntity: ChatRoomEntity) {
retryDB("insertOrReplace($chatRoomEntity)") {
chatRoomDao().insertOrReplace(chatRoomEntity)
}
}
suspend fun getUser(id: String) = withContext(dbManagerContext) {
retryDB("getUser($id)") {
userDao().getUser(id)
}
}
fun processUsersBatch(users: List<User>) { fun processUsersBatch(users: List<User>) {
launch(dbManagerContext) { launch(dbManagerContext) {
val list = ArrayList<BaseUserEntity>(users.size) val list = ArrayList<BaseUserEntity>(users.size)
......
package chat.rocket.android.favoritemessages.di package chat.rocket.android.favoritemessages.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class FavoriteMessagesFragmentModule { class FavoriteMessagesFragmentModule {
...@@ -21,20 +14,4 @@ class FavoriteMessagesFragmentModule { ...@@ -21,20 +14,4 @@ class FavoriteMessagesFragmentModule {
fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView { fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView {
return frag return frag
} }
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FavoriteMessagesFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
...@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
...@@ -116,9 +115,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -116,9 +115,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as ChatDetailsActivity).let { (activity as ChatRoomActivity).showToolbarTitle(getString(R.string.title_favorite_messages))
it.setToolbarTitle(getString(R.string.title_favorite_messages))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
} }
} }
\ No newline at end of file
package chat.rocket.android.files.di package chat.rocket.android.files.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.files.presentation.FilesView import chat.rocket.android.files.presentation.FilesView
import chat.rocket.android.files.ui.FilesFragment import chat.rocket.android.files.ui.FilesFragment
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class FilesFragmentModule { class FilesFragmentModule {
...@@ -17,20 +14,4 @@ class FilesFragmentModule { ...@@ -17,20 +14,4 @@ class FilesFragmentModule {
fun provideFilesView(frag: FilesFragment): FilesView { fun provideFilesView(frag: FilesFragment): FilesView {
return frag return frag
} }
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FilesFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
...@@ -15,7 +15,7 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -15,7 +15,7 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.files.adapter.FilesAdapter import chat.rocket.android.files.adapter.FilesAdapter
import chat.rocket.android.files.presentation.FilesPresenter import chat.rocket.android.files.presentation.FilesPresenter
import chat.rocket.android.files.presentation.FilesView import chat.rocket.android.files.presentation.FilesView
...@@ -152,9 +152,11 @@ class FilesFragment : Fragment(), FilesView { ...@@ -152,9 +152,11 @@ class FilesFragment : Fragment(), FilesView {
} }
private fun setupToolbar(totalFiles: Long) { private fun setupToolbar(totalFiles: Long) {
(activity as ChatDetailsActivity).let { (activity as ChatRoomActivity).showToolbarTitle(
it.setToolbarTitle(getString(R.string.title_files_total, totalFiles)) (getString(
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp) R.string.title_files_total,
} totalFiles
))
)
} }
} }
\ No newline at end of file
package chat.rocket.android.members.di package chat.rocket.android.members.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.members.presentation.MembersNavigator
import chat.rocket.android.members.presentation.MembersView import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.ui.MembersFragment import chat.rocket.android.members.ui.MembersFragment
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class MembersFragmentModule { class MembersFragmentModule {
...@@ -19,24 +14,4 @@ class MembersFragmentModule { ...@@ -19,24 +14,4 @@ class MembersFragmentModule {
fun membersView(frag: MembersFragment): MembersView { fun membersView(frag: MembersFragment): MembersView {
return frag return frag
} }
@Provides
@PerFragment
fun provideChatRoomNavigator(activity: ChatDetailsActivity) = MembersNavigator(activity)
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: MembersFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
package chat.rocket.android.members.di package chat.rocket.android.members.di
import chat.rocket.android.members.ui.MembersFragment
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.members.ui.MemberBottomSheetFragment import chat.rocket.android.members.ui.MembersFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -12,9 +11,4 @@ abstract class MembersFragmentProvider { ...@@ -12,9 +11,4 @@ abstract class MembersFragmentProvider {
@ContributesAndroidInjector(modules = [MembersFragmentModule::class]) @ContributesAndroidInjector(modules = [MembersFragmentModule::class])
@PerFragment @PerFragment
abstract fun provideMembersFragment(): MembersFragment abstract fun provideMembersFragment(): MembersFragment
@ContributesAndroidInjector()
@PerFragment
abstract fun provideMemberBottomSheetFragment(): MemberBottomSheetFragment
} }
\ No newline at end of file
package chat.rocket.android.members.presentation
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.userdetails.ui.userDetailsIntent
class MembersNavigator(internal val activity: ChatDetailsActivity) {
fun toMemberDetails(userId: String) {
activity.apply {
startActivity(this.userDetailsIntent(userId, ""))
}
}
}
package chat.rocket.android.members.presentation package chat.rocket.android.members.presentation
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.members.uimodel.MemberUiModel import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -18,7 +18,7 @@ import javax.inject.Named ...@@ -18,7 +18,7 @@ import javax.inject.Named
class MembersPresenter @Inject constructor( class MembersPresenter @Inject constructor(
private val view: MembersView, private val view: MembersView,
private val navigator: MembersNavigator, private val navigator: ChatRoomNavigator,
private val dbManager: DatabaseManager, private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String, @Named("currentServer") private val currentServer: String,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
......
package chat.rocket.android.members.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.textContent
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_member_bottom_sheet.*
import javax.inject.Inject
fun newInstance(
avatarUri: String,
realName: String,
username: String,
email: String,
utcOffset: String
): BottomSheetDialogFragment {
return MemberBottomSheetFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_AVATAR_URI, avatarUri)
putString(BUNDLE_REAL_NAME, realName)
putString(BUNDLE_USERNAME, username)
putString(BUNDLE_EMAIL, email)
putString(BUNDLE_UTC_OFFSET, utcOffset)
}
}
}
internal const val TAG_MEMBER_BOTTOM_SHEET_FRAGMENT = "MemberBottomSheetFragment"
private const val BUNDLE_AVATAR_URI = "avatar_uri"
private const val BUNDLE_REAL_NAME = "real_name"
private const val BUNDLE_USERNAME = "username"
private const val BUNDLE_EMAIL = "email"
private const val BUNDLE_UTC_OFFSET = "utc_offset"
class MemberBottomSheetFragment : BottomSheetDialogFragment() {
@Inject
lateinit var analyticsManager: AnalyticsManager
private lateinit var avatarUri: String
private lateinit var realName: String
private lateinit var username: String
private lateinit var email: String
private lateinit var utcOffset: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
avatarUri = bundle.getString(BUNDLE_AVATAR_URI)
realName = bundle.getString(BUNDLE_REAL_NAME)
username = bundle.getString(BUNDLE_USERNAME)
email = bundle.getString(BUNDLE_EMAIL)
utcOffset = bundle.getString(BUNDLE_UTC_OFFSET)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater.inflate(R.layout.fragment_member_bottom_sheet, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showMemberDetails()
analyticsManager.logScreenView(ScreenViewEvent.MemberBottomSheet)
}
private fun showMemberDetails() {
image_bottom_sheet_avatar.setImageURI(avatarUri)
text_bottom_sheet_member_name.content = realName
text_bottom_sheet_member_username.content = username
if (email.isNotEmpty()) {
text_member_email_address.textContent = email
} else {
text_email_address.isVisible = false
text_member_email_address.isVisible = false
}
if (utcOffset.isNotEmpty()) {
text_member_utc.content = utcOffset
} else {
text_utc.isVisible = false
text_member_utc.isVisible = false
}
}
}
\ No newline at end of file
...@@ -12,16 +12,18 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -12,16 +12,18 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.members.adapter.MembersAdapter import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.presentation.MembersPresenter import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.presentation.MembersView import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.uimodel.MemberUiModel import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_members.* import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject import javax.inject.Inject
...@@ -120,20 +122,31 @@ class MembersFragment : Fragment(), MembersView { ...@@ -120,20 +122,31 @@ class MembersFragment : Fragment(), MembersView {
private fun setupRecyclerView() { private fun setupRecyclerView() {
ui { ui {
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = LinearLayoutManager(context)
recycler_view.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL)) recycler_view.addItemDecoration(
DividerItemDecoration(
it,
DividerItemDecoration.HORIZONTAL
)
)
recycler_view.adapter = adapter recycler_view.adapter = adapter
} }
} }
private fun setupToolbar(totalMembers: Long? = null) { private fun setupToolbar(totalMembers: Long? = null) {
(activity as ChatDetailsActivity).let { with((activity as ChatRoomActivity)) {
if (totalMembers != null) { if (totalMembers != null) {
it.setToolbarTitle(getString(R.string.title_counted_members, totalMembers)) showToolbarTitle(
(getString(
R.string.title_counted_members,
totalMembers
))
)
} else { } else {
it.setToolbarTitle(getString(R.string.title_members)) showToolbarTitle((getString(R.string.title_members)))
} }
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp) this.clearLightStatusBar()
toolbar.isVisible = true
} }
} }
} }
\ No newline at end of file
package chat.rocket.android.mentions.di package chat.rocket.android.mentions.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.mentions.presentention.MentionsView import chat.rocket.android.mentions.presentention.MentionsView
import chat.rocket.android.mentions.ui.MentionsFragment import chat.rocket.android.mentions.ui.MentionsFragment
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class MentionsFragmentModule { class MentionsFragmentModule {
...@@ -17,20 +14,4 @@ class MentionsFragmentModule { ...@@ -17,20 +14,4 @@ class MentionsFragmentModule {
fun provideMentionsView(frag: MentionsFragment): MentionsView { fun provideMentionsView(frag: MentionsFragment): MentionsView {
return frag return frag
} }
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: MentionsFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
...@@ -12,8 +12,8 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -12,8 +12,8 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.mentions.presentention.MentionsPresenter import chat.rocket.android.mentions.presentention.MentionsPresenter
...@@ -37,13 +37,12 @@ internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment" ...@@ -37,13 +37,12 @@ internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MentionsFragment : Fragment(), MentionsView { class MentionsFragment : Fragment(), MentionsView {
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: MentionsPresenter lateinit var presenter: MentionsPresenter
@Inject @Inject
lateinit var analyticsManager: AnalyticsManager lateinit var analyticsManager: AnalyticsManager
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -122,9 +121,6 @@ class MentionsFragment : Fragment(), MentionsView { ...@@ -122,9 +121,6 @@ class MentionsFragment : Fragment(), MentionsView {
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as ChatDetailsActivity).let { (activity as ChatRoomActivity).showToolbarTitle((getString(R.string.msg_mentions)))
it.setToolbarTitle(getString(R.string.msg_mentions))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
} }
} }
\ No newline at end of file
package chat.rocket.android.pinnedmessages.di package chat.rocket.android.pinnedmessages.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class PinnedMessagesFragmentModule { class PinnedMessagesFragmentModule {
...@@ -17,20 +14,4 @@ class PinnedMessagesFragmentModule { ...@@ -17,20 +14,4 @@ class PinnedMessagesFragmentModule {
fun providePinnedMessagesView(frag: PinnedMessagesFragment): PinnedMessagesView { fun providePinnedMessagesView(frag: PinnedMessagesFragment): PinnedMessagesView {
return frag return frag
} }
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: PinnedMessagesFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
} }
\ No newline at end of file
...@@ -12,14 +12,12 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -12,14 +12,12 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatdetails.ui.ChatDetailsActivity
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui import chat.rocket.android.util.extensions.ui
...@@ -123,9 +121,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { ...@@ -123,9 +121,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as ChatDetailsActivity).let { (activity as ChatRoomActivity).showToolbarTitle((getString(R.string.title_pinned_messages)))
it.setToolbarTitle(getString(R.string.title_pinned_messages))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
} }
} }
\ No newline at end of file
package chat.rocket.android.userdetails.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.userdetails.presentation.UserDetailsView
import chat.rocket.android.userdetails.ui.UserDetailsFragment
import dagger.Module
import dagger.Provides
@Module
class UserDetailsFragmentModule {
@Provides
@PerFragment
fun provideUserDetailsView(frag: UserDetailsFragment): UserDetailsView = frag
}
\ No newline at end of file
package chat.rocket.android.userdetails.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.userdetails.ui.UserDetailsFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class UserDetailsFragmentProvider {
@ContributesAndroidInjector(modules = [UserDetailsFragmentModule::class])
@PerFragment
abstract fun provideUserDetailsFragment(): UserDetailsFragment
}
package chat.rocket.android.userdetails.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.userdetails.presentation.UserDetailsView
import chat.rocket.android.userdetails.ui.UserDetailsActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class UserDetailsModule {
@Provides
@PerActivity
fun provideJob() = Job()
@Provides
@PerActivity
fun provideUserDetailsView(activity: UserDetailsActivity): UserDetailsView = activity
@Provides
@PerActivity
fun provideLifecycleOwner(activity: UserDetailsActivity): LifecycleOwner = activity
@Provides
@PerActivity
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
package chat.rocket.android.userdetails.presentation package chat.rocket.android.userdetails.presentation
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.model.ChatRoomEntity import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.server.domain.GetConnectingServerInteractor import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.util.ifNull
import chat.rocket.common.model.userStatusOf
import chat.rocket.core.internal.rest.createDirectMessage import chat.rocket.core.internal.rest.createDirectMessage
import chat.rocket.core.model.ChatRoom import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
...@@ -23,141 +23,98 @@ class UserDetailsPresenter @Inject constructor( ...@@ -23,141 +23,98 @@ class UserDetailsPresenter @Inject constructor(
private val view: UserDetailsView, private val view: UserDetailsView,
private val dbManager: DatabaseManager, private val dbManager: DatabaseManager,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: ChatRoomNavigator,
serverInteractor: GetConnectingServerInteractor, serverInteractor: GetConnectingServerInteractor,
factory: ConnectionManagerFactory factory: ConnectionManagerFactory
) { ) {
private var currentServer = serverInteractor.get()!! private var currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer) private val manager = factory.create(currentServer)
private val client = manager.client private val client = manager.client
private val interactor = FetchChatRoomsInteractor(client,dbManager) private val interactor = FetchChatRoomsInteractor(client, dbManager)
private lateinit var userEntity: UserEntity
fun loadUserDetails(userId: String) { fun loadUserDetails(userId: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
val user = withContext(CommonPool) { view.showLoading()
dbManager.userDao().getUser(id = userId) dbManager.getUser(userId)?.let {
} userEntity = it
val avatarUrl =
user?.let { u -> userEntity.username?.let { currentServer.avatarUrl(avatar = it) }
val openedChatRooms = chatRoomByName(name = u.name) val username = userEntity.username
val avatarUrl = u.username?.let { currentServer.avatarUrl(avatar = it) } val name = userEntity.name
val utcOffset =
val chatRoom: ChatRoom? = openedChatRooms.firstOrNull() userEntity.utcOffset // TODO Convert UTC and display like the mockup
if (avatarUrl != null && username != null && name != null && utcOffset != null) {
view.showUserDetails( view.showUserDetails(
avatarUrl = avatarUrl, avatarUrl = avatarUrl,
username = u.username, name = name,
name = u.name, username = username,
utcOffset = u.utcOffset, status = userEntity.status,
status = u.status, utcOffset = utcOffset.toString()
chatRoom = chatRoom
) )
} else {
throw Exception()
}
} }
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.e(ex) Timber.e(ex)
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
} }
} finally {
view.hideLoading()
} }
} }
fun createDirectMessage(id: String) = launchUI(strategy) {
try {
val result = retryIO("createDirectMessage($id") {
client.createDirectMessage(username = id)
} }
interactor.refreshChatRooms() fun createDirectMessage(username: String) {
launchUI(strategy) {
try {
view.showLoading()
val userEntity = withContext(CommonPool) { withContext(DefaultDispatcher) {
dbManager.userDao().getUser(id = id) val directMessage = retryIO("createDirectMessage($username") {
client.createDirectMessage(username)
} }
if (userEntity != null) { val chatRoomEntity = ChatRoomEntity(
val chatRoom = ChatRoom( id = directMessage.id,
id = result.id,
type = roomTypeOf(RoomType.DIRECT_MESSAGE),
name = userEntity.username ?: userEntity.name.orEmpty(), name = userEntity.username ?: userEntity.name.orEmpty(),
fullName = userEntity.name,
favorite = false,
open = false,
alert = false,
status = userStatusOf(userEntity.status),
client = client,
broadcast = false,
archived = false,
default = false,
description = null, description = null,
groupMentions = null, type = RoomType.DIRECT_MESSAGE,
userMentions = null, fullname = userEntity.name,
lastMessage = null,
lastSeen = null,
topic = null,
announcement = null,
roles = null,
unread = 0,
readonly = false,
muted = null,
subscriptionId = "", subscriptionId = "",
timestamp = null, updatedAt = directMessage.updatedAt
updatedAt = result.updatedAt,
user = null
) )
withContext(CommonPool + strategy.jobs) { dbManager.insertOrReplaceRoom(chatRoomEntity)
dbManager.chatRoomDao().insertOrReplace(chatRoom = ChatRoomEntity(
id = chatRoom.id, interactor.refreshChatRooms()
name = chatRoom.name,
description = chatRoom.description,
type = chatRoom.type.toString(),
fullname = chatRoom.fullName,
subscriptionId = chatRoom.subscriptionId,
updatedAt = chatRoom.updatedAt
))
}
view.toDirectMessage(chatRoom = chatRoom) navigator.toChatRoom(
chatRoomId = chatRoomEntity.id,
chatRoomName = chatRoomEntity.name,
chatRoomType = chatRoomEntity.type,
isReadOnly = false,
chatRoomLastSeen = -1,
isSubscribed = chatRoomEntity.open,
isCreator = true,
isFavorite = false
)
} }
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.e(ex) Timber.e(ex)
view.onOpenDirectMessageError() ex.message?.let {
} view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
} }
} finally {
private suspend fun chatRoomByName(name: String? = null): List<ChatRoom> = withContext(CommonPool) { view.hideLoading()
return@withContext dbManager.chatRoomDao().getAllSync().filter {
if (name == null) {
return@filter true
}
it.chatRoom.name == name || it.chatRoom.fullname == name
}.map {
with(it.chatRoom) {
ChatRoom(
id = id,
subscriptionId = subscriptionId,
type = roomTypeOf(type),
unread = unread,
broadcast = broadcast ?: false,
alert = alert,
fullName = fullname,
name = name ?: "",
favorite = favorite ?: false,
default = isDefault ?: false,
readonly = readonly,
open = open,
lastMessage = null,
archived = false,
status = null,
user = null,
userMentions = userMentions,
client = client,
announcement = null,
description = null,
groupMentions = groupMentions,
roles = null,
topic = null,
lastSeen = this.lastSeen,
timestamp = timestamp,
updatedAt = updatedAt
)
} }
} }
} }
......
package chat.rocket.android.userdetails.presentation package chat.rocket.android.userdetails.presentation
import chat.rocket.core.model.ChatRoom import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
interface UserDetailsView { interface UserDetailsView : LoadingView, MessageView {
fun showUserDetails(avatarUrl: String?, username: String?, name: String?, utcOffset: Float?, status: String, chatRoom: ChatRoom?) /**
* Shows user detail.
fun toDirectMessage(chatRoom: ChatRoom) *
* @param avatarUrl The user avatar URL.
fun onOpenDirectMessageError() * @param name The user's name.
* @param username The user's username.
* @param status The user's status.
* @param utcOffset The user's UTC offset.
*/
fun showUserDetails(
avatarUrl: String,
name: String,
username: String,
status: String,
utcOffset: String
)
} }
package chat.rocket.android.userdetails.ui
import android.content.Context
import android.content.Intent
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import blurred
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.emoji.internal.GlideApp
import chat.rocket.android.userdetails.presentation.UserDetailsPresenter
import chat.rocket.android.userdetails.presentation.UserDetailsView
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extension.orFalse
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.retryIO
import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.rest.createDirectMessage
import chat.rocket.core.model.ChatRoom
import chat.rocket.core.model.userId
import com.bumptech.glide.Glide
import com.bumptech.glide.Priority
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.CenterInside
import com.bumptech.glide.load.resource.bitmap.FitCenter
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.google.android.material.snackbar.Snackbar
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.activity_user_details.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneId
import org.threeten.bp.ZoneOffset
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.format.DateTimeFormatter
import timber.log.Timber
import javax.inject.Inject
import kotlin.math.roundToLong
fun Context.userDetailsIntent(userId: String, subscriptionId: String): Intent {
return Intent(this, UserDetailsActivity::class.java).apply {
putExtra(EXTRA_USER_ID, userId)
putExtra(EXTRA_SUBSCRIPTION_ID, subscriptionId)
}
}
const val EXTRA_USER_ID = "EXTRA_USER_ID"
const val EXTRA_SUBSCRIPTION_ID = "EXTRA_USERNAME"
class UserDetailsActivity : AppCompatActivity(), UserDetailsView, HasSupportFragmentInjector {
@Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
@Inject
lateinit var presenter: UserDetailsPresenter
private lateinit var subscriptionId: String
private lateinit var userId: String
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_details)
setupToolbar()
userId = intent.getStringExtra(EXTRA_USER_ID)
subscriptionId = intent.getStringExtra(EXTRA_SUBSCRIPTION_ID)
showLoadingView(true)
presenter.loadUserDetails(userId = userId)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentDispatchingAndroidInjector
private fun setupToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
toolbar.setNavigationOnClickListener { finish() }
}
override fun showUserDetails(
avatarUrl: String?,
username: String?,
name: String?,
utcOffset: Float?,
status: String,
chatRoom: ChatRoom?
) {
text_view_name.text = name
text_view_username.text = username
text_view_status.text = status.capitalize()
try {
launch(UI) {
val image = withContext(CommonPool) {
try {
val requestOptions = RequestOptions()
.priority(Priority.IMMEDIATE)
.transforms(CenterInside(), FitCenter())
return@withContext GlideApp.with(this@UserDetailsActivity)
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(avatarUrl)
.apply(requestOptions)
.submit()
.get()
} catch (ex: Exception) {
Timber.e(ex)
return@withContext null
} finally {
showLoadingView(false)
}
}
image?.let {
val blurredBitmap = image.blurred(context = this@UserDetailsActivity,
newWidth = toolbar.measuredWidth, newHeight = toolbar.measuredHeight)
toolbar.background = BitmapDrawable(resources, blurredBitmap)
GlideApp.with(this@UserDetailsActivity)
.asBitmap()
.transforms(RoundedCorners(25), CenterCrop())
.load(image)
.into(image_view_avatar)
}
}
utcOffset?.let {
val offsetLong = it.roundToLong()
val offset = if (it > 0) "+$offsetLong" else offsetLong.toString()
val formatter = DateTimeFormatter.ofPattern("'(GMT$offset)' hh:mm a")
val zoneId = ZoneId.systemDefault()
val timeNow = OffsetDateTime.now(ZoneOffset.UTC).plusHours(offsetLong).toLocalDateTime()
text_view_tz.text = formatter.format(ZonedDateTime.of(timeNow, zoneId))
}
text_view_message.setOnClickListener {
if (chatRoom == null) {
presenter.createDirectMessage(id = userId)
} else {
toDirectMessage(chatRoom)
}
}
image_view_message.setOnClickListener {
if (chatRoom == null) {
presenter.createDirectMessage(id = userId)
} else {
toDirectMessage(chatRoom)
}
}
} catch (ex: Exception) {
Timber.e(ex)
}
}
private fun showLoadingView(show: Boolean) {
runOnUiThread {
group_user_details.isVisible = !show
view_loading.isVisible = show
}
}
override fun onOpenDirectMessageError() {
Snackbar.make(root_layout, R.string.error_opening_dm, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.retry) {
presenter.createDirectMessage(userId)
}.show()
}
override fun toDirectMessage(chatRoom: ChatRoom) {
finish()
if (chatRoom.subscriptionId.isEmpty() || chatRoom.subscriptionId != subscriptionId) {
startActivity(
chatRoomIntent(
chatRoomId = chatRoom.id,
chatRoomName = chatRoom.name,
chatRoomType = chatRoom.type.toString(),
isReadOnly = chatRoom.readonly.orFalse(),
chatRoomLastSeen = chatRoom.lastSeen ?: 0,
isSubscribed = chatRoom.open,
isCreator = false,
isFavorite = chatRoom.favorite
)
)
}
}
}
package chat.rocket.android.userdetails.ui
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.userdetails.presentation.UserDetailsPresenter
import chat.rocket.android.userdetails.presentation.UserDetailsView
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setLightStatusBar
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import com.bumptech.glide.Glide
import com.bumptech.glide.load.MultiTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import dagger.android.support.AndroidSupportInjection
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_user_details.*
import javax.inject.Inject
fun newInstance(userId: String): Fragment {
return UserDetailsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_USER_ID, userId)
}
}
}
internal const val TAG_USER_DETAILS_FRAGMENT = "UserDetailsFragment"
private const val BUNDLE_USER_ID = "user_id"
class UserDetailsFragment : Fragment(), UserDetailsView {
@Inject
lateinit var presenter: UserDetailsPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private lateinit var userId: String
private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
userId = bundle.getString(BUNDLE_USER_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_user_details)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListeners()
presenter.loadUserDetails(userId)
analyticsManager.logScreenView(ScreenViewEvent.UserDetails)
}
override fun onDestroyView() {
handler.removeCallbacksAndMessages(null)
super.onDestroyView()
}
override fun showUserDetails(
avatarUrl: String,
name: String,
username: String,
status: String,
utcOffset: String
) {
val requestBuilder = Glide.with(this).load(avatarUrl)
requestBuilder.apply(
RequestOptions.bitmapTransform(MultiTransformation(BlurTransformation(), CenterCrop()))
).into(image_blur)
requestBuilder.apply(RequestOptions.bitmapTransform(RoundedCorners(14)))
.into(image_avatar)
text_name.text = name
text_username.text = username
text_description_status.text = status.substring(0, 1).toUpperCase() + status.substring(1)
text_description_timezone.text = utcOffset
// We should also setup the user details listeners.
text_message.setOnClickListener { presenter.createDirectMessage(username) }
}
override fun showLoading() {
group_user_details.isVisible = false
view_loading.isVisible = true
}
override fun hideLoading() {
group_user_details.isVisible = true
view_loading.isVisible = false
}
override fun showMessage(resId: Int) {
ui { showToast(resId) }
}
override fun showMessage(message: String) {
ui { showToast(message) }
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun setupToolbar() {
handler.postDelayed({
with(activity as ChatRoomActivity) {
view?.let {
setLightStatusBar(
it,
ContextCompat.getColor(this, R.color.whitesmoke)
)
}
toolbar.isVisible = false
}
}, 400)
}
private fun setupListeners() {
image_arrow_back.setOnClickListener { activity?.onBackPressed() }
}
}
\ No newline at end of file
...@@ -11,6 +11,7 @@ import android.view.View ...@@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
...@@ -22,12 +23,16 @@ import androidx.fragment.app.Fragment ...@@ -22,12 +23,16 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import chat.rocket.android.R import chat.rocket.android.R
fun FragmentActivity.setLightStatusBar(view: View) { fun FragmentActivity.setLightStatusBar(view: View, @ColorInt color: Int = 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var flags = view.systemUiVisibility var flags = view.systemUiVisibility
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.systemUiVisibility = flags view.systemUiVisibility = flags
window.statusBarColor = ContextCompat.getColor(this, R.color.colorWhite) window.statusBarColor = if (color == 0) {
ContextCompat.getColor(this, R.color.colorWhite)
} else {
color
}
} }
} }
......
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
android:shape="rectangle"> android:shape="rectangle">
<padding <padding
android:bottom="2dp" android:bottom="3dp"
android:left="2dp" android:left="3dp"
android:right="2dp" android:right="3dp"
android:top="2dp" /> android:top="3dp" />
<solid android:color="@android:color/white" /> <solid android:color="@color/color_white" />
<corners android:radius="25px" /> <corners android:radius="6dp" />
</shape> </shape>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="20" android:viewportWidth="20"
android:viewportHeight="20"> android:viewportHeight="20">
<path <path
android:fillColor="#1d74f5" android:fillColor="#1d74f5"
android:fillType="evenOdd" android:fillType="evenOdd"
......
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".chatdetails.ui.ChatDetailsActivity">
<include
android:id="@+id/layout_app_bar"
layout="@layout/app_bar_chat_details" />
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:fitsSystemWindows="true">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/image_view_message"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_username"
app:srcCompat="@drawable/ic_message_24dp" />
<TextView
android:id="@+id/text_view_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/message"
android:textColor="#1d74f5"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="@+id/image_view_message"
app:layout_constraintStart_toStartOf="@+id/image_view_message"
app:layout_constraintTop_toBottomOf="@+id/image_view_message" />
<TextView
android:id="@+id/text_view_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:fontFamily="sans-serif-medium"
android:textColor="@android:color/black"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Karem Flusser" />
<TextView
android:id="@+id/text_view_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/darkGray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_name"
tools:text="karem.flusser" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:text="@string/status"
android:textColor="@color/darkGray"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_message" />
<TextView
android:id="@+id/text_view_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textColor="@android:color/black"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3"
tools:text="Online" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/timezone"
android:textColor="@color/darkGray"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_status" />
<TextView
android:id="@+id/text_view_tz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textColor="@android:color/black"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:text="(UTC-2) 11:08 AM" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_user_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="text_view_tz,textView,text_view_status,text_view_message,textView3,text_view_name,text_view_username,image_view_message" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:elevation="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="@android:color/white"
app:expandedTitleGravity="top"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="?actionBarSize"
app:elevation="0dp"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="?android:attr/homeAsUpIndicator"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<ImageView
android:id="@+id/image_view_avatar"
android:layout_width="98dp"
android:layout_height="98dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="16dp"
android:layout_marginTop="?actionBarSize"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_round_bounds_white"
tools:srcCompat="@tools:sample/avatars[6]" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout 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:background="@color/colorPrimary"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="@drawable/ic_close_white_24dp"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/text_view_drawable_padding"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/colorWhite"
android:textSize="18sp"
android:textStyle="bold"
tools:text="@string/title_channel_details" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/member_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
tools:context=".members.ui.MemberBottomSheetFragment">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_bottom_sheet_avatar"
android:layout_width="0dp"
android:layout_height="200dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/name_and_username_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorBackgroundMemberContainer"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
app:layout_constraintBottom_toBottomOf="@+id/image_bottom_sheet_avatar"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="@+id/text_bottom_sheet_member_name"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
tools:text="Ronald Perkins" />
<TextView
android:id="@+id/text_bottom_sheet_member_username"
style="@style/Sender.Name.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textColor="@color/colorWhite"
tools:text="\@ronaldPerkins" />
</LinearLayout>
<TextView
android:id="@+id/text_email_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/msg_email_address"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_bottom_sheet_avatar" />
<TextView
android:id="@+id/text_member_email_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="10dp"
android:textColor="@color/colorPrimaryText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_email_address"
tools:text="ronald@perkins.com" />
<TextView
android:id="@+id/text_utc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/msg_utc_offset"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_member_email_address" />
<TextView
android:id="@+id/text_member_utc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="10dp"
android:textColor="@color/colorPrimaryText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_utc"
tools:text="+01:00" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">
<ImageView
android:id="@+id/image_blur"
android:layout_width="match_parent"
android:layout_height="120dp" />
<ImageView
android:id="@+id/image_arrow_back"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="@dimen/screen_edge_left_and_right_margins"
android:src="@drawable/ic_arrow_back_white_24dp"
android:tint="@color/color_black"
app:layout_constraintStart_toStartOf="@+id/image_blur"
app:layout_constraintTop_toTopOf="@+id/image_blur" />
<ImageView
android:id="@+id/image_avatar"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/bg_border_user_details_avatar"
app:layout_constraintBottom_toBottomOf="@+id/image_blur"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_blur"
tools:srcCompat="@tools:sample/avatars[6]" />
<TextView
android:id="@+id/text_name"
style="@style/UserDetails.TextView.Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_avatar"
tools:text="Karem Flusser" />
<TextView
android:id="@+id/text_username"
style="@style/UserDetails.TextView.Username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_name"
tools:text="karem.flusser" />
<TextView
android:id="@+id/text_message"
style="@style/UserDetails.TextView.Actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:drawableTop="@drawable/ic_message_24dp"
android:text="@string/msg_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username" />
<TextView
android:id="@+id/text_title_status"
style="@style/UserDetails.TextView.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="20dp"
android:text="@string/status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_message" />
<TextView
android:id="@+id/text_description_status"
style="@style/UserDetails.TextView.Description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title_status"
tools:text="Online" />
<TextView
android:id="@+id/text_title_timezone"
style="@style/UserDetails.TextView.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp"
android:text="@string/timezone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description_status" />
<TextView
android:id="@+id/text_description_timezone"
style="@style/UserDetails.TextView.Description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title_timezone"
tools:text="(UTC-2) 11:08 AM" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_user_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="image_blur, image_avatar, text_name, text_username, text_message, text_title_status, text_description_status, text_title_timezone, text_description_timezone" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
...@@ -342,14 +342,10 @@ ...@@ -342,14 +342,10 @@
<string name="notif_success_sending">Nachricht gesendet nach %1$s!</string> <string name="notif_success_sending">Nachricht gesendet nach %1$s!</string>
<string name="read_by">Gelesen von</string> <string name="read_by">Gelesen von</string>
<string name="message_information_title">Nachricht Information</string> <string name="message_information_title">Nachricht Information</string>
<string name="message_room_changed_privacy">Raumtyp geändert zu: %1$s durch %2$s</string> <string name="message_room_changed_privacy">Raumtyp geändert zu: %1$s durch %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Nachrticht</string>
<string name="timezone">Zeitzone</string> <string name="timezone">Zeitzone</string>
<string name="error_opening_dm">Etwas ist schiefgegangen, als wir dieses Gespräch erstellt haben…</string>
<string name="retry">Wiederholen</string>
<!-- Report --> <!-- Report -->
<string name="submit">Senden</string> <string name="submit">Senden</string>
......
...@@ -335,10 +335,7 @@ ...@@ -335,10 +335,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation--> <string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation-->
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string> <!-- TODO - Add proper translation -->
<string name="timezone">Timezone</string> <!-- TODO - Add proper translation --> <string name="timezone">Timezone</string> <!-- TODO - Add proper translation -->
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string> <!-- TODO - Add proper translation -->
<string name="retry">Retry</string> <!-- TODO - Add proper translation -->
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!--TODO - Add proper translation--> <string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
...@@ -338,10 +338,7 @@ ...@@ -338,10 +338,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string> <!-- TODO - Add proper translation -->
<string name="timezone">Timezone</string> <!-- TODO - Add proper translation --> <string name="timezone">Timezone</string> <!-- TODO - Add proper translation -->
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string> <!-- TODO - Add proper translation -->
<string name="retry">Retry</string> <!-- TODO - Add proper translation -->
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!-- TODO - Add proper translation --> <string name="submit">Submit</string> <!-- TODO - Add proper translation -->
......
...@@ -337,10 +337,7 @@ ...@@ -337,10 +337,7 @@
<string name="message_room_changed_privacy">%2$s ने रूम का प्रकार बदलकर %1$s किया </string> <string name="message_room_changed_privacy">%2$s ने रूम का प्रकार बदलकर %1$s किया </string>
<!-- User Details --> <!-- User Details -->
<string name="message">संदेश</string>
<string name="timezone">समय क्षेत्र</string> <string name="timezone">समय क्षेत्र</string>
<string name="error_opening_dm">जब हम इस बातचीत को बना रहे थे तो कुछ गड़बड़ हुई …</string>
<string name="retry">पुन: प्रयास करें</string>
<!-- Report --> <!-- Report -->
<string name="submit">जमा करें</string> <string name="submit">जमा करें</string>
......
...@@ -332,10 +332,7 @@ ...@@ -332,10 +332,7 @@
<string name="message_room_changed_privacy">Il tipo di stanza è cambiato in: %1$s da %2$s</string> <string name="message_room_changed_privacy">Il tipo di stanza è cambiato in: %1$s da %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Messaggio</string>
<string name="timezone">Fuso Orario</string> <string name="timezone">Fuso Orario</string>
<string name="error_opening_dm">È successo qualcosa di sbagliato durante la creazione di questa conversazione...</string>
<string name="retry">Riprova</string>
<!-- Report --> <!-- Report -->
<string name="submit">Invia</string> <string name="submit">Invia</string>
......
...@@ -340,10 +340,7 @@ ...@@ -340,10 +340,7 @@
<string name="message_room_changed_privacy">ルームタイプを %2$s から %1$s に変更しました</string> <string name="message_room_changed_privacy">ルームタイプを %2$s から %1$s に変更しました</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string> <!--TODO - Add proper translation-->
<string name="timezone">Timezone</string> <!--TODO - Add proper translation--> <string name="timezone">Timezone</string> <!--TODO - Add proper translation-->
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string> <!-- TODO - Add proper translation -->
<string name="retry">Retry</string> <!-- TODO - Add proper translation -->
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!--TODO - Add proper translation--> <string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
...@@ -205,13 +205,12 @@ ...@@ -205,13 +205,12 @@
<string name="message_user_joined_channel">Entrou na sala</string> <string name="message_user_joined_channel">Entrou na sala</string>
<string name="message_welcome">Bem-vindo, %s</string> <string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string> <string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Pinou uma mensagem:</string> <string name="message_pinned">Fixou uma mensagem:</string>
<string name="message_muted">Usuário %1$s foi silenciado por %2$s</string> <string name="message_muted">Usuário %1$s foi silenciado por %2$s</string>
<string name="message_unmuted">Usuário %1$s saiu do modo silenciado por %2$s</string> <string name="message_unmuted">Usuário %1$s saiu do modo silenciado por %2$s</string>
<string name="message_role_add">%1$s foi definido %2$s por %3$s</string> <string name="message_role_add">%1$s foi definido %2$s por %3$s</string>
<string name="message_role_removed">%1$s não é mais %2$s por %3$s</string> <string name="message_role_removed">%1$s não é mais %2$s por %3$s</string>
// TODO:Add proper translation. <string name="message_credentials_saved_successfully">Credenciais salvas com sucesso</string>
<string name="message_credentials_saved_successfully">Credentials saved successfully</string>
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Responder</string> <string name="action_msg_reply">Responder</string>
...@@ -233,7 +232,7 @@ ...@@ -233,7 +232,7 @@
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Edição não permitida</string> <string name="permission_editing_not_allowed">Edição não permitida</string>
<string name="permission_deleting_not_allowed">Remoção não permitida</string> <string name="permission_deleting_not_allowed">Remoção não permitida</string>
<string name="permission_pinning_not_allowed">Pinagem não permitida</string> <string name="permission_pinning_not_allowed">Fixagem não permitida</string>
<string name="permission_starring_not_allowed">Marcar como favorita não permitido</string> <string name="permission_starring_not_allowed">Marcar como favorita não permitido</string>
<!-- Search message --> <!-- Search message -->
...@@ -252,13 +251,13 @@ ...@@ -252,13 +251,13 @@
<string name="msg_all_the_mentions_appear_here">Todas menções\naparecerão aqui</string> <string name="msg_all_the_mentions_appear_here">Todas menções\naparecerão aqui</string>
<!-- Pinned Messages --> <!-- Pinned Messages -->
<string name="title_pinned_messages">Mensagens Pinadas</string> <string name="title_pinned_messages">Mensagens Fixadas</string>
<string name="no_pinned_messages">Nenhuma mensagem pinada</string> <string name="no_pinned_messages">Nenhuma mensagem fixada</string>
<string name="no_pinned_description">Todas mensagens pinadas\naparecerão aqui</string> <string name="no_pinned_description">Todas mensagens fixadas\naparecerão aqui</string>
<!-- Favorite Messages --> <!-- Favorite Messages -->
<string name="title_favorite_messages">Messagens Favoritas</string> <string name="title_favorite_messages">Mensagens Favoritas</string>
<string name="no_favorite_messages">Nenhuma messagem favorita</string> <string name="no_favorite_messages">Nenhuma mensagem favorita</string>
<string name="no_favorite_description">Todas mensagens favoritas\naparecerão aqui</string> <string name="no_favorite_description">Todas mensagens favoritas\naparecerão aqui</string>
<!-- Files --> <!-- Files -->
...@@ -336,10 +335,7 @@ ...@@ -336,10 +335,7 @@
<string name="message_room_changed_privacy">O tipo da sala mudou para: %1$s por %2$s</string> <string name="message_room_changed_privacy">O tipo da sala mudou para: %1$s por %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Mensagem</string>
<string name="timezone">Fuso horário</string> <string name="timezone">Fuso horário</string>
<string name="error_opening_dm">Algo deu errado quando tentamos abrir esta conversa…</string>
<string name="retry">Retentar</string>
<!-- Report --> <!-- Report -->
<string name="submit">Enviar</string> <string name="submit">Enviar</string>
......
...@@ -334,10 +334,7 @@ ...@@ -334,10 +334,7 @@
<string name="message_room_changed_privacy">Тип канала изменен на: %1$s пользователем %2$s</string> <string name="message_room_changed_privacy">Тип канала изменен на: %1$s пользователем %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Сообщение</string> <string name="timezone">Часовой поясe</string>
<string name="timezone">Часовой пояс</string>
<string name="error_opening_dm">При создании этого диалога что-то пошло не так...</string>
<string name="retry">Повторить</string>
<!-- Report --> <!-- Report -->
<string name="submit">Отправить</string> <string name="submit">Отправить</string>
......
...@@ -337,10 +337,7 @@ ...@@ -337,10 +337,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation--> <string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation-->
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string> <!--TODO - Add proper translation-->
<string name="timezone">Timezone</string> <!--TODO - Add proper translation--> <string name="timezone">Timezone</string> <!--TODO - Add proper translation-->
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string> <!-- TODO - Add proper translation -->
<string name="retry">Retry</string> <!-- TODO - Add proper translation -->
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!--TODO - Add proper translation--> <string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
...@@ -335,10 +335,7 @@ ...@@ -335,10 +335,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!-- TODO - Add proper translation --> <string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!-- TODO - Add proper translation -->
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string>
<string name="timezone">Timezone</string> <string name="timezone">Timezone</string>
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string> <!-- TODO - Add proper translation -->
<string name="retry">Retry</string> <!-- TODO - Add proper translation -->
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!-- TODO - Add proper translation --> <string name="submit">Submit</string> <!-- TODO - Add proper translation -->
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
<color name="colorTimestampText">#FF9DA2A9</color> <color name="colorTimestampText">#FF9DA2A9</color>
<color name="colorTimestampTextUnread">#FF5699FF</color> <color name="colorTimestampTextUnread">#FF5699FF</color>
<color name="colorLastMessageText">#99000000</color> <color name="colorLastMessageText">#99000000</color>
<color name="colorUserDetailsNameText">#FF0C0D0F</color>
<color name="colorUserDetailsUsernameText">#8B000000</color>
<!-- User status colors --> <!-- User status colors -->
<color name="colorUserStatusOnline">#2FE1A8</color> <color name="colorUserStatusOnline">#2FE1A8</color>
......
...@@ -351,11 +351,8 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -351,11 +351,8 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="foss" translatable="false">(FOSS)</string> <string name="foss" translatable="false">(FOSS)</string>
<!-- User Details --> <!-- User Details -->
<string name="message">Message</string>
<string name="timezone">Timezone</string> <string name="timezone">Timezone</string>
<string name="status" translatable="false">Status</string> <string name="status" translatable="false">Status</string>
<string name="error_opening_dm">Something went wrong while we were creating this conversation…</string>
<string name="retry">Retry</string>
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <string name="submit">Submit</string>
......
...@@ -155,6 +155,43 @@ ...@@ -155,6 +155,43 @@
</style> </style>
<!-- End chat list --> <!-- End chat list -->
<!-- User details -->
<style name="UserDetails.TextView.Name" parent="TextAppearance.AppCompat">
<item name="android:textSize">18sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@color/colorUserDetailsNameText</item>
</style>
<style name="UserDetails.TextView.Username" parent="TextAppearance.AppCompat">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorUserDetailsUsernameText</item>
</style>
<style name="UserDetails.TextView.Actions" parent="TextAppearance.AppCompat">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorAccent</item>
</style>
<style name="UserDetails.TextView.Title" parent="TextAppearance.AppCompat">
<item name="android:textSize">12sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorAuthenticationSecondaryText</item>
</style>
<style name="UserDetails.TextView.Description" parent="TextAppearance.AppCompat">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorPrimary</item>
</style>
<!-- End user details-->
<style name="EditText.Password" parent="TextAppearance.AppCompat"> <style name="EditText.Password" parent="TextAppearance.AppCompat">
<!-- Hint color and label color in FALSE state --> <!-- Hint color and label color in FALSE state -->
<item name="android:textColorHint">@color/colorPrimaryDark</item> <item name="android:textColorHint">@color/colorPrimaryDark</item>
......
...@@ -51,6 +51,7 @@ ext { ...@@ -51,6 +51,7 @@ ext {
markwon : '2.0.0', markwon : '2.0.0',
aVLoadingIndicatorView: '2.1.3', aVLoadingIndicatorView: '2.1.3',
glide : '4.8.0', glide : '4.8.0',
glideTransformations : '4.0.0',
// For wearable // For wearable
wear : '2.3.0', wear : '2.3.0',
...@@ -118,6 +119,7 @@ ext { ...@@ -118,6 +119,7 @@ ext {
frescoImageViewer : "com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}", frescoImageViewer : "com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}",
glide : "com.github.bumptech.glide:glide:${versions.glide}", glide : "com.github.bumptech.glide:glide:${versions.glide}",
glideProcessor : "com.github.bumptech.glide:compiler:${versions.glide}", glideProcessor : "com.github.bumptech.glide:compiler:${versions.glide}",
glideTransformations : "jp.wasabeef:glide-transformations:${versions.glideTransformations}",
markwon : "ru.noties:markwon:${versions.markwon}", markwon : "ru.noties:markwon:${versions.markwon}",
......
...@@ -74,7 +74,7 @@ class SuggestionsView : FrameLayout, TextWatcher { ...@@ -74,7 +74,7 @@ class SuggestionsView : FrameLayout, TextWatcher {
// If we don't have any adapter bound to any token bail out. // If we don't have any adapter bound to any token bail out.
if (adaptersByToken.isEmpty()) return if (adaptersByToken.isEmpty()) return
if (editor?.get() != null && editor?.get()?.selectionStart ?: 0 <= completionOffset.get()) { if (editor?.get() != null && editor?.get()?.selectionStart ?: 0 < completionOffset.get()) {
completionOffset.set(NO_STATE_INDEX) completionOffset.set(NO_STATE_INDEX)
collapse() collapse()
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment