Unverified Commit 35a8ab72 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge branch 'beta' into develop

parents d18051a7 7f6cf4ff
......@@ -140,6 +140,7 @@ dependencies {
implementation libraries.frescoAnimatedWebP
implementation libraries.glide
implementation libraries.glideTransformations
kapt libraries.kotshiCompiler
implementation libraries.kotshiApi
......
......@@ -78,11 +78,6 @@
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".chatdetails.ui.ChatDetailsActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".chatinformation.ui.MessageInfoActivity"
android:theme="@style/AppTheme"
......@@ -93,16 +88,6 @@
android:name=".settings.password.ui.PasswordActivity"
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
android:name=".push.DirectReplyReceiver"
android:enabled="true"
......
......@@ -16,9 +16,9 @@ sealed class ScreenViewEvent(val screenName: String) {
object ChatRoom : ScreenViewEvent("ChatRoomFragment")
object ChatRooms : ScreenViewEvent("ChatRoomsFragment")
object CreateChannel : ScreenViewEvent("CreateChannelFragment")
object UserDetails : ScreenViewEvent("UserDetailsFragment")
object FavoriteMessages : ScreenViewEvent("FavoriteMessagesFragment")
object Files : ScreenViewEvent("FilesFragment")
object MemberBottomSheet : ScreenViewEvent("MemberBottomSheetFragment")
object Members : ScreenViewEvent("MembersFragment")
object Mentions : ScreenViewEvent("MentionsFragment")
object MessageInfo : ScreenViewEvent("MessageInfoFragment")
......
......@@ -5,7 +5,8 @@ import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider {
@Module
abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = [TwoFAFragmentModule::class])
@PerFragment
......
package chat.rocket.android.chatdetails.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatdetails.presentation.ChatDetailsView
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.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class ChatDetailsFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun chatDetailsView(frag: ChatDetailsFragment): ChatDetailsView {
......@@ -27,16 +20,4 @@ class ChatDetailsFragmentModule {
@Provides
@PerFragment
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
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.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
......@@ -10,11 +11,12 @@ import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getInfo
import chat.rocket.core.model.Room
import timber.log.Timber
import javax.inject.Inject
class ChatDetailsPresenter @Inject constructor(
private val view: ChatDetailsView,
private val navigator: ChatDetailsNavigator,
private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory
......@@ -29,11 +31,11 @@ class ChatDetailsPresenter @Inject constructor(
val room = retryIO("getInfo($chatRoomId, null, $chatRoomType") {
client.getInfo(chatRoomId, null, roomTypeOf(chatRoomType))
}
view.displayDetails(roomToChatDetails(room))
} catch(e: Exception) {
e.message.let {
view.showMessage(it!!)
} catch(exception: Exception) {
Timber.e(exception)
exception.message?.let {
view.showMessage(it)
}.ifNull {
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
import DrawableHelper
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
......@@ -15,6 +16,7 @@ import chat.rocket.android.chatdetails.presentation.ChatDetailsPresenter
import chat.rocket.android.chatdetails.presentation.ChatDetailsView
import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModel
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.showToast
import chat.rocket.android.util.extensions.ui
......@@ -32,7 +34,7 @@ fun newInstance(
disableMenu: Boolean
): ChatDetailsFragment {
return ChatDetailsFragment().apply {
arguments = Bundle(1).apply {
arguments = Bundle(4).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed)
......@@ -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_DISABLE_MENU = "BUNDLE_DISABLE_MENU"
class ChatDetailsFragment: Fragment(), ChatDetailsView {
class ChatDetailsFragment : Fragment(), ChatDetailsView {
@Inject
lateinit var presenter: ChatDetailsPresenter
@Inject
lateinit var factory: ChatDetailsViewModelFactory
private var adapter: ChatDetailsAdapter? = null
private lateinit var viewModel: ChatDetailsViewModel
private var chatRoomId: String? = null
......@@ -72,13 +72,15 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED)
disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_chat_details)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
......@@ -94,9 +96,12 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
val text = room.name
name.text = text
bindImage(chatRoomType!!)
content_topic.text = if (room.topic.isNullOrEmpty()) getString(R.string.msg_no_topic) else room.topic
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
content_topic.text =
if (room.topic.isNullOrEmpty()) getString(R.string.msg_no_topic) else room.topic
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 {
it.addOption(getString(R.string.msg_mentions), R.drawable.ic_at_black_20dp) {
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!!)
}
}
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!!)
}
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!!)
}
}
......@@ -178,9 +192,9 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
layoutManager = LinearLayoutManager(it)
addItemDecoration(
DividerItemDecoration(
it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)
it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)
)
)
itemAnimator = DefaultItemAnimator()
......@@ -192,9 +206,9 @@ class ChatDetailsFragment: Fragment(), ChatDetailsView {
}
private fun setupToolbar() {
(activity as ChatDetailsActivity).let {
it.setNavigationIcon(R.drawable.ic_close_white_24dp)
it.setToolbarTitle(getString(R.string.title_channel_details))
with((activity as ChatRoomActivity)) {
hideToolbarChatRoomIcon()
showToolbarTitle(getString(R.string.title_channel_details))
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.adapter
import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.*
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
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.util.extensions.inflate
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.ButtonAction
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import timber.log.Timber
import java.security.InvalidParameterException
......@@ -22,7 +28,8 @@ class ChatRoomAdapter(
private val roomName: String? = null,
private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null
private val reactionListener: EmojiReactionListener? = null,
private val navigator: ChatRoomNavigator? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseUiModel<*>>()
......@@ -34,7 +41,11 @@ class ChatRoomAdapter(
return when (viewType.toViewType()) {
BaseUiModel.ViewType.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 -> {
val view = parent.inflate(R.layout.message_url_preview)
......@@ -42,11 +53,20 @@ class ChatRoomAdapter(
}
BaseUiModel.ViewType.ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment)
AttachmentViewHolder(view, actionsListener, reactionListener, actionAttachmentOnClickListener)
AttachmentViewHolder(
view,
actionsListener,
reactionListener,
actionAttachmentOnClickListener
)
}
BaseUiModel.ViewType.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)
}
}
......@@ -117,7 +137,8 @@ class ChatRoomAdapter(
fun prependData(dataSet: List<BaseUiModel<*>>) {
//---At first we will update all already saved elements with received updated ones
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) {
this.dataSet[matchedIndex] = newItem
notifyItemChanged(matchedIndex)
......@@ -266,7 +287,12 @@ class ChatRoomAdapter(
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)
......
package chat.rocket.android.chatroom.adapter
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.text.Spannable
......@@ -11,7 +10,6 @@ import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.userdetails.ui.userDetailsIntent
import chat.rocket.core.model.isSystemMessage
import com.bumptech.glide.load.resource.gif.GifDrawable
import kotlinx.android.synthetic.main.avatar.view.*
......@@ -20,7 +18,8 @@ import kotlinx.android.synthetic.main.item_message.view.*
class MessageViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null
reactionListener: EmojiReactionListener? = null,
private val avatarListener: (String) -> Unit
) : BaseViewHolder<MessageUiModel>(itemView, listener, reactionListener), Drawable.Callback {
init {
......@@ -73,24 +72,14 @@ class MessageViewHolder(
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 {
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?) {
with(itemView) {
text_content.removeCallbacks(what)
......
......@@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment
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.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager
......@@ -16,16 +15,11 @@ import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module
class ChatRoomFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
......@@ -38,12 +32,6 @@ class ChatRoomFragmentModule {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
@Provides
@PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
......@@ -71,6 +59,12 @@ class ChatRoomFragmentModule {
@Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor
): 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
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class ChatRoomModule {
@Provides
@PerActivity
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
import dagger.android.ContributesAndroidInjector
@Module abstract class MessageServiceProvider {
@ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideMessageService(): MessageService
}
\ No newline at end of file
package chat.rocket.android.chatroom.presentation
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.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.chatRoomIntent
......@@ -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.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT
import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.userdetails.ui.TAG_USER_DETAILS_FRAGMENT
import chat.rocket.android.util.extensions.addFragmentBackStack
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(
chatRoomId: String,
chatRoomType: String,
isChatRoomSubscribed: Boolean,
isMenuDisabled: Boolean
) {
activity.startActivity(
activity.chatDetailsIntent(
activity.addFragmentBackStack(TAG_CHAT_DETAILS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.chatdetails.ui.newInstance(
chatRoomId,
chatRoomType,
isChatRoomSubscribed,
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() {
......
......@@ -40,6 +40,7 @@ import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
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.ChatRoomView
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
......@@ -68,6 +69,7 @@ import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.createImageFile
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.fadeOut
import chat.rocket.android.util.extensions.getBitmpap
......@@ -85,6 +87,7 @@ import dagger.android.support.AndroidSupportInjection
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
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_row_item.view.*
import kotlinx.android.synthetic.main.fragment_chat_room.*
......@@ -145,13 +148,14 @@ internal const val MENU_ACTION_SHOW_DETAILS = 2
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener,
ChatRoomAdapter.OnActionSelected, Drawable.Callback {
@Inject
lateinit var presenter: ChatRoomPresenter
@Inject
lateinit var parser: MessageParser
@Inject
lateinit var analyticsManager: AnalyticsManager
@Inject
lateinit var navigator: ChatRoomNavigator
private lateinit var adapter: ChatRoomAdapter
internal lateinit var chatRoomId: String
private lateinit var chatRoomName: String
......@@ -284,7 +288,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
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(
......@@ -1057,7 +1061,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
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?) {
......
......@@ -108,7 +108,7 @@ private fun ChatRoomFragment.setupDetailsMenuItem(menu: Menu) {
Menu.NONE,
MENU_ACTION_SHOW_DETAILS,
Menu.NONE,
"Channel Details"
R.string.title_channel_details
).setIcon(R.drawable.ic_info_outline_white_24dp)
.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
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity
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.ui.MessageInfoActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
......@@ -38,14 +36,14 @@ import chat.rocket.android.server.ui.ChangeServerActivity
import chat.rocket.android.settings.di.SettingsFragmentProvider
import chat.rocket.android.settings.password.di.PasswordFragmentProvider
import chat.rocket.android.settings.password.ui.PasswordActivity
import chat.rocket.android.userdetails.di.UserDetailsModule
import chat.rocket.android.userdetails.ui.UserDetailsActivity
import chat.rocket.android.userdetails.di.UserDetailsFragmentProvider
import chat.rocket.android.webview.adminpanel.di.AdminPanelWebViewFragmentProvider
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(
modules = [AuthenticationModule::class,
......@@ -77,17 +75,9 @@ abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(
modules = [
ChatRoomModule::class,
ChatRoomFragmentProvider::class
]
)
abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity
@ContributesAndroidInjector(
modules = [
ChatDetailsModule::class,
modules = [ChatRoomModule::class,
ChatRoomFragmentProvider::class,
UserDetailsFragmentProvider::class,
ChatDetailsFragmentProvider::class,
MembersFragmentProvider::class,
MentionsFragmentProvider::class,
......@@ -96,7 +86,7 @@ abstract class ActivityBuilder {
FilesFragmentProvider::class
]
)
abstract fun bindChatDetailsActivity(): ChatDetailsActivity
abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity
@ContributesAndroidInjector(modules = [PasswordFragmentProvider::class])
......@@ -113,8 +103,4 @@ abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(modules = [DrawModule::class])
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) {
}
}
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>) {
launch(dbManagerContext) {
val list = ArrayList<BaseUserEntity>(users.size)
......
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.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module
class FavoriteMessagesFragmentModule {
......@@ -21,20 +14,4 @@ class FavoriteMessagesFragmentModule {
fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView {
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
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
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.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
......@@ -116,9 +115,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
}
private fun setupToolbar() {
(activity as ChatDetailsActivity).let {
it.setToolbarTitle(getString(R.string.title_favorite_messages))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
(activity as ChatRoomActivity).showToolbarTitle(getString(R.string.title_favorite_messages))
}
}
\ No newline at end of file
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.files.presentation.FilesView
import chat.rocket.android.files.ui.FilesFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class FilesFragmentModule {
......@@ -17,20 +14,4 @@ class FilesFragmentModule {
fun provideFilesView(frag: FilesFragment): FilesView {
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
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
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.presentation.FilesPresenter
import chat.rocket.android.files.presentation.FilesView
......@@ -152,9 +152,11 @@ class FilesFragment : Fragment(), FilesView {
}
private fun setupToolbar(totalFiles: Long) {
(activity as ChatDetailsActivity).let {
it.setToolbarTitle(getString(R.string.title_files_total, totalFiles))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
(activity as ChatRoomActivity).showToolbarTitle(
(getString(
R.string.title_files_total,
totalFiles
))
)
}
}
\ No newline at end of file
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.members.presentation.MembersNavigator
import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.ui.MembersFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class MembersFragmentModule {
......@@ -19,24 +14,4 @@ class MembersFragmentModule {
fun membersView(frag: MembersFragment): MembersView {
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
import chat.rocket.android.members.ui.MembersFragment
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.android.ContributesAndroidInjector
......@@ -12,9 +11,4 @@ abstract class MembersFragmentProvider {
@ContributesAndroidInjector(modules = [MembersFragmentModule::class])
@PerFragment
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
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
......@@ -18,7 +18,7 @@ import javax.inject.Named
class MembersPresenter @Inject constructor(
private val view: MembersView,
private val navigator: MembersNavigator,
private val navigator: ChatRoomNavigator,
private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String,
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
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
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.members.adapter.MembersAdapter
import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.presentation.MembersView
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.showToast
import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject
......@@ -120,20 +122,31 @@ class MembersFragment : Fragment(), MembersView {
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager = linearLayoutManager
recycler_view.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL))
recycler_view.layoutManager = LinearLayoutManager(context)
recycler_view.addItemDecoration(
DividerItemDecoration(
it,
DividerItemDecoration.HORIZONTAL
)
)
recycler_view.adapter = adapter
}
}
private fun setupToolbar(totalMembers: Long? = null) {
(activity as ChatDetailsActivity).let {
with((activity as ChatRoomActivity)) {
if (totalMembers != null) {
it.setToolbarTitle(getString(R.string.title_counted_members, totalMembers))
showToolbarTitle(
(getString(
R.string.title_counted_members,
totalMembers
))
)
} 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
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.mentions.presentention.MentionsView
import chat.rocket.android.mentions.ui.MentionsFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class MentionsFragmentModule {
......@@ -17,20 +14,4 @@ class MentionsFragmentModule {
fun provideMentionsView(frag: MentionsFragment): MentionsView {
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
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
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.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.mentions.presentention.MentionsPresenter
......@@ -37,13 +37,12 @@ internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MentionsFragment : Fragment(), MentionsView {
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject
lateinit var presenter: MentionsPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -122,9 +121,6 @@ class MentionsFragment : Fragment(), MentionsView {
}
private fun setupToolbar() {
(activity as ChatDetailsActivity).let {
it.setToolbarTitle(getString(R.string.msg_mentions))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
(activity as ChatRoomActivity).showToolbarTitle((getString(R.string.msg_mentions)))
}
}
\ No newline at end of file
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.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class PinnedMessagesFragmentModule {
......@@ -17,20 +14,4 @@ class PinnedMessagesFragmentModule {
fun providePinnedMessagesView(frag: PinnedMessagesFragment): PinnedMessagesView {
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
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
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.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter
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.showToast
import chat.rocket.android.util.extensions.ui
......@@ -123,9 +121,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
}
private fun setupToolbar() {
(activity as ChatDetailsActivity).let {
it.setToolbarTitle(getString(R.string.title_pinned_messages))
it.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
}
(activity as ChatRoomActivity).showToolbarTitle((getString(R.string.title_pinned_messages)))
}
}
\ 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
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
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.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.model.userStatusOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.createDirectMessage
import chat.rocket.core.model.ChatRoom
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext
import timber.log.Timber
import javax.inject.Inject
......@@ -23,141 +23,98 @@ class UserDetailsPresenter @Inject constructor(
private val view: UserDetailsView,
private val dbManager: DatabaseManager,
private val strategy: CancelStrategy,
private val navigator: ChatRoomNavigator,
serverInteractor: GetConnectingServerInteractor,
factory: ConnectionManagerFactory
) {
private var currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer)
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) {
launchUI(strategy) {
try {
val user = withContext(CommonPool) {
dbManager.userDao().getUser(id = userId)
}
user?.let { u ->
val openedChatRooms = chatRoomByName(name = u.name)
val avatarUrl = u.username?.let { currentServer.avatarUrl(avatar = it) }
val chatRoom: ChatRoom? = openedChatRooms.firstOrNull()
view.showLoading()
dbManager.getUser(userId)?.let {
userEntity = it
val avatarUrl =
userEntity.username?.let { currentServer.avatarUrl(avatar = it) }
val username = userEntity.username
val name = userEntity.name
val utcOffset =
userEntity.utcOffset // TODO Convert UTC and display like the mockup
view.showUserDetails(
avatarUrl = avatarUrl,
username = u.username,
name = u.name,
utcOffset = u.utcOffset,
status = u.status,
chatRoom = chatRoom
)
if (avatarUrl != null && username != null && name != null && utcOffset != null) {
view.showUserDetails(
avatarUrl = avatarUrl,
name = name,
username = username,
status = userEntity.status,
utcOffset = utcOffset.toString()
)
} else {
throw Exception()
}
}
} catch (ex: Exception) {
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)
}
fun createDirectMessage(username: String) {
launchUI(strategy) {
try {
view.showLoading()
interactor.refreshChatRooms()
withContext(DefaultDispatcher) {
val directMessage = retryIO("createDirectMessage($username") {
client.createDirectMessage(username)
}
val userEntity = withContext(CommonPool) {
dbManager.userDao().getUser(id = id)
}
val chatRoomEntity = ChatRoomEntity(
id = directMessage.id,
name = userEntity.username ?: userEntity.name.orEmpty(),
description = null,
type = RoomType.DIRECT_MESSAGE,
fullname = userEntity.name,
subscriptionId = "",
updatedAt = directMessage.updatedAt
)
if (userEntity != null) {
val chatRoom = ChatRoom(
id = result.id,
type = roomTypeOf(RoomType.DIRECT_MESSAGE),
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,
groupMentions = null,
userMentions = null,
lastMessage = null,
lastSeen = null,
topic = null,
announcement = null,
roles = null,
unread = 0,
readonly = false,
muted = null,
subscriptionId = "",
timestamp = null,
updatedAt = result.updatedAt,
user = null
)
dbManager.insertOrReplaceRoom(chatRoomEntity)
withContext(CommonPool + strategy.jobs) {
dbManager.chatRoomDao().insertOrReplace(chatRoom = ChatRoomEntity(
id = chatRoom.id,
name = chatRoom.name,
description = chatRoom.description,
type = chatRoom.type.toString(),
fullname = chatRoom.fullName,
subscriptionId = chatRoom.subscriptionId,
updatedAt = chatRoom.updatedAt
))
}
interactor.refreshChatRooms()
view.toDirectMessage(chatRoom = chatRoom)
}
} catch (ex: Exception) {
Timber.e(ex)
view.onOpenDirectMessageError()
}
}
private suspend fun chatRoomByName(name: String? = null): List<ChatRoom> = withContext(CommonPool) {
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
)
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) {
Timber.e(ex)
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
......
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?)
fun toDirectMessage(chatRoom: ChatRoom)
fun onOpenDirectMessageError()
/**
* Shows user detail.
*
* @param avatarUrl The user avatar URL.
* @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
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
......@@ -22,12 +23,16 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
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) {
var flags = view.systemUiVisibility
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
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 @@
android:shape="rectangle">
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
android:bottom="3dp"
android:left="3dp"
android:right="3dp"
android:top="3dp" />
<solid android:color="@android:color/white" />
<solid android:color="@color/color_white" />
<corners android:radius="25px" />
<corners android:radius="6dp" />
</shape>
......@@ -3,6 +3,7 @@
android:height="24dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#1d74f5"
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 @@
<string name="notif_success_sending">Nachricht gesendet nach %1$s!</string>
<string name="read_by">Gelesen von</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>
<!-- User Details -->
<string name="message">Nachrticht</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 -->
<string name="submit">Senden</string>
......
......@@ -335,10 +335,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation-->
<!-- User Details -->
<string name="message">Message</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 -->
<string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
......@@ -338,10 +338,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string>
<!-- User Details -->
<string name="message">Message</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 -->
<string name="submit">Submit</string> <!-- TODO - Add proper translation -->
......
......@@ -337,10 +337,7 @@
<string name="message_room_changed_privacy">%2$s ने रूम का प्रकार बदलकर %1$s किया </string>
<!-- User Details -->
<string name="message">संदेश</string>
<string name="timezone">समय क्षेत्र</string>
<string name="error_opening_dm">जब हम इस बातचीत को बना रहे थे तो कुछ गड़बड़ हुई …</string>
<string name="retry">पुन: प्रयास करें</string>
<!-- Report -->
<string name="submit">जमा करें</string>
......
......@@ -332,10 +332,7 @@
<string name="message_room_changed_privacy">Il tipo di stanza è cambiato in: %1$s da %2$s</string>
<!-- User Details -->
<string name="message">Messaggio</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 -->
<string name="submit">Invia</string>
......
......@@ -340,10 +340,7 @@
<string name="message_room_changed_privacy">ルームタイプを %2$s から %1$s に変更しました</string>
<!-- User Details -->
<string name="message">Message</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 -->
<string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
......@@ -335,10 +335,7 @@
<string name="message_room_changed_privacy">O tipo da sala mudou para: %1$s por %2$s</string>
<!-- User Details -->
<string name="message">Mensagem</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 -->
<string name="submit">Enviar</string>
......
......@@ -334,10 +334,7 @@
<string name="message_room_changed_privacy">Тип канала изменен на: %1$s пользователем %2$s</string>
<!-- User Details -->
<string name="message">Сообщение</string>
<string name="timezone">Часовой пояс</string>
<string name="error_opening_dm">При создании этого диалога что-то пошло не так...</string>
<string name="retry">Повторить</string>
<string name="timezone">Часовой поясe</string>
<!-- Report -->
<string name="submit">Отправить</string>
......
......@@ -337,10 +337,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation-->
<!-- User Details -->
<string name="message">Message</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 -->
<string name="submit">Submit</string> <!--TODO - Add proper translation-->
......
......@@ -335,10 +335,7 @@
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!-- TODO - Add proper translation -->
<!-- User Details -->
<string name="message">Message</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 -->
<string name="submit">Submit</string> <!-- TODO - Add proper translation -->
......
......@@ -13,6 +13,8 @@
<color name="colorTimestampText">#FF9DA2A9</color>
<color name="colorTimestampTextUnread">#FF5699FF</color>
<color name="colorLastMessageText">#99000000</color>
<color name="colorUserDetailsNameText">#FF0C0D0F</color>
<color name="colorUserDetailsUsernameText">#8B000000</color>
<!-- User status colors -->
<color name="colorUserStatusOnline">#2FE1A8</color>
......
......@@ -351,11 +351,8 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="foss" translatable="false">(FOSS)</string>
<!-- User Details -->
<string name="message">Message</string>
<string name="timezone">Timezone</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 -->
<string name="submit">Submit</string>
......
......@@ -155,6 +155,43 @@
</style>
<!-- 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">
<!-- Hint color and label color in FALSE state -->
<item name="android:textColorHint">@color/colorPrimaryDark</item>
......
......@@ -51,6 +51,7 @@ ext {
markwon : '2.0.0',
aVLoadingIndicatorView: '2.1.3',
glide : '4.8.0',
glideTransformations : '4.0.0',
// For wearable
wear : '2.3.0',
......@@ -118,6 +119,7 @@ ext {
frescoImageViewer : "com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}",
glide : "com.github.bumptech.glide:glide:${versions.glide}",
glideProcessor : "com.github.bumptech.glide:compiler:${versions.glide}",
glideTransformations : "jp.wasabeef:glide-transformations:${versions.glideTransformations}",
markwon : "ru.noties:markwon:${versions.markwon}",
......
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