Unverified Commit ac2b6927 authored by Lucio Maciel's avatar Lucio Maciel Committed by GitHub

Merge pull request #681 from filipedelimabrito/feature/show-chat-list-using-sdk

[NEW] Show list of chats using the SDK (pure online)
parents 3242e0c5 d1696701
......@@ -78,6 +78,8 @@ dependencies {
implementation libraries.aVLoadingIndicatorView
implementation libraries.textDrawable
testImplementation libraries.junit
androidTestImplementation (libraries.expressoCore , {
exclude group: 'com.android.support', module: 'support-annotations'
......
......@@ -30,7 +30,7 @@
</activity>
<activity
android:name=".app.MainActivity"
android:name=".chatrooms.ui.MainActivity"
android:theme="@style/ChatListTheme" />
<activity
......
import android.content.Context
import chat.rocket.android.R
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.Period
import org.threeten.bp.*
import org.threeten.bp.format.DateTimeFormatter
import org.threeten.bp.format.FormatStyle
import org.threeten.bp.format.TextStyle
......@@ -14,20 +12,20 @@ object DateTimeHelper {
private val lastWeek = today.minusWeeks(1)
/**
* Returns a date from a LocalDateTime or the textual representation if the LocalDateTime has a max period of a week from the current date.
* Returns a date from a [LocalDateTime] or the textual representation if the [LocalDateTime] has a max period of a week from the current date.
*
* @param localDateTime The LocalDateTime.
* @param localDateTime The [LocalDateTime].
* @param context The context.
* @return The date or the textual representation from a LocalDateTime.
* @return The date or the textual representation from a [LocalDateTime].
*/
fun getDate(localDateTime: LocalDateTime, context: Context): String {
val localDate = localDateTime.toLocalDate()
return when (localDate) {
today -> localDateTime.toLocalTime().toString()
today -> formatLocalTime(localDateTime.toLocalTime())
yesterday -> context.getString(R.string.msg_yesterday)
else -> {
if (Period.between(lastWeek, localDate).days <= 0) {
formatDate(localDate)
formatLocalDate(localDate)
} else {
localDate.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.getDefault())
}
......@@ -36,18 +34,33 @@ object DateTimeHelper {
}
/**
* Returns a time from a LocalDateTime.
* Returns a time from a [LocalDateTime].
*
* @param localDateTime The LocalDateTime.
* @return The time from a LocalDateTime.
* @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime].
*/
fun getTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localDateTime.toLocalTime().format(formatter).toString()
}
private fun formatDate(localDate: LocalDate): String {
/**
* Returns a [LocalDateTime] from a [Long].
*
* @param long The [Long]
* @return The [LocalDateTime] from a [Long].
*/
fun getLocalDateTime(long: Long): LocalDateTime {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(long), ZoneId.systemDefault())
}
private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
return localDate.format(formatter).toString()
}
private fun formatLocalTime(localTime: LocalTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localTime.format(formatter).toString()
}
}
\ No newline at end of file
import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat
import android.widget.EditText
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.helper.TextHelper
import com.amulyakhare.textdrawable.TextDrawable
object DrawableHelper {
private val AVATAR_BACKGROUND_HEXADECIMAL_COLORS = intArrayOf(
0xFFF44336.toInt(), 0xFFE91E63.toInt(), 0xFF9C27B0.toInt(), 0xFF673AB7.toInt(), 0xFF3F51B5.toInt(),
0xFF2196F3.toInt(), 0xFF03A9F4.toInt(), 0xFF00BCD4.toInt(), 0xFF009688.toInt(), 0xFF4CAF50.toInt(),
0xFF8BC34A.toInt(), 0xFFCDDC39.toInt(), 0xFFFFC107.toInt(), 0xFFFF9800.toInt(), 0xFFFF5722.toInt(),
0xFF795548.toInt(), 0xFF9E9E9E.toInt(), 0xFF607D8B.toInt())
/**
* Returns a Drawable from its ID.
*
......@@ -37,7 +44,7 @@ object DrawableHelper {
* @see wrapDrawables
* @see tintDrawable
*/
fun wrapDrawable(drawable: Drawable) = DrawableCompat.wrap(drawable)
fun wrapDrawable(drawable: Drawable): Drawable = DrawableCompat.wrap(drawable)
/**
* Tints an array of Drawable.
......@@ -116,4 +123,29 @@ object DrawableHelper {
}
return userStatusDrawable
}
/**
* Returns a drawable with the first character from a string.
*
* @param string The string to get its first character and to get the avatar background color.
* @return A drawable with the string first character.
*/
fun getTextDrawable(string: String): Drawable {
return TextDrawable.builder()
.beginConfig()
.useFont(Typeface.SANS_SERIF)
.endConfig()
.buildRound(TextHelper.getFirstCharacter(string), getAvatarBackgroundColor(string))
}
/**
* Returns a background color to be rendered on the avatar.
*
* @param string Gets the background color based on the provided string.
* @return A hexadecimal color.
* @see (Rocket.Chat/server/startup/avatar.js)
*/
private fun getAvatarBackgroundColor(string: String): Int {
return AVATAR_BACKGROUND_HEXADECIMAL_COLORS[string.length % AVATAR_BACKGROUND_HEXADECIMAL_COLORS.size]
}
}
\ No newline at end of file
package chat.rocket.android.app
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.app.chatlist.ChatListFragment
import chat.rocket.android.util.addFragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addFragment("ChatListFragment", R.id.fragment_container) {
ChatListFragment()
}
}
}
\ No newline at end of file
package chat.rocket.android.app.chatlist
import chat.rocket.android.app.User
import org.threeten.bp.LocalDateTime
data class Chat(val user: User,
val name: String,
val type: String,
// Todo replace to Message type instead of String
val lastMessage: String,
val lastMessageDateTime: LocalDateTime,
val totalUnreadMessages: Int)
\ No newline at end of file
package chat.rocket.android.app.chatlist
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.app.User
import kotlinx.android.synthetic.main.fragment_chat_list.*
import org.threeten.bp.LocalDateTime
class ChatListFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_chat_list, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showChatList(createDumpData())
}
// This is just a sample showing 8 chat rooms (aka user subscription). We need to get it rid in a real word. REMARK: remove this comment and this method.
private fun createDumpData(): List<Chat> {
val filipe = User("1", "Filipe Brito", "filipe.brito", "online", "https://open.rocket.chat/avatar/filipe.brito")
val lucio = User("2", "Lucio Maciel", "Lucio Maciel", "busy", "https://open.rocket.chat/avatar/lucio.maciel")
val leonardo = User("3", "Leonardo Aramaki", "leonardo.aramaki", "busy", "https://open.rocket.chat/avatar/leonardo.aramaki")
val sing = User("4", "Filipe Brito", "filipe.brito", "online", "https://open.rocket.chat/avatar/filipe.brito")
val hetal = User("5", "Filipe Brito", "filipe.brito", "online", "https://open.rocket.chat/avatar/filipe.brito")
val matheus = User("6", "Filipe Brito", "filipe.brito", "online", "https://open.rocket.chat/avatar/filipe.brito")
val bestrun = User("7", "Filipe Brito", "filipe.brito", "online", "https://open.rocket.chat/avatar/filipe.brito")
val djc = User("8", "3djc", "3djc", "online", "https://open.rocket.chat/avatar/filipe.brito")
val dumpChat1 = Chat(leonardo,
"Leonardo Aramaki",
"d",
"Type something",
LocalDateTime.of(2017, 11, 21,1, 3),
1)
val dumpChat2 = Chat(filipe,
"Filipe Brito",
"d",
"Type something...Type something...Type something",
LocalDateTime.of(2017, 11, 22,1, 3),
150)
val dumpChat3 = Chat(lucio,
"Lucio Maciel",
"d",
"Type something",
LocalDateTime.of(2017, 11, 17,1, 3),
0)
val dumpChat4 = Chat(sing,
"mobile-internal",
"p",
"@aaron.ogle @rafael.kellermann same problem over here. Although all the servers show up on the selection.",
LocalDateTime.of(2017, 11, 15,1, 3),
0)
val dumpChat5 = Chat(hetal,
"general",
"c",
"Has joined the channel.",
LocalDateTime.of(2017, 11, 13,1, 3),
0)
val dumpChat6 = Chat(matheus,
"androidnativeapp",
"c",
"Yes @sttyru, but you'll need to implement from the ground up following the docs at docs.rocket.chat where you can see the REST (HTTP) and Real-Time (WebSockets) calls.",
LocalDateTime.of(2017, 11, 14,1, 3),
0)
val dumpChat7 = Chat(bestrun,
"androidnativeapp-2",
"c",
"Just downloaded .zip and imported into Android Studio then build the project.",
LocalDateTime.of(2017, 11, 4,12, 47),
0)
val dumpChat8 = Chat(djc,
"iosnativeapp",
"c",
"Ok, got confused by the blog announcement that shows a screenshot with github oAuth ! Sorry !",
LocalDateTime.of(2017, 11, 4,12, 43),
0)
// creates a list of chat sorted by lastMessageDateTime attribute.
return listOf(dumpChat1, dumpChat2, dumpChat3, dumpChat4, dumpChat5, dumpChat6, dumpChat7, dumpChat8).sortedByDescending { chat -> chat.lastMessageDateTime }
}
// REMARK: The presenter should call this method. The presenter also need to sort the chat list by latest message (compared by its date).
private fun showChatList(dataSet: List<Chat>) {
activity?.apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
recycler_view.adapter = ChatListAdapter(dataSet.toMutableList(), this)
}
}
}
......@@ -8,7 +8,7 @@ import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.app.User
import kotlinx.android.synthetic.main.fragment_chat_list.*
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import org.threeten.bp.LocalDateTime
class MessageFragment : Fragment() {
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.presentation
import android.content.Context
import android.content.Intent
import chat.rocket.android.R
import chat.rocket.android.app.MainActivity
import chat.rocket.android.chatrooms.ui.MainActivity
import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
......@@ -50,10 +50,10 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int
}
fun toChatList() {
val chatRoom = Intent(activity, MainActivity::class.java).apply {
val chatList = Intent(activity, MainActivity::class.java).apply {
//TODO any parameter to pass
}
activity.startActivity(chatRoom)
activity.startActivity(chatList)
activity.finish()
}
}
......@@ -12,6 +12,7 @@ import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class SignupFragmentModule {
@Provides
fun signupView(frag: SignupFragment): SignupView {
return frag
......@@ -26,4 +27,4 @@ class SignupFragmentModule {
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
}
\ No newline at end of file
......@@ -4,8 +4,9 @@ import chat.rocket.android.authentication.signup.ui.SignupFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class SignupFragmentProvider {
@Module
abstract class SignupFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(SignupFragmentModule::class))
@ContributesAndroidInjector(modules = [SignupFragmentModule::class])
abstract fun provideSignupFragment(): SignupFragment
}
}
\ No newline at end of file
......@@ -153,4 +153,4 @@ class SignupFragment : Fragment(), SignupView {
text_password.isEnabled = value
text_email.isEnabled = value
}
}
\ No newline at end of file
}
......@@ -12,6 +12,7 @@ import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class TwoFAFragmentModule {
@Provides
fun loginView(frag: TwoFAFragment): TwoFAView {
return frag
......
......@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(TwoFAFragmentModule::class))
@ContributesAndroidInjector(modules = [TwoFAFragmentModule::class])
abstract fun provideTwoFAFragment(): TwoFAFragment
}
package chat.rocket.android.chatrooms.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class ChatRoomsFragmentModule {
@Provides
fun chatRoomsView(frag: ChatRoomsFragment): ChatRoomsView {
return frag
}
@Provides
fun provideLifecycleOwner(frag: ChatRoomsFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
@Provides
fun provideJob(): Job {
return Job()
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.di
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ChatRoomsFragmentProvider {
@ContributesAndroidInjector(modules = [ChatRoomsFragmentModule::class])
abstract fun provideChatRoomsFragment(): ChatRoomsFragment
}
\ No newline at end of file
package chat.rocket.android.chatrooms.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.launchUI
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.chatRooms
import chat.rocket.core.model.ChatRoom
import javax.inject.Inject
class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, private val strategy: CancelStrategy) {
@Inject lateinit var client: RocketChatClient
fun chatRooms() {
launchUI(strategy) {
view.showLoading()
val chatRooms = client.chatRooms().update
val openChatRooms = getOpenChatRooms(chatRooms)
val sortedOpenChatRooms = sortChatRooms(openChatRooms)
view.showChatRooms(sortedOpenChatRooms.toMutableList())
view.hideLoading()
}
}
private fun getOpenChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.filter(ChatRoom::open)
}
private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.sortedByDescending {
chatRoom -> chatRoom.lastMessage?.timestamp
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.model.ChatRoom
interface ChatRoomsView : LoadingView, MessageView {
/**
* Shows the chat rooms.
*
* @param dataSet The data set to show.
*/
fun showChatRooms(dataSet: MutableList<ChatRoom>)
}
\ No newline at end of file
package chat.rocket.android.app.chatlist
package chat.rocket.android.chatrooms.ui
import DateTimeHelper
import DrawableHelper
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.common.model.BaseRoom.RoomType
import chat.rocket.core.model.ChatRoom
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.item_chat.view.*
class ChatListAdapter(private var dataSet: MutableList<Chat>, private val context: Context) : RecyclerView.Adapter<ChatListAdapter.ViewHolder>() {
class ChatRoomsAdapter(private var dataSet: MutableList<ChatRoom>, private val context: Context) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_chat, parent, false)
return ViewHolder(view)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_chat))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val chat = dataSet[position]
val chatRoom = dataSet[position]
val chatRoomName = chatRoom.name
holder.userAvatar.setImageURI(chat.user.avatarUri)
holder.chatName.text = chat.name
holder.lastMessage.text = chat.lastMessage
holder.lastMessageDateTime.text = DateTimeHelper.getDate(chat.lastMessageDateTime, context)
holder.chatName.textContent = chatRoomName
when (chat.type) {
"p" -> DrawableHelper.compoundDrawable(holder.chatName, DrawableHelper.getDrawableFromId(R.drawable.ic_lock_outline_black, context))
"c" -> DrawableHelper.compoundDrawable(holder.chatName, DrawableHelper.getDrawableFromId(R.drawable.ic_hashtag_black, context))
"d" -> DrawableHelper.compoundDrawable(holder.chatName, DrawableHelper.getUserStatusDrawable(chat.user.status, context))
if (chatRoom.type == RoomType.ONE_TO_ONE) {
// TODO Check the best way to get the current server url.
val canonicalUrl = chatRoom.client.restUrl.toString()
holder.userAvatar.setImageURI(UrlHelper.getAvatarUrl(canonicalUrl, chatRoomName))
holder.userAvatar.setVisibility(true)
} else {
holder.roomAvatar.setImageDrawable(DrawableHelper.getTextDrawable(chatRoomName))
holder.roomAvatar.setVisibility(true)
}
val totalUnreadMessage = chat.totalUnreadMessages
val totalUnreadMessage = chatRoom.unread
when {
totalUnreadMessage in 1..99 -> {
holder.unreadMessage.text = totalUnreadMessage.toString()
holder.unreadMessage.visibility = View.VISIBLE
holder.unreadMessage.textContent = totalUnreadMessage.toString()
holder.unreadMessage.setVisibility(true)
}
totalUnreadMessage > 99 -> {
holder.unreadMessage.text = context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
holder.unreadMessage.visibility = View.VISIBLE
holder.unreadMessage.textContent = context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
holder.unreadMessage.setVisibility(true)
}
}
val lastMessage = chatRoom.lastMessage
val lastMessageSender = lastMessage?.sender
if (lastMessage != null && lastMessageSender != null) {
val message = lastMessage.message
val senderUsername = lastMessageSender.username
when (senderUsername) {
chatRoomName -> {
holder.lastMessage.textContent = message
}
// TODO Change to MySelf
// chatRoom.user?.username -> {
// holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message"
// }
else -> {
holder.lastMessage.textContent = "@$senderUsername: $message"
}
}
val localDateTime = DateTimeHelper.getLocalDateTime(lastMessage.timestamp)
holder.lastMessageDateTime.textContent = DateTimeHelper.getDate(localDateTime, context)
}
}
override fun getItemCount(): Int = dataSet.size
override fun getItemViewType(position: Int): Int = position
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val userAvatar: SimpleDraweeView = itemView.image_user_avatar
val roomAvatar: ImageView = itemView.image_room_avatar
val chatName: TextView = itemView.text_chat_name
val lastMessage: TextView = itemView.text_last_message
val lastMessageDateTime: TextView = itemView.text_last_message_date_time
......
package chat.rocket.android.chatrooms.ui
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import javax.inject.Inject
class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var presenter: ChatRoomsPresenter
companion object {
fun newInstance() = ChatRoomsFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_chat_rooms, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.chatRooms()
}
override fun showChatRooms(dataSet: MutableList<ChatRoom>) {
activity?.apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this, 144, 32))
recycler_view.adapter = ChatRoomsAdapter(dataSet, this)
}
}
override fun showLoading() = view_loading.show()
override fun hideLoading() = view_loading.hide()
override fun showMessage(message: String) = Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
}
\ No newline at end of file
package chat.rocket.android.chatrooms.ui
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.util.addFragment
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import javax.inject.Inject
class MainActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AndroidInjection.inject(this)
addFragment("ChatRoomsFragment", R.id.fragment_container) {
ChatRoomsFragment.newInstance()
}
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector
}
}
\ No newline at end of file
package chat.rocket.android.dagger.module
import chat.rocket.android.app.MainActivity
import chat.rocket.android.authentication.di.*
import chat.rocket.android.authentication.di.AuthenticationModule
import chat.rocket.android.authentication.login.di.LoginFragmentProvider
import chat.rocket.android.authentication.server.di.ServerFragmentProvider
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.chatrooms.di.ChatRoomsFragmentProvider
import chat.rocket.android.chatrooms.ui.MainActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class ActivityBuilder {
@Module
abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(modules = [AuthenticationModule::class,
LoginFragmentProvider::class,
ServerFragmentProvider::class,
LoginFragmentProvider::class,
SignupFragmentProvider::class,
TwoFAFragmentProvider::class
])
abstract fun bindAuthenticationActivity(): AuthenticationActivity
@ContributesAndroidInjector abstract fun bindMainActivity(): MainActivity
}
@ContributesAndroidInjector(modules = [ChatRoomsFragmentProvider::class])
abstract fun bindMainActivity(): MainActivity
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.widget.TextView
import chat.rocket.android.util.ifEmpty
object TextHelper {
......@@ -27,4 +28,15 @@ object TextHelper {
textView.movementMethod = LinkMovementMethod.getInstance()
textView.setText(spannableString, TextView.BufferType.SPANNABLE)
}
/**
* Returns the first character from a string.
*
* @param string The string to get its first character.
* @return The first character from a string.
*/
fun getFirstCharacter(string: String): String {
string.ifEmpty("?")
return string.substring(0, 1).toUpperCase()
}
}
\ No newline at end of file
package chat.rocket.android.helper
object UrlHelper {
/**
* Returns the avatar URL.
*
* @param serverUrl The serverUrl.
* @param chatRoomName The chat room name.
* @return The avatar URL.
*/
fun getAvatarUrl(serverUrl: String, chatRoomName: String): String = serverUrl + "avatar/" + chatRoomName
}
\ No newline at end of file
......@@ -6,7 +6,9 @@ import chat.rocket.android.R
fun AppCompatActivity.addFragment(tag: String, layoutId: Int, newInstance: () -> Fragment) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance()
supportFragmentManager.beginTransaction().replace(layoutId, fragment, tag).commit()
supportFragmentManager.beginTransaction()
.replace(layoutId, fragment, tag)
.commit()
}
fun AppCompatActivity.addFragmentBackStack(tag: String, layoutId: Int, newInstance: () -> Fragment) {
......
......@@ -14,10 +14,10 @@ fun String.ifEmpty(value: String): String {
}
fun View.setVisibility(value: Boolean) {
if (value) {
this.visibility = View.VISIBLE
visibility = if (value) {
View.VISIBLE
} else {
this.visibility = View.GONE
View.GONE
}
}
......@@ -34,5 +34,5 @@ var TextView.textContent: String
var TextView.hintContent: String
get() = hint.toString()
set(value) {
text = value
hint = value
}
package chat.rocket.android.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.support.annotation.DrawableRes
import android.support.v4.content.ContextCompat
import android.support.v7.widget.RecyclerView
/**
* Adds a default or custom divider to specific item views from the adapter's data set.
* @see RecyclerView.ItemDecoration
*/
class DividerItemDecoration() : RecyclerView.ItemDecoration() {
private lateinit var divider: Drawable
private var boundStart = 0
private var boundRight = 0
// Default divider will be used.
constructor(context: Context) : this() {
val attrs = intArrayOf(android.R.attr.listDivider)
val styledAttributes = context.obtainStyledAttributes(attrs)
divider = styledAttributes.getDrawable(0)
styledAttributes.recycle()
}
// Default divider with custom boundaries (start and right) will be used.
constructor(context: Context, boundStart: Int, boundRight: Int) : this() {
val attrs = intArrayOf(android.R.attr.listDivider)
val styledAttributes = context.obtainStyledAttributes(attrs)
divider = styledAttributes.getDrawable(0)
styledAttributes.recycle()
this.boundStart = boundStart
this.boundRight = boundRight
}
// Custom divider will be used.
constructor(context: Context, @DrawableRes drawableResId: Int) : this() {
val customDrawable = ContextCompat.getDrawable(context, drawableResId)
if (customDrawable != null) {
divider = customDrawable
}
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val left = parent.paddingLeft + boundStart
val right = (parent.width - parent.paddingRight) - boundRight
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + divider.intrinsicHeight
divider.setBounds(left, top, right, bottom)
divider.draw(c)
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
android:layout_height="match_parent"
tools:context=".chatrooms.ui.ChatRoomsFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
......@@ -24,4 +26,14 @@
app:floatingSearch_searchBarMarginRight="4dp"
app:floatingSearch_searchBarMarginTop="28dp" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorColor="@color/black"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
</RelativeLayout>
\ No newline at end of file
......@@ -4,20 +4,34 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginBottom="12dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp">
android:layout_marginTop="12dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_user_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
<android.support.constraint.ConstraintLayout
android:id="@+id/avatar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/middle_container"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
app:layout_constraintTop_toTopOf="parent">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_user_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:visibility="gone"
app:roundAsCircle="true" />
<ImageView
android:id="@+id/image_room_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:visibility="gone"
tools:ignore="contentDescription" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/middle_container"
......@@ -25,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toRightOf="@+id/image_user_avatar"
app:layout_constraintLeft_toRightOf="@+id/avatar_container"
app:layout_constraintRight_toLeftOf="@+id/right_container">
<TextView
......@@ -36,17 +50,16 @@
android:drawablePadding="5dp"
android:ellipsize="end"
android:maxLines="1"
tools:text="Ronald Perkins" />
tools:text="General" />
<TextView
android:id="@+id/text_last_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:maxLines="2"
app:layout_constraintTop_toBottomOf="@+id/text_chat_name"
tools:text="Type something" />
tools:text="You: Type something" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
......
......@@ -34,6 +34,6 @@
<string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_content_description_chat_icon">Ícone do chat</string>
<string name="msg_you">Você</string>
</resources>
......@@ -14,4 +14,6 @@
<color name="colorDividerMessageComposer">#D8D8D8</color>
<color name="white">#FFFFFFFF</color>
<color name="black">#FF000000</color>
</resources>
\ No newline at end of file
......@@ -36,6 +36,6 @@
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_chat_icon">Chat icon</string>
<string name="msg_you">You</string>
</resources>
\ No newline at end of file
......@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "com.android.tools.build:gradle:3.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
......@@ -23,6 +23,7 @@ allprojects {
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://jitpack.io" }
maven { url "http://dl.bintray.com/amulyakhare/maven" } // For TextDrawable.
}
apply from: rootProject.file('dependencies.gradle')
......
......@@ -23,11 +23,12 @@ ext {
okhttp : '3.9.0',
timber : '4.5.1',
threeTenABP : '1.0.5',
fresco : '1.5.0',
fresco : '1.7.1',
frescoImageViewer : '0.5.0',
floatingSearchView : '2.1.1',
androidSvg : '1.2.1',
aVLoadingIndicatorView : '2.1.3',
textDrawable : '1.0.2',
// For testing
junit : '4.12',
......@@ -82,6 +83,8 @@ ext {
aVLoadingIndicatorView : "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
textDrawable : "com.github.rocketchat:textdrawable:${versions.textDrawable}",
// For testing
toolsJar : files(Jvm.current().getToolsJar()),
junit : "junit:junit:$versions.junit",
......
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