Unverified Commit 36598c5d authored by divyanshu bhargava's avatar divyanshu bhargava Committed by GitHub

Merge pull request #35 from RocketChat/develop

merge
parents 124b8519 35ed646c
......@@ -114,8 +114,6 @@ dependencies {
implementation libraries.markwon
implementation libraries.sheetMenu
implementation libraries.aVLoadingIndicatorView
implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
......
......@@ -79,11 +79,6 @@
android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" />
<!-- TODO: Change to fragment -->
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
<receiver
android:name=".push.DirectReplyReceiver"
android:enabled="true"
......
package chat.rocket.android.about.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_about.*
class AboutFragment : Fragment() {
companion object {
fun newInstance() = AboutFragment()
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_about, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupViews()
}
private fun setupViews() {
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
}
private fun setupToolbar() {
val toolbar = (activity as MainActivity).toolbar
toolbar.title = getString(R.string.title_about)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
toolbar.setNavigationOnClickListener {
this.activity?.onBackPressed()
}
}
override fun onStop() {
super.onStop()
(activity as MainActivity).toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
}
}
package chat.rocket.android.chatroom.adapter
import androidx.recyclerview.widget.RecyclerView
import android.view.ContextThemeWrapper
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.toList
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager
import ru.whalemare.sheetmenu.extension.inflate
import ru.whalemare.sheetmenu.extension.toList
abstract class BaseViewHolder<T : BaseUiModel<*>>(
itemView: View,
......@@ -89,8 +90,15 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star)
isChecked = isStarred
}
val adapter = ActionListAdapter(menuItems, this@BaseViewHolder)
BottomSheetMenu(adapter).show(view.context)
view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
}
}
}
......
package chat.rocket.android.chatroom.ui.bottomsheet
import com.google.android.material.bottomsheet.BottomSheetDialog
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem
import ru.whalemare.sheetmenu.SheetMenu
import ru.whalemare.sheetmenu.adapter.MenuAdapter
class BottomSheetMenu(adapter: MenuAdapter) : SheetMenu(adapter = adapter) {
override fun processRecycler(recycler: RecyclerView, dialog: BottomSheetDialog) {
if (layoutManager == null) {
layoutManager = LinearLayoutManager(recycler.context)
}
// Superclass SheetMenu adapter property is nullable MenuAdapter? but this class enforces
// passing one at the constructor, so we assume it's always non-null.
val adapter = adapter!!
val callback = adapter.callback
adapter.callback = MenuItem.OnMenuItemClickListener {
callback?.onMenuItemClick(it)
dialog.cancel()
true
}
recycler.adapter = adapter
recycler.layoutManager = layoutManager
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.message_action_item.view.*
import kotlinx.android.synthetic.main.message_bottomsheet.*
class MessageActionsBottomSheet : BottomSheetDialogFragment() {
private lateinit var adapter: MessageActionAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.message_bottomsheet, container, false)
}
fun addItems(items: List<MenuItem>, itemClickListener: MenuItem.OnMenuItemClickListener) {
adapter = MessageActionAdapter()
adapter.addItems(items, ActionItemClickListener(dismissAction = { dismiss() },
itemClickListener = itemClickListener))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomsheet_recycler_view.layoutManager = LinearLayoutManager(context)
bottomsheet_recycler_view.adapter = adapter
}
private class ActionItemClickListener(
val dismissAction: () -> Unit,
val itemClickListener: MenuItem.OnMenuItemClickListener
)
private class MessageActionAdapter : RecyclerView.Adapter<MessageActionViewHolder>() {
private lateinit var itemClickListener: ActionItemClickListener
private val menuItems = mutableListOf<MenuItem>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageActionViewHolder {
return MessageActionViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.message_action_item, parent, false)
)
}
override fun getItemCount() = menuItems.size
override fun onBindViewHolder(holder: MessageActionViewHolder, position: Int) {
holder.bind(menuItems[position], itemClickListener)
}
fun addItems(items: List<MenuItem>, itemClickListener: ActionItemClickListener) {
this.itemClickListener = itemClickListener
menuItems.clear()
menuItems.addAll(items)
}
}
private class MessageActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: MenuItem, itemClickListener: ActionItemClickListener) {
with(itemView) {
message_action_title.text = item.title
message_action_icon.setImageDrawable(item.icon)
setOnClickListener {
itemClickListener.itemClickListener.onMenuItemClick(item)
itemClickListener.dismissAction.invoke()
}
}
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet.adapter
import android.view.MenuItem
import chat.rocket.android.R
import chat.rocket.android.util.extensions.setVisible
/**
* An adapter for bottomsheet menu that lists all the actions that could be taken over a chat message.
*/
class ActionListAdapter(
menuItems: List<MenuItem> = emptyList(),
callback: MenuItem.OnMenuItemClickListener
) : ListBottomSheetAdapter(menuItems = menuItems, callback = callback) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = menuItems[position]
if (showIcons) {
holder.imageIcon.setVisible(item.icon != null)
} else {
holder.imageIcon.setVisible(false)
}
holder.imageIcon.setImageDrawable(item.icon)
holder.textTitle.text = item.title
holder.itemView.setOnClickListener {
callback?.onMenuItemClick(item)
}
val deleteTextColor = holder.itemView.context.resources.getColor(R.color.colorRed)
val color = if (item.itemId == R.id.action_message_delete) {
deleteTextColor
} else {
textColors.get(item.itemId)
}
holder.textTitle.setTextColor(color)
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet.adapter
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.annotation.IdRes
import android.util.SparseIntArray
import android.view.MenuItem
import chat.rocket.android.R
import ru.whalemare.sheetmenu.adapter.MenuAdapter
/**
* A regular bottomsheet adapter with added possibility to hide or show a menu item given its item id.
* Also added the possibility to change text colors for the menu items.
*/
open class ListBottomSheetAdapter(menuItems: List<MenuItem> = emptyList(), callback: MenuItem.OnMenuItemClickListener) :
MenuAdapter(menuItems = menuItems, callback = callback, itemLayoutId = R.layout.item_linear, showIcons = true) {
// Maps menu item ids to colors.
protected val textColors: SparseIntArray = SparseIntArray(menuItems.size)
init {
for (item in menuItems) {
textColors.put(item.itemId, Color.BLACK)
}
}
/**
* Hide a menu item and disable it.
*
* @param itemId The id of the menu item to disable and hide.
*/
fun hideMenuItem(@IdRes itemId: Int) {
menuItems.firstOrNull { it.itemId == itemId }?.apply {
setVisible(false)
setEnabled(false)
}
}
/**
* Show a menu item and enable it.
*
* @param itemId The id of the menu item to enable and show.
*/
fun showMenuItem(@IdRes itemId: Int) {
menuItems.firstOrNull { it.itemId == itemId }?.apply {
setVisible(true)
setEnabled(true)
}
}
/**
* Change a menu item text color given by its id to the given color.
*
* @param itemId The id of menu item.
* @param color The color (not the resource color id) of the menu item.
*/
fun setMenuItemTextColor(@IdRes itemId: Int, @ColorInt color: Int) {
val itemIndex = menuItems.indexOfFirst { it.itemId == itemId }
if (itemIndex > -1) {
textColors.put(itemId, color)
}
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.date
import chat.rocket.android.util.extensions.localDateTime
......@@ -111,7 +112,7 @@ class RoomUiModelMapper(
}
private fun mapName(name: String, fullName: String?, unread: Boolean): CharSequence {
val roomName = if (settings.useRealName()) {
val roomName = if (settings.useSpecialCharsOnRoom() || settings.useRealName()) {
fullName ?: name
} else {
name
......
......@@ -6,6 +6,7 @@ import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.util.extensions.launchUI
......@@ -36,13 +37,11 @@ class ChatRoomsPresenter @Inject constructor(
fun loadChatRoom(chatRoom: chat.rocket.android.db.model.ChatRoom) {
with(chatRoom.chatRoom) {
val isDirectMessage = roomTypeOf(type) is RoomType.DirectMessage
val roomName = if (isDirectMessage
&& fullname != null
&& settings.useRealName()) {
fullname!!
} else {
name
}
val roomName = if (settings.useSpecialCharsOnRoom() || (isDirectMessage && settings.useRealName())) {
fullname ?: name
} else {
name
}
launchUI(strategy) {
val myself = getCurrentUser()
......
......@@ -18,6 +18,7 @@ import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.util.extensions.*
import chat.rocket.common.model.RoomType
import chat.rocket.core.model.ChatRoom
......@@ -123,7 +124,7 @@ class ChatRoomsAdapter(
}
private fun chatRoomName(chatRoom: ChatRoom): String {
return if (settings.useRealName()) {
return if (settings.useSpecialCharsOnRoom() || settings.useRealName()) {
chatRoom.fullName ?: chatRoom.name
} else {
chatRoom.name
......
......@@ -148,7 +148,8 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
inflater.inflate(R.menu.chatrooms, menu)
val searchItem = menu.findItem(R.id.action_search)
searchView = searchItem?.actionView as SearchView
searchView = searchItem?.actionView as? SearchView
searchView?.setIconifiedByDefault(false)
searchView?.maxWidth = Integer.MAX_VALUE
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
......@@ -176,7 +177,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
0 -> R.id.radio_sort_alphabetical
else -> R.id.radio_sort_activity
})
radioGroup.setOnCheckedChangeListener({ _, checkedId ->
radioGroup.setOnCheckedChangeListener { _, checkedId ->
run {
SharedPreferenceHelper.putInt(Constants.CHATROOM_SORT_TYPE_KEY, when (checkedId) {
R.id.radio_sort_alphabetical -> 0
......@@ -184,23 +185,21 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
else -> 1
})
}
})
}
groupByTypeCheckBox.isChecked = groupByType
groupByTypeCheckBox.setOnCheckedChangeListener({ _, isChecked ->
groupByTypeCheckBox.setOnCheckedChangeListener { _, isChecked ->
SharedPreferenceHelper.putBoolean(Constants.CHATROOM_GROUP_BY_TYPE_KEY, isChecked)
})
}
val dialogSort = AlertDialog.Builder(context)
AlertDialog.Builder(context)
.setTitle(R.string.dialog_sort_title)
.setView(dialogLayout)
.setPositiveButton("Done", { dialog, _ ->
.setPositiveButton("Done") { dialog, _ ->
invalidateQueryOnSearch()
updateSort()
dialog.dismiss()
})
dialogSort.show()
}.show()
}
}
return super.onOptionsItemSelected(item)
......
......@@ -10,11 +10,11 @@ import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
import chat.rocket.android.chatroom.di.ChatRoomModule
import chat.rocket.android.chatroom.di.FavoriteMessagesFragmentProvider
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatrooms.di.ChatRoomsFragmentProvider
import chat.rocket.android.createchannel.di.CreateChannelProvider
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.favoritemessages.di.FavoriteMessagesFragmentProvider
import chat.rocket.android.files.di.FilesFragmentProvider
import chat.rocket.android.main.di.MainModule
import chat.rocket.android.main.ui.MainActivity
......
package chat.rocket.android.chatroom.di
package chat.rocket.android.favoritemessages.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.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 {
@Provides
@PerFragment
fun provideJob() = Job()
fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FavoriteMessagesFragment): LifecycleOwner {
return frag
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView {
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
package chat.rocket.android.chatroom.di
package chat.rocket.android.favoritemessages.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
......
......@@ -2,26 +2,27 @@ package chat.rocket.android.favoritemessages.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.ChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getFavoriteMessages
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class FavoriteMessagesPresenter @Inject constructor(
private val view: FavoriteMessagesView,
private val strategy: CancelStrategy,
private val roomsInteractor: ChatRoomsInteractor,
private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String,
private val mapper: UiModelMapper,
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client = factory.create(serverUrl)
private val client: RocketChatClient = factory.create(currentServer)
private var offset: Int = 0
/**
......@@ -33,8 +34,9 @@ class FavoriteMessagesPresenter @Inject constructor(
launchUI(strategy) {
try {
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val favoriteMessages = client.getFavoriteMessages(roomId, it.type, offset)
dbManager.getRoom(roomId)?.let {
val favoriteMessages =
client.getFavoriteMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val messageList = mapper.map(favoriteMessages.result, asNotReversed = true)
view.showFavoriteMessages(messageList)
offset += 1 * 30
......
......@@ -3,34 +3,54 @@ 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.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.files.presentation.FilesView
import chat.rocket.android.files.ui.FilesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module
class FilesFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun provideFilesView(frag: FilesFragment): FilesView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FilesFragment): LifecycleOwner {
return frag
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideFilesView(frag: FilesFragment): FilesView {
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
......@@ -2,28 +2,29 @@ package chat.rocket.android.files.presentation
import androidx.core.net.toUri
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.files.uimodel.FileUiModelMapper
import chat.rocket.android.server.domain.ChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getFiles
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class FilesPresenter @Inject constructor(
private val view: FilesView,
private val strategy: CancelStrategy,
private val roomsInteractor: ChatRoomsInteractor,
private val mapper: FileUiModelMapper,
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
private val view: FilesView,
private val strategy: CancelStrategy,
private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String,
private val mapper: FileUiModelMapper,
val factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client = factory.create(serverUrl)
private val client: RocketChatClient = factory.create(currentServer)
private var offset: Int = 0
/**
......@@ -35,8 +36,8 @@ class FilesPresenter @Inject constructor(
launchUI(strategy) {
try {
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val files = client.getFiles(roomId, it.type, offset)
dbManager.getRoom(roomId)?.let {
val files = client.getFiles(roomId, roomTypeOf(it.chatRoom.type), offset)
val filesUiModel = mapper.mapToUiModelList(files.result)
view.showFiles(filesUiModel, files.total)
offset += 1 * 30
......
......@@ -3,6 +3,7 @@ package chat.rocket.android.helper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.domain.useRealName
import chat.rocket.common.model.RoomType
import chat.rocket.core.model.ChatRoom
......@@ -24,7 +25,11 @@ class MessageHelper @Inject constructor(
is RoomType.LiveChat -> "livechat"
else -> "custom"
}
val name = if (settings.useRealName()) chatRoom.fullName ?: chatRoom.name else chatRoom.name
val name = if (settings.useSpecialCharsOnRoom() || settings.useRealName()) {
chatRoom.fullName ?: chatRoom.name
} else {
chatRoom.name
}
return "[ ]($currentServer/$type/$name?msg=${message.id}) "
}
......
......@@ -4,19 +4,25 @@ import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.ui.ChatRoomActivity
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.members.presentation.MembersNavigator
import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.ui.MembersFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module
class MembersFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun membersView(frag: MembersFragment): MembersView {
return frag
}
@Provides
@PerFragment
......@@ -24,10 +30,24 @@ class MembersFragmentModule {
@Provides
@PerFragment
fun membersView(frag: MembersFragment): MembersView {
return frag
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: MembersFragment): LifecycleOwner {
......
package chat.rocket.android.members.presentation
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.domain.ChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getMembers
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class MembersPresenter @Inject constructor(
private val view: MembersView,
private val navigator: MembersNavigator,
private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String,
private val strategy: CancelStrategy,
private val roomsInteractor: ChatRoomsInteractor,
private val mapper: MemberUiModelMapper,
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl)
private val client: RocketChatClient = factory.create(currentServer)
private var offset: Long = 0
/**
* Loads all the chat room members for the given room id.
*
* @param roomId The id of the room to get chat room members from.
*/
fun loadChatRoomsMembers(roomId: String) {
launchUI(strategy) {
try {
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val members = client.getMembers(it.id, it.type, offset, 60)
dbManager.getRoom(roomId)?.let {
val members =
client.getMembers(roomId, roomTypeOf(it.chatRoom.type), offset, 60)
val memberUiModels = mapper.mapToUiModelList(members.result)
view.showMembers(memberUiModels, members.total)
offset += 1 * 60L
}.ifNull {
Timber.e("Couldn't find a room with id: $roomId at current server")
Timber.e("Couldn't find a room with id: $roomId at current server.")
}
} catch (exception: RocketChatException) {
exception.message?.let {
......@@ -52,12 +58,12 @@ class MembersPresenter @Inject constructor(
}
fun toMemberDetails(memberUiModel: MemberUiModel) {
val avatarUri = memberUiModel.avatarUri.toString()
val realName = memberUiModel.realName.toString()
val username = "@${memberUiModel.username}"
val email = memberUiModel.email ?: ""
val utcOffset = memberUiModel.utcOffset.toString()
navigator.toMemberDetails(avatarUri, realName, username, email, utcOffset)
navigator.toMemberDetails(
memberUiModel.avatarUri.toString(),
memberUiModel.realName.toString(),
"@${memberUiModel.username}",
memberUiModel.email ?: "",
memberUiModel.utcOffset.toString()
)
}
}
\ No newline at end of file
......@@ -3,34 +3,54 @@ 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.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module
class PinnedMessagesFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun providePinnedMessagesView(frag: PinnedMessagesFragment): PinnedMessagesView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: PinnedMessagesFragment): LifecycleOwner {
return frag
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
fun provideDatabaseManager(
factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String
): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun providePinnedMessagesView(frag: PinnedMessagesFragment): PinnedMessagesView {
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
......@@ -2,26 +2,27 @@ package chat.rocket.android.pinnedmessages.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.ChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getPinnedMessages
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class PinnedMessagesPresenter @Inject constructor(
private val view: PinnedMessagesView,
private val strategy: CancelStrategy,
private val roomsInteractor: ChatRoomsInteractor,
private val dbManager: DatabaseManager,
@Named("currentServer") private val currentServer: String,
private val mapper: UiModelMapper,
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client = factory.create(serverUrl)
private val client: RocketChatClient = factory.create(currentServer)
private var offset: Int = 0
/**
......@@ -33,8 +34,9 @@ class PinnedMessagesPresenter @Inject constructor(
launchUI(strategy) {
try {
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val pinnedMessages = client.getPinnedMessages(roomId, it.type, offset)
dbManager.getRoom(roomId)?.let {
val pinnedMessages =
client.getPinnedMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val messageList = mapper.map(pinnedMessages.result, asNotReversed = true)
view.showPinnedMessages(messageList)
offset += 1 * 30
......
......@@ -71,6 +71,7 @@ fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as Str
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
fun PublicSettings.useSpecialCharsOnRoom(): Boolean = this[ALLOW_ROOM_NAME_SPECIAL_CHARS]?.value == true
fun PublicSettings.faviconLarge(): String? = this[FAVICON_512]?.value as String?
fun PublicSettings.favicon(): String? = this[FAVICON_196]?.value as String?
fun PublicSettings.wideTile(): String? = this[WIDE_TILE_310]?.value as String?
......
package chat.rocket.android.server.infraestructure
import android.os.Build
import chat.rocket.android.BuildConfig
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.android.server.domain.TokenRepository
import okhttp3.OkHttpClient
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class RocketChatClientFactory @Inject constructor(private val okHttpClient: OkHttpClient,
private val repository: TokenRepository,
private val logger: PlatformLogger) {
class RocketChatClientFactory @Inject constructor(
private val okHttpClient: OkHttpClient,
private val repository: TokenRepository,
private val logger: PlatformLogger
) {
private val cache = HashMap<String, RocketChatClient>()
fun create(url: String): RocketChatClient {
......@@ -23,6 +27,7 @@ class RocketChatClientFactory @Inject constructor(private val okHttpClient: OkHt
val client = RocketChatClient.create {
httpClient = okHttpClient
restUrl = url
userAgent = "RC Mobile; Android ${Build.VERSION.RELEASE}; v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
tokenRepository = repository
platformLogger = logger
}
......
package chat.rocket.android.settings.about.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.util.extensions.textContent
import kotlinx.android.synthetic.main.about_view.*
import kotlinx.android.synthetic.main.app_bar_password.*
class AboutActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_about)
setupToolbar()
setupViews()
}
private fun setupViews() {
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
}
private fun setupToolbar() {
setSupportActionBar(toolbar)
text_change_password.textContent = getString(R.string.title_about)
}
override fun onBackPressed() {
super.onBackPressed()
finish()
overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return super.onNavigateUp()
}
}
......@@ -9,9 +9,10 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import chat.rocket.android.R
import chat.rocket.android.settings.about.ui.AboutActivity
import chat.rocket.android.about.ui.AboutFragment
import chat.rocket.android.settings.password.ui.PasswordActivity
import chat.rocket.android.settings.presentation.SettingsView
import chat.rocket.android.util.extensions.addFragmentBackStack
import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.fragment_settings.*
import kotlin.reflect.KClass
......@@ -39,7 +40,9 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
startNewActivity(PasswordActivity::class)
}
resources.getString(R.string.title_about) -> {
startNewActivity(AboutActivity::class)
(activity as AppCompatActivity).addFragmentBackStack("AboutFragmnet", R.id.fragment_container){
AboutFragment.newInstance()
}
}
}
}
......
package chat.rocket.android.util.extensions
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import androidx.fragment.app.Fragment
import chat.rocket.android.R
......@@ -86,4 +92,23 @@ fun Fragment.showToast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SH
showToast(getString(resource), duration)
fun Fragment.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) =
activity?.showToast(message, duration)
\ No newline at end of file
activity?.showToast(message, duration)
@SuppressLint("RestrictedApi")
fun Context.inflate(@MenuRes menuRes: Int): Menu {
val menu = MenuBuilder(this)
val menuInflater = SupportMenuInflater(this)
menuInflater.inflate(menuRes, menu)
return menu
}
/**
* Developed by Magora-Systems.com
* @since 2017
* @author Anton Vlasov - whalemare
*/
fun Menu.toList(): List<MenuItem> {
val menuItems = ArrayList<MenuItem>(this.size())
(0 until this.size()).mapTo(menuItems) { this.getItem(it) }
return menuItems
}
\ No newline at end of file
<?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"
android:theme="@style/AppTheme"
tools:context="chat.rocket.android.settings.about.ui.AboutActivity">
<include
android:id="@+id/layout_app_bar"
layout="@layout/app_bar_password" />
<include layout="@layout/about_view"/>
</LinearLayout>
\ 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"
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
tools:context="chat.rocket.android.about.ui.AboutFragment">
<ImageView
android:id="@+id/image_app_name"
......@@ -14,7 +13,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:layout_width="160dp"
......@@ -25,8 +24,7 @@
app:layout_constraintBottom_toTopOf="@id/image_app_name"
android:adjustViewBounds="true"
android:scaleX="1.5"
android:scaleY="1.5"
/>
android:scaleY="1.5" />
<TextView
android:id="@+id/text_version_name"
......@@ -37,7 +35,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_app_name"
android:layout_marginTop="16dp"
android:textColor="@color/colorSecondaryText"/>
android:textColor="@color/colorSecondaryText" />
<TextView
android:id="@+id/text_build_number"
......@@ -48,6 +46,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_version_name"
android:layout_marginTop="8dp"
android:textColor="@color/colorSecondaryText"/>
android:textColor="@color/colorSecondaryText" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/message_action_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/message_action_title"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_action_message_reply_24dp" />
<TextView
android:id="@+id/message_action_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/message_action_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Responder" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bottomsheet_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
\ No newline at end of file
......@@ -6,13 +6,13 @@
android:id="@+id/action_search"
android:icon="@drawable/ic_search_white_24px"
android:title="@string/action_search"
android:actionViewClass="androidx.appcompat.widget.SearchView"
android:showAsAction="ifRoom|collapseActionView" />
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
<item
android:id="@+id/action_sort"
android:icon="@drawable/ic_sort"
android:title="@string/menu_chatroom_sort"
android:showAsAction="always" />
app:showAsAction="ifRoom" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_message_reply"
......
......@@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0-alpha18'
classpath 'com.android.tools.build:gradle:3.2.0-beta02'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.2.0'
......
......@@ -34,7 +34,6 @@ ext {
kotshi : '1.0.2',
frescoImageViewer : '0.5.1',
markwon : '1.0.3',
sheetMenu : '5ff79ccf14',
aVLoadingIndicatorView: '2.1.3',
flexbox : '0.3.2',
......@@ -102,12 +101,8 @@ ext {
markwon : "ru.noties:markwon:${versions.markwon}",
//sheetMenu : "com.github.whalemare:sheetmenu:${versions.sheetMenu}",
sheetMenu : "com.github.luciofm:sheetmenu:${versions.sheetMenu}",
aVLoadingIndicatorView: "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
//For the wear app
wearable : "com.google.android.support:wearable:${versions.wear}",
playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}",
......
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