Commit d0cc012d authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Add create channel feature.

parent 1a137f9a
......@@ -73,12 +73,9 @@
android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".createChannel.ui.CreateNewChannelActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity android:name=".createChannel.addMembers.ui.AddMembersActivity"
<activity
android:name=".createchannel.addmembers.ui.AddMembersActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="stateAlwaysHidden" />
......
......@@ -613,8 +613,14 @@ class ChatRoomPresenter @Inject constructor(
val name = it.name ?: ""
val searchList = mutableListOf(username, name)
it.emails?.forEach { email -> searchList.add(email.address) }
PeopleSuggestionViewModel(currentServer.avatarUrl(username),
username, username, name, it.status, false, searchList)
PeopleSuggestionViewModel(
currentServer.avatarUrl(username),
username,
username,
name,
it.status,
false,
searchList)
}.filterNot { filterSelfOut && self != null && self == it.text })
}
ROOMS -> {
......
package chat.rocket.android.chatrooms.ui
import android.content.Intent
import android.app.AlertDialog
import android.os.Bundle
import android.os.Handler
......@@ -22,7 +21,6 @@ import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.createChannel.ui.CreateNewChannelActivity
import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper
......@@ -105,7 +103,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
setupToolbar()
setupRecyclerView()
setupFab()
presenter.loadChatRooms()
}
......@@ -193,7 +190,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
/*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await()*/
text_no_search.isVisible = newDataSet.isEmpty()
text_no_result_found.isVisible = newDataSet.isEmpty()
if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
......@@ -314,13 +311,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
sectionedAdapter?.setSections(sections.toArray(dummy))
}
private fun setupFab() {
create_new_channel_fab.setOnClickListener {
val intent = Intent(activity, CreateNewChannelActivity::class.java)
startActivity(intent)
}
}
private fun queryChatRoomsByName(name: String?): Boolean {
presenter.chatRoomsByName(name ?: "")
return true
......
package chat.rocket.android.createChannel.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.createChannel.presentation.CreateNewChannelView
import chat.rocket.android.createChannel.ui.CreateNewChannelActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class CreateNewChannelModule {
@Provides
fun provideLifecycleOwner(activity: CreateNewChannelActivity): LifecycleOwner {
return activity
}
@Provides
@PerActivity
fun createChannelView(activity: CreateNewChannelActivity): CreateNewChannelView {
return activity
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
\ No newline at end of file
package chat.rocket.android.createChannel.di
import chat.rocket.android.createChannel.ui.CreateNewChannelActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class CreateNewChannelProvider {
@ContributesAndroidInjector(modules = [CreateNewChannelModule::class])
abstract fun provideNewChannelActivity(): CreateNewChannelActivity
}
\ No newline at end of file
package chat.rocket.android.createChannel.presentation
import chat.rocket.android.core.behaviours.LoadingView
interface CreateNewChannelView : LoadingView {
/**
* Show a message that a channel was successfully created
*/
fun showChannelCreatedSuccessfullyMessage()
/**
* Show message and clear text in edit text
*
* @param resId Resource id of the message to be shown
*/
fun showMessageAndClearText(resId: Int)
/**
* Show message and clear text in edit text
*
* @param message Toast message to be shown
*/
fun showMessageAndClearText(message: String)
/**
* Show error message
*/
fun showErrorMessage()
}
\ No newline at end of file
package chat.rocket.android.createChannel.ui
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.support.design.chip.Chip
import android.support.v7.app.AppCompatActivity
import android.view.MenuItem
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.createChannel.addMembers.ui.AddMembersActivity
import chat.rocket.android.createChannel.presentation.CreateNewChannelPresenter
import chat.rocket.android.createChannel.presentation.CreateNewChannelView
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType
import dagger.android.AndroidInjection
import kotlinx.android.synthetic.main.activity_create_new_channel.*
import kotlinx.android.synthetic.main.layout_toolbar.*
import javax.inject.Inject
internal const val ADD_MEMBERS_ACTIVITY_REQUEST_CODE = 1
class CreateNewChannelActivity : AppCompatActivity(), CreateNewChannelView {
@Inject
lateinit var presenter: CreateNewChannelPresenter
private var channelType: RoomType = RoomType.CHANNEL
private var listOfUsers: ArrayList<String> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_new_channel)
setUpToolBar()
setUpOnClickListeners()
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
android.R.id.home -> {
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
if (requestCode == ADD_MEMBERS_ACTIVITY_REQUEST_CODE && data != null) {
listOfUsers = data.getStringArrayListExtra("members")
selected_members_chips.removeAllViews()
refreshMembersChips()
}
}
}
override fun showLoading() {
view_loading.isVisible = true
layout_container.alpha = 0.5f
layout_container.isEnabled = false
}
override fun hideLoading() {
view_loading.isVisible = false
layout_container.alpha = 1.0f
layout_container.isEnabled = true
}
override fun showChannelCreatedSuccessfullyMessage() {
showToast(getString(R.string.msg_channel_created_successfully))
finish()
}
override fun showMessageAndClearText(resId: Int) {
channel_name_edit_text.setText("")
showToast(getString(resId))
}
override fun showMessageAndClearText(message: String) {
channel_name_edit_text.setText("")
showToast(message)
}
override fun showErrorMessage() {
showMessageAndClearText(getString(R.string.msg_generic_error))
}
private fun refreshMembersChips() {
for (element in listOfUsers) {
val memberChip = Chip(this)
memberChip.chipText = element
memberChip.isCloseIconEnabled = false
memberChip.isLongClickable = false
memberChip.setChipBackgroundColorResource(R.color.icon_grey)
selected_members_chips.addView(memberChip)
}
}
private fun setUpToolBar() {
setSupportActionBar(toolbar)
toolbar_title.text = getString(R.string.title_create_new_channel)
toolbar_action_text.text = getString(R.string.action_create_new_channel)
toolbar_action_text.alpha = 1.0f
toolbar_action_text.isEnabled = true
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
private fun setUpOnClickListeners() {
public_channel.setOnClickListener {
channelType = RoomType.CHANNEL
channel_type.text = getString(R.string.public_channel_type)
channel_description.text = getString(R.string.public_channel_description)
placeholder.setImageDrawable(getDrawable(R.drawable.ic_hashtag_black_12dp))
(getDrawable(R.drawable.button_border) as GradientDrawable).setColor(
resources.getColor(
R.color.default_background
)
)
(getDrawable(R.drawable.button_solid) as GradientDrawable).setColor(resources.getColor(R.color.colorRed))
private_channel.background = getDrawable(R.drawable.button_border)
public_channel.background = getDrawable(R.drawable.button_solid)
private_channel.setTextColor(resources.getColor(R.color.colorRed))
public_channel.setTextColor(resources.getColor(R.color.default_background))
}
private_channel.setOnClickListener {
channelType = RoomType.PRIVATE_GROUP
channel_type.text = getString(R.string.private_channel_type)
channel_description.text = getString(R.string.private_channel_type_description)
placeholder.setImageDrawable(getDrawable(R.drawable.ic_lock_black_12_dp))
(getDrawable(R.drawable.button_border) as GradientDrawable).setColor(
resources.getColor(R.color.colorRed)
)
(getDrawable(R.drawable.button_solid) as GradientDrawable).setColor(
resources.getColor(R.color.default_background)
)
(getDrawable(R.drawable.button_solid) as GradientDrawable).setStroke(
1,
resources.getColor(R.color.colorRed)
)
private_channel.background = getDrawable(R.drawable.button_border)
public_channel.background = getDrawable(R.drawable.button_solid)
private_channel.setTextColor(resources.getColor(R.color.default_background))
public_channel.setTextColor(resources.getColor(R.color.colorRed))
}
toolbar_action_text.setOnClickListener {
if (channel_name_edit_text.textContent.isNotEmpty()) {
presenter.createNewChannel(
channelType,
channel_name_edit_text.text.toString(),
listOfUsers,
false
)
} else{
showToast(getString(R.string.msg_channel_name_required))
}
}
add_members_view.setOnClickListener {
val intent = Intent(this, AddMembersActivity::class.java)
intent.putExtra("chips", listOfUsers)
startActivityForResult(intent, ADD_MEMBERS_ACTIVITY_REQUEST_CODE)
}
}
}
\ No newline at end of file
package chat.rocket.android.createChannel.addMembers.di
package chat.rocket.android.createchannel.addmembers.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.createChannel.addMembers.presentation.AddMembersView
import chat.rocket.android.createChannel.addMembers.ui.AddMembersActivity
import chat.rocket.android.createchannel.addmembers.presentation.AddMembersView
import chat.rocket.android.createchannel.addmembers.ui.AddMembersActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
......@@ -11,6 +11,7 @@ import kotlinx.coroutines.experimental.Job
@Module
class AddMembersModule {
@Provides
fun provideLifecycleOwner(activity: AddMembersActivity): LifecycleOwner {
return activity
......
package chat.rocket.android.createChannel.addMembers.di
package chat.rocket.android.createchannel.addmembers.di
import chat.rocket.android.createChannel.addMembers.ui.AddMembersActivity
import chat.rocket.android.createchannel.addmembers.ui.AddMembersActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
......
package chat.rocket.android.createChannel.addMembers.presentation
package chat.rocket.android.createchannel.addmembers.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.members.viewmodel.MemberViewModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.queryUsers
import chat.rocket.core.internal.rest.searchUser
import javax.inject.Inject
class AddMembersPresenter @Inject constructor(
private val view: AddMembersView,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
private val mapper: MemberViewModelMapper,
factory: RocketChatClientFactory
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
) {
private val client = factory.create(serverInteractor.get()!!)
private var offset: Long = 0
fun queryUsersFromRegex(queryParam: String, offset: Long = 0) {
view.showLoading()
fun searchUser(query: String) {
launchUI(strategy) {
try {
val allMembers = retryIO("queryUsers($queryParam)") {
client.queryUsers(queryParam, 60, offset)
}
val memberViewModelMapper = mapper.mapToViewModelList(allMembers.result)
view.showMembers(memberViewModelMapper, allMembers.total)
view.showLoading()
val users = client.searchUser(query, offset)
val memberViewModelMapper = mapper.mapToViewModelList(users.result)
view.showUsers(memberViewModelMapper, users.total)
offset += 1 * 30L
} catch (ex: RocketChatException) {
ex.message?.let {
view.showMessage(it)
......
package chat.rocket.android.createChannel.addMembers.presentation
package chat.rocket.android.createchannel.addmembers.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.android.members.viewmodel.MemberViewModel
interface AddMembersView : LoadingView, MessageView {
/**
* Show members on the basis of query
* Show the users of the server on the basis of query.
*
* @param dataSet The list of members
* @param total The number of members returned
* @param dataSet The list of users to show.
* @param total The number of users returned.
*/
fun showMembers(dataSet: List<MemberViewModel>, total: Long)
fun showUsers(dataSet: List<MemberViewModel>, total: Long)
}
\ No newline at end of file
package chat.rocket.android.createChannel.addMembers.ui
package chat.rocket.android.createchannel.addmembers.ui
import android.app.Activity
import android.content.Intent
......@@ -8,67 +8,61 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.MenuItem
import android.widget.EditText
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.createChannel.addMembers.presentation.AddMembersPresenter
import chat.rocket.android.createChannel.addMembers.presentation.AddMembersView
import chat.rocket.android.createchannel.addmembers.presentation.AddMembersPresenter
import chat.rocket.android.createchannel.addmembers.presentation.AddMembersView
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.viewmodel.MemberViewModel
import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.widget.DividerItemDecoration
import com.jakewharton.rxbinding2.widget.RxTextView
import dagger.android.AndroidInjection
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.BehaviorSubject
import kotlinx.android.synthetic.main.activity_add_members.*
import kotlinx.android.synthetic.main.layout_toolbar.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class AddMembersActivity : AppCompatActivity(), AddMembersView {
@Inject
lateinit var presenter: AddMembersPresenter
private lateinit var queryParam: String
private var membersToAdd: ArrayList<String> = ArrayList()
private val adapter: MembersAdapter = MembersAdapter { memberViewModel ->
if (!membersToAdd.contains(memberViewModel.username)) {
addNewChip(memberViewModel)
updateToolBar()
search_view.setText("")
} else {
showMessage(getString(R.string.msg_member_already_added))
private var query: String = ""
private var membersToAdd = arrayListOf<String>()
private val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
private lateinit var editTextDisposable: Disposable
private val adapter: MembersAdapter = MembersAdapter {
it.username?.let {
if (!membersToAdd.contains(it)) {
buildChip(it)
membersToAdd.add(it)
updateToolBar()
} else {
showMessage(getString(R.string.msg_member_already_added))
}
}
}
private lateinit var observableForSearchView: Disposable
private lateinit var observableForToolbarAction: Disposable
private val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_members)
setUpToolBar()
setUpRecyclerView()
setOnClickListeners()
setInitialChips()
setupToolBar()
setupRecyclerView()
setupInitialChips()
setListeners()
}
override fun onStart() {
super.onStart()
setUpObservableForSearchView()
subscribeEditText()
}
override fun onStop() {
super.onStop()
//dispose off the rx disposables
observableForToolbarAction.dispose()
observableForSearchView.dispose()
unsubscribeEditText()
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
......@@ -82,27 +76,6 @@ class AddMembersActivity : AppCompatActivity(), AddMembersView {
return super.onOptionsItemSelected(item)
}
override fun showMembers(dataSet: List<MemberViewModel>, total: Long) {
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
if (dataSet.size >= 59) {
search_results.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
page: Int,
totalItemsCount: Int,
recyclerView: RecyclerView?
) {
presenter.queryUsersFromRegex(queryParam, page * 60L)
}
})
}
} else {
adapter.appendData(dataSet)
}
}
override fun showLoading() {
view_loading.isVisible = true
}
......@@ -123,74 +96,50 @@ class AddMembersActivity : AppCompatActivity(), AddMembersView {
showMessage(getString(R.string.msg_generic_error))
}
private fun setInitialChips() {
membersToAdd = intent.getStringArrayListExtra("chips")
for (element in membersToAdd) {
buildNewChip(element)
}
updateToolBar()
}
private fun setUpObservableForSearchView() {
observableForSearchView = observableFromSearchView(search_view)
.debounce(300, TimeUnit.MILLISECONDS)
.filter { item -> item.isNotEmpty() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { query ->
queryParam = query
run {
adapter.reAllocateArrayList()
presenter.queryUsersFromRegex(query)
}
override fun showUsers(dataSet: List<MemberViewModel>, total: Long) {
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
page: Int,
totalItemsCount: Int,
recyclerView: RecyclerView?
) {
presenter.searchUser(query)
}
})
}
}
private fun addNewChip(memberViewModel: MemberViewModel) {
buildNewChip(memberViewModel.displayName)
membersToAdd.add(memberViewModel.displayName)
}
private fun buildNewChip(chipText: String) {
val memberChip = Chip(this)
memberChip.chipText = chipText
memberChip.isCloseIconEnabled = true
memberChip.setChipBackgroundColorResource(R.color.icon_grey)
memberChip.setOnCloseIconClickListener { view ->
members_chips.removeView(view)
membersToAdd.remove((view as Chip).chipText.toString())
updateToolBar()
}
members_chips.addView(memberChip)
}
private fun updateToolBar() {
toolbar_action_text.isEnabled = membersToAdd.isNotEmpty()
if (membersToAdd.size == 0) {
toolbar_action_text.alpha = 0.8f
} else {
toolbar_action_text.alpha = 1.0f
adapter.appendData(dataSet)
}
toolbar_title.textContent =
getString(R.string.title_add_members, membersToAdd.size.toString())
}
private fun setUpToolBar() {
private fun setupToolBar() {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar_title.text = getString(R.string.title_add_members, "0")
toolbar_action_text.text = getString(R.string.action_select_members)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
private fun setupRecyclerView() {
recycler_view.layoutManager = linearLayoutManager
recycler_view.adapter = adapter
recycler_view.addItemDecoration(DividerItemDecoration(this))
}
private fun setUpRecyclerView() {
search_results.layoutManager = linearLayoutManager
search_results.adapter = adapter
search_results.addItemDecoration(DividerItemDecoration(this))
private fun setupInitialChips() {
membersToAdd = intent.getStringArrayListExtra("chips")
for (element in membersToAdd) {
buildChip(element)
}
updateToolBar()
}
private fun setOnClickListeners() {
toolbar_action_text.setOnClickListener { view ->
if (view.isEnabled) {
private fun setListeners() {
toolbar_action_text.setOnClickListener {
if (it.isEnabled) {
val intent = Intent()
intent.putExtra("members", membersToAdd)
setResult(Activity.RESULT_OK, intent)
......@@ -199,13 +148,45 @@ class AddMembersActivity : AppCompatActivity(), AddMembersView {
}
}
private fun observableFromSearchView(searchView: EditText): Observable<String> {
val observableSubject: BehaviorSubject<String> = BehaviorSubject.create()
observableForToolbarAction = RxTextView.textChanges(searchView).subscribe { text ->
if (text.isNotBlank()) {
observableSubject.onNext(text.toString())
private fun subscribeEditText() {
editTextDisposable = text_search_member.asObservable()
.debounce(500, TimeUnit.MILLISECONDS)
.filter { t -> t.isNotEmpty() && t != query }
.subscribe {
adapter.clearData()
query = it.toString()
presenter.searchUser(query)
}
}
private fun unsubscribeEditText() {
editTextDisposable.dispose()
}
private fun buildChip(chipText: String) {
val chip = Chip(this)
chip.chipText = chipText
chip.isCloseIconEnabled = true
chip.setChipBackgroundColorResource(R.color.icon_grey)
chip.setOnCloseIconClickListener {
members_chips.removeView(it)
membersToAdd.remove((it as Chip).chipText.toString())
updateToolBar()
}
members_chips.addView(chip)
}
private fun updateToolBar() {
if (membersToAdd.isEmpty()) {
toolbar_action_text.alpha = 0.8F
toolbar_action_text.isEnabled = false
members_chips.isVisible = false
} else {
toolbar_action_text.alpha = 1.0F
toolbar_action_text.isEnabled = true
members_chips.isVisible = true
}
return observableSubject
toolbar_title.textContent =
getString(R.string.title_add_members, membersToAdd.size.toString())
}
}
\ No newline at end of file
package chat.rocket.android.createchannel.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.createchannel.presentation.CreateChannelView
import chat.rocket.android.createchannel.ui.CreateChannelFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
@Module
@PerFragment
class CreateChannelModule {
@Provides
fun createChannelView(fragment: CreateChannelFragment): CreateChannelView {
return fragment
}
@Provides
fun provideLifecycleOwner(fragment: CreateChannelFragment): LifecycleOwner {
return fragment
}
}
\ No newline at end of file
package chat.rocket.android.createchannel.di
import chat.rocket.android.createchannel.ui.CreateChannelFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class CreateChannelProvider {
@ContributesAndroidInjector(modules = [CreateChannelModule::class])
abstract fun provideCreateChannelFragment(): CreateChannelFragment
}
\ No newline at end of file
package chat.rocket.android.createChannel.presentation
package chat.rocket.android.createchannel.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.members.viewmodel.MemberViewModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
......@@ -9,17 +11,20 @@ import chat.rocket.common.model.RoomType
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.createChannel
import chat.rocket.core.internal.rest.searchUser
import javax.inject.Inject
class CreateNewChannelPresenter @Inject constructor(
private val view: CreateNewChannelView,
class CreateChannelPresenter @Inject constructor(
private val view: CreateChannelView,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory
private val mapper: MemberViewModelMapper,
private val navigator: MainNavigator,
val serverInteractor: GetCurrentServerInteractor,
val factory: RocketChatClientFactory
) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun createNewChannel(
fun createChannel(
roomType: RoomType,
channelName: String,
usersList: List<String>,
......@@ -27,18 +32,40 @@ class CreateNewChannelPresenter @Inject constructor(
) {
launchUI(strategy) {
view.showLoading()
view.disableUserInput()
try {
client.createChannel(roomType, channelName, usersList, readOnly)
view.prepareToShowChatList()
view.showChannelCreatedSuccessfullyMessage()
toChatList()
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessageAndClearText(it)
view.showMessage(it)
}.ifNull {
view.showErrorMessage()
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
view.enableUserInput()
}
}
}
fun searchUser(query: String) {
launchUI(strategy) {
try {
val users = client.searchUser(query, count = 5)
val memberViewModelMapper = mapper.mapToViewModelList(users.result)
view.showUserSuggestion(memberViewModelMapper)
} catch (ex: RocketChatException) {
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
}
}
fun toChatList() = navigator.toChatList()
}
\ No newline at end of file
package chat.rocket.android.createchannel.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.android.members.viewmodel.MemberViewModel
interface CreateChannelView : LoadingView, MessageView {
/**
* Shows the server's users suggestion (on the basis of the user typing - the query).
*
* @param dataSet The list of server's users to show.
*/
fun showUserSuggestion(dataSet: List<MemberViewModel>)
/**
* Shows the navigation drawer with the chat item checked before showing the chat list.
* This function is invoked after successfully created the channel.
*/
fun prepareToShowChatList()
/**
* Shows a message that a channel was successfully created.
*/
fun showChannelCreatedSuccessfullyMessage()
/**
* Enables the user input.
*/
fun enableUserInput()
/**
* Disables the user input.
*/
fun disableUserInput()
}
\ No newline at end of file
package chat.rocket.android.createchannel.ui
import android.os.Bundle
import android.support.design.chip.Chip
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.support.v7.view.ActionMode
import android.support.v7.widget.LinearLayoutManager
import android.view.*
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import chat.rocket.android.R
import chat.rocket.android.createchannel.presentation.CreateChannelPresenter
import chat.rocket.android.createchannel.presentation.CreateChannelView
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.viewmodel.MemberViewModel
import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType
import dagger.android.support.AndroidSupportInjection
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_create_channel.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback {
@Inject
lateinit var createChannelPresenter: CreateChannelPresenter
private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter {
if (it.username != null) {
processSelectedMember(it.username)
}
}
private val compositeDisposable = CompositeDisposable()
private var channelType: RoomType = RoomType.CHANNEL
private var isChannelReadOnly: Boolean = false
private var memberList = arrayListOf<String>()
companion object {
fun newInstance() = CreateChannelFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_create_channel)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolBar()
setupViewListeners()
setupRecyclerView()
subscribeEditTexts()
}
override fun onDestroyView() {
super.onDestroyView()
unsubscribeEditTexts()
}
override fun showLoading() {
ui {
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
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))
}
override fun showUserSuggestion(dataSet: List<MemberViewModel>) {
// Hiding the progress because we are showing it already.
hideSuggestionViewInProgress()
if (dataSet.isEmpty()) {
showNoSuggestionView()
} else {
showSuggestionViewResults(dataSet)
}
}
override fun prepareToShowChatList() {
with(activity as MainActivity) {
setCheckedNavDrawerItem(R.id.action_chat_rooms)
openDrawer()
getDrawerLayout().postDelayed(600) {
closeDrawer()
createChannelPresenter.toChatList()
}
}
}
override fun showChannelCreatedSuccessfullyMessage() {
showMessage(getString(R.string.msg_channel_created_successfully))
}
override fun enableUserInput() {
text_channel_name.isEnabled = true
text_invite_members.isEnabled = true
}
override fun disableUserInput() {
text_channel_name.isEnabled = false
text_invite_members.isEnabled = false
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.create_channel, menu)
mode.title = getString(R.string.title_create_channel)
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false
override fun onActionItemClicked(mode: ActionMode, menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_create_channel -> {
createChannelPresenter.createChannel(
channelType,
text_channel_name.text.toString(),
memberList,
isChannelReadOnly
)
mode.finish()
true
}
else -> {
false
}
}
}
override fun onDestroyActionMode(mode: ActionMode) {
actionMode = null
}
private fun setupToolBar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_create_channel)
}
private fun setupViewListeners() {
switch_channel_type.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
text_channel_type.text = getString(R.string.msg_private_channel)
text_channel_type_description.text =
getString(R.string.msg_private_channel_description)
image_channel_icon.setImageDrawable(
context?.getDrawable(R.drawable.ic_lock_black_12_dp)
)
channelType = RoomType.PRIVATE_GROUP
} else {
text_channel_type.text = getString(R.string.msg_public_channel)
text_channel_type_description.text =
getString(R.string.msg_public_channel_description)
image_channel_icon.setImageDrawable(
context?.getDrawable(R.drawable.ic_hashtag_black_12dp)
)
channelType = RoomType.CHANNEL
}
}
switch_read_only.setOnCheckedChangeListener { _, isChecked ->
isChannelReadOnly = isChecked
}
}
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it))
recycler_view.adapter = adapter
}
}
private fun subscribeEditTexts() {
val channelNameDisposable = text_channel_name.asObservable()
.subscribe {
if (it.isNotBlank()) {
startActionMode()
} else {
finishActionMode()
}
}
val inviteMembersDisposable = text_invite_members.asObservable()
.debounce(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe {
if (it.length >= 3) {
showSuggestionViewInProgress()
createChannelPresenter.searchUser(it.toString())
} else {
hideSuggestionView()
}
}
compositeDisposable.addAll(channelNameDisposable, inviteMembersDisposable)
}
private fun unsubscribeEditTexts() {
compositeDisposable.dispose()
}
private fun startActionMode() {
if (actionMode == null) {
actionMode = (activity as MainActivity).startSupportActionMode(this)
}
}
private fun finishActionMode() {
actionMode?.finish()
}
private fun processSelectedMember(username: String) {
if (memberList.contains(username)) {
showMessage(getString(R.string.msg_member_already_added))
} else {
hideSuggestionView()
text_invite_members.setText("")
addMember(username)
addChip(username)
chip_group_member.isVisible = true
}
}
private fun addMember(username: String) {
memberList.add(username)
}
private fun removeMember(username: String) {
memberList.remove(username)
}
private fun addChip(chipText: String) {
val chip = Chip(context)
chip.chipText = chipText
chip.isCloseIconEnabled = true
chip.setChipBackgroundColorResource(R.color.icon_grey)
setupChipOnCloseIconClickListener(chip)
chip_group_member.addView(chip)
}
private fun setupChipOnCloseIconClickListener(chip: Chip) {
chip.setOnCloseIconClickListener {
removeChip(it)
removeMember((it as Chip).chipText.toString())
// whenever we remove a chip we should process the chip group visibility.
processChipGroupVisibility()
}
}
private fun removeChip(chip: View) {
chip_group_member.removeView(chip)
}
private fun processChipGroupVisibility() {
chip_group_member.isVisible = memberList.isNotEmpty()
}
private fun showSuggestionView() {
view_member_suggestion.isVisible = true
}
private fun hideSuggestionView() {
view_member_suggestion.isVisible = false
}
private fun showSuggestionViewInProgress() {
recycler_view.isVisible = false
text_member_not_found.isVisible = false
view_member_suggestion_loading.isVisible = true
showSuggestionView()
}
private fun hideSuggestionViewInProgress() {
view_member_suggestion_loading.isVisible = false
}
private fun showSuggestionViewResults(dataSet: List<MemberViewModel>) {
adapter.clearData()
adapter.prependData(dataSet)
text_member_not_found.isVisible = false
recycler_view.isVisible = true
showSuggestionView()
}
private fun showNoSuggestionView() {
recycler_view.isVisible = false
text_member_not_found.isVisible = true
showSuggestionView()
}
}
\ No newline at end of file
......@@ -13,11 +13,9 @@ 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.addMembers.di.AddMembersModule
import chat.rocket.android.createChannel.addMembers.ui.AddMembersActivity
import chat.rocket.android.createChannel.di.CreateNewChannelModule
import chat.rocket.android.createChannel.di.CreateNewChannelProvider
import chat.rocket.android.createChannel.ui.CreateNewChannelActivity
import chat.rocket.android.createchannel.addmembers.di.AddMembersModule
import chat.rocket.android.createchannel.addmembers.ui.AddMembersActivity
import chat.rocket.android.createchannel.di.CreateChannelProvider
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.files.di.FilesFragmentProvider
import chat.rocket.android.main.di.MainModule
......@@ -27,6 +25,7 @@ import chat.rocket.android.pinnedmessages.di.PinnedMessagesFragmentProvider
import chat.rocket.android.profile.di.ProfileFragmentProvider
import chat.rocket.android.server.di.ChangeServerModule
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 dagger.Module
......@@ -52,7 +51,9 @@ abstract class ActivityBuilder {
@ContributesAndroidInjector(
modules = [MainModule::class,
ChatRoomsFragmentProvider::class,
ProfileFragmentProvider::class
CreateChannelProvider::class,
ProfileFragmentProvider::class,
SettingsFragmentProvider::class
]
)
abstract fun bindMainActivity(): MainActivity
......@@ -78,10 +79,6 @@ abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = [ChangeServerModule::class])
abstract fun bindChangeServerActivity(): ChangeServerActivity
@PerActivity
@ContributesAndroidInjector(modules = [CreateNewChannelModule::class])
abstract fun bindCreateNewChannelActivity(): CreateNewChannelActivity
@PerActivity
@ContributesAndroidInjector(modules = [AddMembersModule::class])
abstract fun bindAddMembersActivity(): AddMembersActivity
......
......@@ -4,6 +4,7 @@ import chat.rocket.android.R
import chat.rocket.android.authentication.ui.newServerIntent
import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.createchannel.ui.CreateChannelFragment
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.ui.ProfileFragment
import chat.rocket.android.server.ui.changeServerIntent
......@@ -18,6 +19,12 @@ class MainNavigator(internal val activity: MainActivity) {
}
}
fun toCreateChannel() {
activity.addFragment("CreateChannelFragment", R.id.fragment_container) {
CreateChannelFragment.newInstance()
}
}
fun toUserProfile() {
activity.addFragment("ProfileFragment", R.id.fragment_container) {
ProfileFragment.newInstance()
......@@ -30,15 +37,26 @@ class MainNavigator(internal val activity: MainActivity) {
}
}
fun toChatRoom(chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean,
isChatRoomCreator: Boolean) {
activity.startActivity(activity.chatRoomIntent(chatRoomId, chatRoomName, chatRoomType,
isChatRoomReadOnly, chatRoomLastSeen, isChatRoomSubscribed, isChatRoomCreator))
fun toChatRoom(
chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean,
isChatRoomCreator: Boolean
) {
activity.startActivity(
activity.chatRoomIntent(
chatRoomId,
chatRoomName,
chatRoomType,
isChatRoomReadOnly,
chatRoomLastSeen,
isChatRoomSubscribed,
isChatRoomCreator
)
)
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
}
......
......@@ -55,6 +55,8 @@ class MainPresenter @Inject constructor(
fun toSettings() = navigator.toSettings()
fun toCreateChannel() = navigator.toCreateChannel()
fun loadCurrentInfo() {
checkServerInfo(currentServer)
launchUI(strategy) {
......
......@@ -4,7 +4,9 @@ import DrawableHelper
import android.app.Activity
import android.app.AlertDialog
import android.os.Bundle
import android.support.annotation.IdRes
import android.support.v4.app.Fragment
import android.support.v4.widget.DrawerLayout
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.view.Gravity
......@@ -26,7 +28,6 @@ import chat.rocket.android.util.extensions.showToast
import chat.rocket.common.model.UserStatus
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import com.google.android.gms.common.api.GoogleApiClient
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
......@@ -186,14 +187,14 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
setSupportActionBar(toolbar)
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
toolbar.setNavigationOnClickListener {
drawer_layout.openDrawer(Gravity.START)
openDrawer()
}
}
private fun setupNavigationView() {
view_navigation.setNavigationItemSelectedListener { menuItem ->
menuItem.isChecked = true
drawer_layout.closeDrawer(Gravity.START)
closeDrawer()
onNavDrawerItemSelected(menuItem)
true
}
......@@ -207,6 +208,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
R.id.action_profile -> {
presenter.toUserProfile()
}
R.id.action_channel -> {
presenter.toCreateChannel()
}
R.id.action_settings -> {
presenter.toSettings()
}
......@@ -244,9 +248,25 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
}
header.image_avatar.setOnClickListener {
view_navigation.menu.findItem(R.id.action_profile).isChecked = true
view_navigation.menu.findItem(R.id.action_update_profile).isChecked = true
presenter.toUserProfile()
drawer_layout.closeDrawer(Gravity.START)
}
}
fun getDrawerLayout(): DrawerLayout {
return drawer_layout
}
fun openDrawer() {
drawer_layout.openDrawer(Gravity.START)
}
fun closeDrawer() {
drawer_layout.closeDrawer(Gravity.START)
}
fun setCheckedNavDrawerItem(@IdRes item: Int) {
view_navigation.setCheckedItem(item)
}
}
\ No newline at end of file
......@@ -10,17 +10,22 @@ import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_member.view.*
class MembersAdapter(private val listener: (MemberViewModel) -> Unit) : RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
private var dataSet: List<MemberViewModel> = ArrayList()
class MembersAdapter(private val listener: (MemberViewModel) -> Unit) :
RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
private var dataSet = emptyList<MemberViewModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder = ViewHolder(parent.inflate(R.layout.item_member))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder =
ViewHolder(parent.inflate(R.layout.item_member))
override fun onBindViewHolder(holder: MembersAdapter.ViewHolder, position: Int) = holder.bind(dataSet[position], listener)
override fun onBindViewHolder(holder: MembersAdapter.ViewHolder, position: Int) =
holder.bind(dataSet[position], listener)
override fun getItemCount(): Int = dataSet.size
fun reAllocateArrayList(){
this.dataSet = ArrayList()
fun clearData() {
val itemCount = dataSet.size
dataSet = emptyList()
notifyItemRangeRemoved(0, itemCount)
}
fun prependData(dataSet: List<MemberViewModel>) {
......@@ -36,11 +41,11 @@ class MembersAdapter(private val listener: (MemberViewModel) -> Unit) : Recycler
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(memberViewModel: MemberViewModel, listener: (MemberViewModel) -> Unit) = with(itemView) {
image_avatar.setImageURI(memberViewModel.avatarUri)
text_member.content = memberViewModel.displayName
setOnClickListener { listener(memberViewModel) }
}
fun bind(memberViewModel: MemberViewModel, listener: (MemberViewModel) -> Unit) =
with(itemView) {
image_avatar.setImageURI(memberViewModel.avatarUri)
text_member.content = memberViewModel.displayName
setOnClickListener { listener(memberViewModel) }
}
}
}
\ No newline at end of file
......@@ -36,8 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MembersFragment : Fragment(), MembersView {
@Inject
lateinit var presenter: MembersPresenter
private val adapter: MembersAdapter =
MembersAdapter { memberViewModel -> presenter.toMemberDetails(memberViewModel) }
private val adapter: MembersAdapter = MembersAdapter { presenter.toMemberDetails(it) }
private val linearLayoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
private lateinit var chatRoomId: String
......
......@@ -36,11 +36,6 @@ class MemberViewModel(
private fun getUserDisplayName(): String {
val username = member.username
val realName = member.name
if (username == null && realName != null) {
return realName
} else if (username != null && realName == null) {
return username
}
val senderName = if (settings.useRealName()) realName else username
return senderName ?: username.toString()
}
......
......@@ -13,20 +13,21 @@ import chat.rocket.android.profile.presentation.ProfilePresenter
import chat.rocket.android.profile.presentation.ProfileView
import chat.rocket.android.util.extensions.*
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.avatar_profile.*
import kotlinx.android.synthetic.main.fragment_profile.*
import javax.inject.Inject
class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
@Inject lateinit var presenter: ProfilePresenter
@Inject
lateinit var presenter: ProfilePresenter
private lateinit var currentName: String
private lateinit var currentUsername: String
private lateinit var currentEmail: String
private lateinit var currentAvatar: String
private var actionMode: ActionMode? = null
private val disposables = CompositeDisposable()
private lateinit var editTextsDisposable: Disposable
companion object {
fun newInstance() = ProfileFragment()
......@@ -37,7 +38,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_profile)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_profile)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......@@ -51,8 +56,8 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
override fun onDestroyView() {
disposables.clear()
super.onDestroyView()
unsubscribeEditTexts()
}
override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) {
......@@ -71,7 +76,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
profile_container.setVisible(true)
listenToChanges()
subscribeEditTexts()
}
}
......@@ -119,8 +124,13 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode, menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_profile -> {
presenter.updateUserProfile(text_email.textContent, text_name.textContent, text_username.textContent, text_avatar_url.textContent)
R.id.action_update_profile -> {
presenter.updateUserProfile(
text_email.textContent,
text_name.textContent,
text_username.textContent,
text_avatar_url.textContent
)
mode.finish()
true
}
......@@ -135,28 +145,36 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_profile)
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_profile)
}
private fun tintEditTextDrawableStart() {
(activity as MainActivity).apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val emailDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val linkDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_link_black_24dp, this)
val drawables = arrayOf(personDrawable, atDrawable, emailDrawable, linkDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_email, text_avatar_url), drawables)
DrawableHelper.compoundDrawables(
arrayOf(text_name, text_username, text_email, text_avatar_url),
drawables
)
}
}
private fun listenToChanges() {
disposables.add(Observables.combineLatest(text_name.asObservable(),
text_username.asObservable(),
text_email.asObservable(),
text_avatar_url.asObservable()) { text_name, text_username, text_email, text_avatar_url ->
private fun subscribeEditTexts() {
editTextsDisposable = Observables.combineLatest(
text_name.asObservable(),
text_username.asObservable(),
text_email.asObservable(),
text_avatar_url.asObservable()
) { text_name, text_username, text_email, text_avatar_url ->
return@combineLatest (text_name.toString() != currentName ||
text_username.toString() != currentUsername ||
text_email.toString() != currentEmail ||
......@@ -167,7 +185,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
} else {
finishActionMode()
}
})
}
}
private fun unsubscribeEditTexts() {
editTextsDisposable.dispose()
}
private fun startActionMode() {
......
package chat.rocket.android.settings.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.settings.presentation.SettingsView
import chat.rocket.android.settings.ui.SettingsFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class SettingsFragmentModule {
@Provides
fun settingsView(frag: SettingsFragment): SettingsView {
return frag
}
@Provides
fun settingsLifecycleOwner(frag: SettingsFragment): LifecycleOwner {
fun provideLifecycleOwner(frag: SettingsFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ import dagger.android.ContributesAndroidInjector
@Module
abstract class SettingsFragmentProvider {
@ContributesAndroidInjector(modules = [SettingsFragmentModule::class])
abstract fun provideSettingsFragment(): SettingsFragment
}
\ No newline at end of file
......@@ -16,16 +16,19 @@ import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.fragment_settings.*
import kotlin.reflect.KClass
class SettingsFragment: Fragment(), SettingsView, AdapterView.OnItemClickListener {
class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener {
companion object {
fun newInstance() = SettingsFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_settings)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_settings)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListView()
}
......@@ -34,7 +37,8 @@ class SettingsFragment: Fragment(), SettingsView, AdapterView.OnItemClickListene
when (parent?.getItemAtPosition(position).toString()) {
resources.getString(R.string.title_password) -> {
startNewActivity(PasswordActivity::class)
}resources.getString(R.string.title_about) -> {
}
resources.getString(R.string.title_about) -> {
startNewActivity(AboutActivity::class)
}
}
......@@ -45,7 +49,8 @@ class SettingsFragment: Fragment(), SettingsView, AdapterView.OnItemClickListene
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_settings)
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_settings)
}
private fun startNewActivity(classType: KClass<out AppCompatActivity>) {
......
......@@ -4,10 +4,10 @@ import android.widget.EditText
import com.jakewharton.rxbinding2.widget.RxTextView
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
import io.reactivex.schedulers.Schedulers
fun EditText.asObservable(): Observable<CharSequence> {
return RxTextView.textChanges(this)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.mainThread())
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:endColor="@color/default_background"
android:startColor="@color/default_background" />
<stroke
android:width="1dp"
android:color="@color/colorRed" />
<corners
android:bottomRightRadius="5dp"
android:topRightRadius="5dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorRed" />
<corners
android:bottomLeftRadius="5dp"
android:topLeftRadius="5dp" />
</shape>
\ No newline at end of file
......@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorSecondaryText"
android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
android:fillColor="#FF000000"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>
......@@ -2,7 +2,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/colorAccent" />
<solid android:color="@color/colorAccent" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#10000000" />
<corners android:radius="5dp" />
<size android:height="2dp" />
</shape>
\ No newline at end of file
......@@ -26,6 +26,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:visibility="gone"
app:chipSpacing="3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
......@@ -45,7 +46,7 @@
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/search_view"
android:id="@+id/text_search_member"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@android:color/transparent"
......@@ -60,15 +61,14 @@
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="@color/colorDividerMessageComposer"
app:layout_constraintTop_toBottomOf="@id/search_view" />
app:layout_constraintTop_toBottomOf="@id/text_search_member" />
<android.support.v7.widget.RecyclerView
android:id="@+id/search_results"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="8dp"
android:scrollbars="vertical"
app:layout_constraintTop_toBottomOf="@id/separator_1"
app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/separator_1" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="match_parent">
<include
android:id="@+id/toolbar_layout"
layout="@layout/layout_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/layout_container"
app:layout_constraintTop_toTopOf="parent" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_layout"
tools:visibility="visible" />
<android.support.constraint.ConstraintLayout
android:id="@+id/layout_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_layout">
<android.support.constraint.Guideline
android:id="@+id/button_top_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.03" />
<android.support.constraint.Guideline
android:id="@+id/button_bottom_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.09" />
<Button
android:id="@+id/public_channel"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="@drawable/button_solid"
android:text="@string/public_channel_type"
android:textColor="@color/default_background"
app:layout_constraintBottom_toBottomOf="@id/button_bottom_guideline"
app:layout_constraintEnd_toStartOf="@+id/private_channel"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/button_top_guideline" />
<Button
android:id="@+id/private_channel"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="@drawable/button_border"
android:text="@string/private_channel_type"
android:textColor="@color/colorRed"
app:layout_constraintBottom_toBottomOf="@id/button_bottom_guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/public_channel"
app:layout_constraintTop_toTopOf="@id/button_top_guideline" />
<TextView
android:id="@+id/channel_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/public_channel_type"
android:textColor="@color/colorPrimaryText"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/public_channel" />
<TextView
android:id="@+id/channel_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:text="@string/public_channel_description"
android:textColor="@color/colorSecondaryText"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/channel_type" />
<View
android:id="@+id/separator_1"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:layout_marginTop="8dp"
android:background="@color/colorDividerMessageComposer"
app:layout_constraintTop_toBottomOf="@id/channel_description" />
<ImageView
android:id="@+id/placeholder"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:src="@drawable/ic_hashtag_black_12dp"
app:layout_constraintBottom_toBottomOf="@id/channel_name_text_input_layout"
app:layout_constraintEnd_toStartOf="@id/channel_name_text_input_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/channel_name_text_input_layout" />
<android.support.design.widget.TextInputLayout
android:id="@+id/channel_name_text_input_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:focusable="true"
android:hint="@string/msg_channel_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/placeholder"
app:layout_constraintTop_toBottomOf="@id/separator_1">
<android.support.design.widget.TextInputEditText
android:id="@+id/channel_name_edit_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
<View
android:id="@+id/separator_2"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:layout_marginTop="8dp"
android:background="@color/colorDividerMessageComposer"
app:layout_constraintTop_toBottomOf="@id/channel_name_text_input_layout" />
<android.support.constraint.ConstraintLayout
android:id="@+id/add_members_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingBottom="8dp"
android:paddingTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/separator_2">
<TextView
android:id="@+id/add_members_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="@string/msg_add_members"
android:textColor="@color/colorSecondaryText"
android:textSize="16sp"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:src="@drawable/ic_person_add_black_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toEndOf="@id/add_members_text"
app:layout_constraintTop_toTopOf="parent" />
<android.support.design.chip.ChipGroup
android:id="@+id/selected_members_chips"
style="@style/Widget.MaterialComponents.Chip.Entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:chipSpacing="3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_members_text">
</android.support.design.chip.ChipGroup>
</android.support.constraint.ConstraintLayout>
<View
android:id="@+id/separator_3"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:background="@color/colorDividerMessageComposer"
app:layout_constraintTop_toBottomOf="@id/add_members_view" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -46,25 +46,13 @@
tools:visibility="visible" />
<TextView
android:id="@+id/text_no_search"
android:id="@+id/text_no_result_found"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="56dp"
android:text="@string/msg_no_search_found"
android:textSize="20sp"
android:layout_centerHorizontal="true"
android:visibility="gone"
tools:visibility="visible" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/create_new_channel_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
app:useCompatPadding="true"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_add_white_24dp"
app:backgroundTint="@color/colorAccent"
/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="match_parent"
android:layout_margin="16dp"
android:focusableInTouchMode="true"
tools:context="createchannel.ui.CreateChannelFragment">
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/text_channel_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_public_channel"
android:textColor="@color/colorPrimaryText"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_channel_type_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_public_channel_description"
android:textColor="@color/colorSecondaryText"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_channel_type" />
<Switch
android:id="@+id/switch_channel_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/text_channel_type_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_channel_type" />
<TextView
android:id="@+id/text_read_only"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/msg_ready_only_channel"
android:textColor="@color/colorPrimaryText"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_channel_type_description" />
<TextView
android:id="@+id/text_read_only_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_ready_only_channel_description"
android:textColor="@color/colorSecondaryText"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_read_only" />
<Switch
android:id="@+id/switch_read_only"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/text_read_only_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_read_only" />
<ImageView
android:id="@+id/image_channel_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:src="@drawable/ic_hashtag_black_12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_read_only_description" />
<EditText
android:id="@+id/text_channel_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorDim"
android:hint="@string/msg_channel_name"
android:inputType="text"
android:maxLines="1"
android:paddingEnd="10dp"
android:paddingStart="24dp"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/image_channel_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_channel_icon"
app:layout_constraintTop_toTopOf="@+id/image_channel_icon" />
<ImageView
android:id="@+id/image_invite_member"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_at_black_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_channel_name" />
<EditText
android:id="@+id/text_invite_members"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorDim"
android:hint="@string/msg_invite_members"
android:inputType="text"
android:maxLines="1"
android:paddingEnd="10dp"
android:paddingStart="24dp"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/image_invite_member"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_invite_member"
app:layout_constraintTop_toTopOf="@+id/image_invite_member" />
<android.support.constraint.ConstraintLayout
android:id="@+id/view_member_suggestion"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:background="@color/colorWhite"
android:elevation="2dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/text_invite_members">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_member_suggestion_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_member_not_found"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_member_not_found"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.design.chip.ChipGroup
android:id="@+id/chip_group_member"
style="@style/Widget.MaterialComponents.Chip.Entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
app:chipSpacing="3dp"
app:layout_constraintTop_toBottomOf="@+id/text_invite_members" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -19,17 +19,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorLightTheme"
android:textSize="20sp" />
android:textSize="18sp" />
<TextView
android:id="@+id/toolbar_action_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:alpha="0.7"
android:enabled="false"
android:gravity="end"
android:textSize="16sp" />
android:textSize="14sp" />
</android.support.v7.widget.Toolbar>
......
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_create_channel"
android:icon="@drawable/ic_check_white_24dp"
android:title="@string/action_create" />
</menu>
\ No newline at end of file
......@@ -11,6 +11,16 @@
android:icon="@drawable/ic_chat_bubble_black_24dp"
android:title="@string/title_chats" />
<item
android:id="@+id/action_channel"
android:icon="@drawable/ic_create_black_24dp"
android:title="@string/action_create_channel" />
</group>
<group
android:id="@+id/menu_section_2"
android:checkableBehavior="single">
<item
android:id="@+id/action_profile"
android:icon="@drawable/ic_person_black_24dp"
......@@ -19,16 +29,16 @@
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings_black_24dp"
android:title="@string/title_settings"/>
android:title="@string/title_settings" />
</group>
<group
android:id="@+id/menu_section_2"
android:checkableBehavior="none">
android:id="@+id/menu_section_3"
android:checkableBehavior="single">
<item
android:id="@+id/action_logout"
android:icon="@drawable/ic_exit_to_app_black_24dp"
android:title="@string/action_logout" />
</group>
</menu>
\ No newline at end of file
......@@ -2,7 +2,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_profile"
android:id="@+id/action_update_profile"
android:icon="@drawable/ic_check_white_24dp"
android:title="@string/action_update" />
......
......@@ -16,7 +16,7 @@
<string name="title_update_profile">Actualización del perfil</string>
<string name="title_about">Acerca de</string>
// TODO: Add proper translation.
<string name="title_create_new_channel">Create New Channel</string>
<string name="title_create_channel">Create Channel</string>
<string name="title_add_members">Invita a los miembros (%s)</string>
<!-- Actions -->
......@@ -28,6 +28,8 @@
<string name="action_search">Buscar</string>
<string name="action_update">Actualizar</string>
<string name="action_settings">Configuraciones</string>
// TODO: Add proper translation.
<string name="action_create_channel">Create channel</string>
<string name="action_logout">Cerrar sesión</string>
<string name="action_files">Archivos</string>
<string name="action_confirm_password">Confirmar cambio de contraseña</string>
......@@ -130,11 +132,7 @@
<string name="msg_several_users_are_typing">Several users are typing…</string>
<string name="msg_no_search_found">No se han encontrado resultados</string>
// TODO: Add proper translation.
<string name="msg_member_already_added">You have already selected this user</string>
// TODO: Add proper translation.
<string name="msg_channel_created_successfully">"Channel created successfully</string>
// TODO: Add proper translation.
<string name="msg_channel_name">Channel Name</string>
<string name="msg_channel_name">Channel name</string>
// TODO: Add proper translation.
<string name="msg_add_members">Add Members</string>
// TODO: Add proper translation.
......@@ -142,6 +140,20 @@
<string name="msg_message_copied">Mensaje copiado</string>
<string name="msg_channel_name_required">Por favor ingrese un nombre de canal</string>
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<!-- System messages -->
<string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuario %1$s añadido por %2$s</string>
......@@ -158,7 +170,6 @@
// TODO:Add proper translation.
<string name="message_credentials_saved_successfully">Credentials saved successfully</string>
<!-- Message actions -->
<string name="action_msg_reply">Respuesta</string>
<string name="action_msg_edit">Editar</string>
......@@ -263,9 +274,4 @@
<string name="notif_action_reply_hint">RESPUESTA</string>
<string name="notif_error_sending">La respuesta ha fallado. Inténtalo de nuevo.</string>
<string name="notif_success_sending">Mensaje enviado a %1$s!</string>
<string name="private_channel_type">Privado</string>
<string name="public_channel_type">Público</string>
<string name="private_channel_type_description">Visible solo para ti y para aquellos a quienes invitas</string>
<string name="public_channel_description">Visible para todos</string>
</resources>
......@@ -16,7 +16,7 @@
<string name="title_update_profile">Update profile</string>
<string name="title_about">Sur</string>
// TODO: Add proper translation.
<string name="title_create_new_channel">Create New Channel</string>
<string name="title_create_channel">Create Channel</string>
<string name="title_add_members">Inviter des membres (%s)</string>
<!-- Actions -->
......@@ -28,6 +28,8 @@
<string name="action_search">Chercher</string>
<string name="action_update">Mettre à jour</string>
<string name="action_settings">Paramètres</string>
// TODO: Add proper translation.
<string name="action_create_channel">Create channel</string>
<string name="action_logout">Se déconnecter</string>
<string name="action_files">Fichiers</string>
<string name="action_confirm_password">Confirmer le mot de passe</string>
......@@ -130,11 +132,7 @@
<string name="msg_several_users_are_typing">Several users are typing…</string>
<string name="msg_no_search_found">Aucun résultat trouvé</string>
// TODO: Add proper translation.
<string name="msg_member_already_added">You have already selected this user</string>
// TODO: Add proper translation.
<string name="msg_channel_created_successfully">"Channel created successfully</string>
// TODO: Add proper translation.
<string name="msg_channel_name">Channel Name</string>
<string name="msg_channel_name">Channel name</string>
// TODO: Add proper translation.
<string name="msg_add_members">Add Members</string>
// TODO: Add proper translation.
......@@ -142,6 +140,19 @@
<string name="msg_message_copied">Message copié</string>
<string name="msg_channel_name_required">Veuillez entrer un nom de chaîne</string>
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<!-- System messages -->
<string name="message_room_name_changed">Le nom de le salle a changé à: %1$s par %2$s</string>
<string name="message_user_added_by">Utilisateur %1$s ajouté par %2$s</string>
......@@ -155,10 +166,10 @@
<string name="message_unmuted">Utilisateur %1$s non muté par %2$s</string>
<string name="message_role_add">%1$s a été défini %2$s par %3$s</string>
<string name="message_role_removed">%1$s is no longer %2$s par %3$s</string>
// TODO:Add proper translation.
<string name="message_credentials_saved_successfully">Credentials saved successfully</string>
<!-- Message actions -->
<string name="action_msg_reply">Répondre</string>
<string name="action_msg_edit">Modifier</string>
......@@ -264,9 +275,4 @@
<string name="notif_action_reply_hint">RÉPONDRE</string>
<string name="notif_error_sending">La réponse a échoué. Veuillez réessayer.</string>
<string name="notif_success_sending">Message envoyé à %1$s!</string>
<string name="private_channel_type">Privé</string>
<string name="public_channel_type">Public</string>
<string name="private_channel_type_description">Visible uniquement pour vous et ceux que vous invitez</string>
<string name="public_channel_description">Visible par tout le monde</string>
</resources>
......@@ -17,7 +17,7 @@
<string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string>
<string name="title_about">परिचय</string>
// TODO: Add proper translation.
<string name="title_create_new_channel">Create New Channel</string>
<string name="title_create_channel">Create Channel</string>
<string name="title_add_members">सदस्यों को आमंत्रित करें (%s)</string>
<!-- Actions -->
......@@ -29,6 +29,8 @@
<string name="action_search">खोजें</string>
<string name="action_update">अद्यतन करें</string>
<string name="action_settings">सेटिंग्स</string>
// TODO: Add proper translation.
<string name="action_create_channel">Create channel</string>
<string name="action_logout">लोग आउट करें</string>
<string name="action_files">फ़ाइलें</string>
<string name="action_confirm_password">पासवर्ड परिवर्तन की पुष्टि करें</string>
......@@ -132,11 +134,7 @@
<string name="msg_several_users_are_typing">Several users are typing…</string>
<string name="msg_no_search_found">कोई परिणाम नहीं मिला</string>
// TODO: Add proper translation.
<string name="msg_member_already_added">You have already selected this user</string>
// TODO: Add proper translation.
<string name="msg_channel_created_successfully">"Channel created successfully</string>
// TODO: Add proper translation.
<string name="msg_channel_name">Channel Name</string>
<string name="msg_channel_name">Channel name</string>
// TODO: Add proper translation.
<string name="msg_add_members">Add Members</string>
// TODO: Add proper translation.
......@@ -144,6 +142,19 @@
<string name="msg_message_copied">संदेश कॉपी किया गया</string>
<string name="msg_channel_name_required">कृपया एक चैनल नाम दर्ज करें</string>
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
<string name="message_user_added_by">उपयोगकर्ता %1$s द्वारा %2$s को जोड़ा गया</string>
......@@ -265,9 +276,4 @@
<string name="notif_action_reply_hint">जवाब</string>
<string name="notif_error_sending">उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।</string>
<string name="notif_success_sending">संदेश भेजा गया %1$s!</string>
<string name="private_channel_type">निजी</string>
<string name="public_channel_type">सार्वजनिक</string>
<string name="private_channel_type_description">केवल आप के लिए दृश्यमान और जिन्हें आप आमंत्रित करते हैं</string>
<string name="public_channel_description">हर किसी के लिए दृश्यमान</string>
</resources>
\ No newline at end of file
......@@ -16,7 +16,7 @@
<string name="title_update_profile">Editar perfil</string>
<string name="title_about">Sobre</string>
// TODO: Add proper translation.
<string name="title_create_new_channel">Create New Channel</string>
<string name="title_create_channel">Create Channel</string>
<string name="title_add_members">Convidar membros (%s)</string>
<!-- Actions -->
......@@ -28,6 +28,8 @@
<string name="action_search">Pesquisar</string>
<string name="action_update">Atualizar</string>
<string name="action_settings">Configurações</string>
// TODO: Add proper translation.
<string name="action_create_channel">Create channel</string>
<string name="action_logout">Sair</string>
<string name="action_files">Arquivos</string>
<string name="action_confirm_password">Confirme a nova senha</string>
......@@ -121,19 +123,25 @@
<string name="msg_are_typing">\u0020estão digitando…</string>
<string name="msg_several_users_are_typing">Vários usuários estão digitando…</string>
<string name="msg_no_search_found">nenhum resultado encontrado</string>
// TODO: Add proper translation.
<string name="msg_member_already_added">You have already selected this user</string>
// TODO: Add proper translation.
<string name="msg_channel_created_successfully">"Channel created successfully</string>
// TODO: Add proper translation.
<string name="msg_channel_name">Channel Name</string>
// TODO: Add proper translation.
<string name="msg_add_members">Add Members</string>
// TODO: Add proper translation.
<string name="msg_search">Search</string>
<string name="msg_channel_name">Nome do chat</string>
<string name="msg_add_members">Adcionar membros</string>
<string name="msg_search">Buscar</string>
<string name="msg_message_copied">Mensagem copiada</string>
<string name="msg_channel_name_required">Por favor insira um nome de canal</string>
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
......@@ -251,9 +259,4 @@
<string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string>
<string name="private_channel_type">Privado</string>
<string name="public_channel_type">Público</string>
<string name="private_channel_type_description">Visível apenas para você e para aqueles a quem você convida</string>
<string name="public_channel_description">Visível para todos</string>
</resources>
......@@ -15,7 +15,7 @@
<string name="title_password">Изменить пароль</string>
<string name="title_update_profile">Обновить профиль</string>
<string name="title_about">О программе</string>
<string name="title_create_new_channel">Створити новий канал</string>
<string name="title_create_channel">Створити новий канал</string>
<string name="title_add_members">Запросити учасників (%s)</string>
<!-- Actions -->
......@@ -27,6 +27,8 @@
<string name="action_search">Поиск</string>
<string name="action_update">Обновить</string>
<string name="action_settings">Настройки</string>
// TODO: Add proper translation.
<string name="action_create_channel">Create channel</string>
<string name="action_logout">Выйти</string>
<string name="action_files">Файлы</string>
<string name="action_confirm_password">Подтверждение изменения пароля</string>
......@@ -115,18 +117,23 @@
<string name="msg_several_users_are_typing">Несколько пользователей печатают…</string>
<string name="msg_no_search_found">Результатов не найдено</string>
<string name="msg_message_copied">Сообщение скопировано</string>
<string name="msg_channel_created_successfully">Канал створений успішно</string>
<string name="msg_channel_name">Назва каналу</string>
<string name="msg_add_members">Додати членів</string>
<string name="msg_search">Пошук</string>
<string name="msg_member_already_added">Ви вже вибрали цього користувача</string>
<string name="msg_channel_name_required">Будь ласка, введіть назву каналу</string>
<!--info for creating a channel-->
<string name="private_channel_type">Приватно</string>
<string name="public_channel_type">Громадський</string>
<string name="private_channel_type_description">Видно лише вам та тими, кого ви запрошуєте</string>
<string name="public_channel_description">Видимий для всіх</string>
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<!-- System messages -->
<string name="message_room_name_changed">Название канала изменено на: %1$s by %2$s</string>
......
......@@ -15,8 +15,7 @@
<string name="title_settings">Settings</string>
<string name="title_password">Change Password</string>
<string name="title_update_profile">Update profile</string>
<string name="title_create_new_channel">Create New Channel</string>
<string name="title_add_members">Invite Members (%s)</string>
<string name="title_create_channel">Create Channel</string>
<string name="title_about">About</string>
<!-- Actions -->
......@@ -27,7 +26,9 @@
<string name="action_privacy_policy">Privacy Policy</string>
<string name="action_search">Search</string>
<string name="action_update">Update</string>
<string name="action_create">Create</string>
<string name="action_settings">Settings</string>
<string name="action_create_channel">Create channel</string>
<string name="action_logout">Logout</string>
<string name="action_files">Files</string>
<string name="action_confirm_password">Confirm Password Change</string>
......@@ -51,7 +52,6 @@
<string name="msg_generic_error">Sorry, an error has occurred, please try again</string>
<string name="msg_no_data_to_display">No data to display</string>
<string name="msg_profile_update_successfully">Profile update successfully</string>
<string name="msg_channel_created_successfully">"Channel created successfully</string>
<string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string>
<string name="msg_password">password</string>
......@@ -88,7 +88,7 @@
<string name="msg_utc_offset">UTC offset</string>
<string name="msg_new_password">Enter New Password</string>
<string name="msg_confirm_password">Confirm New Password</string>
<string name="msg_channel_name">Channel Name</string>
<string name="msg_channel_name">Channel name</string>
<string name="msg_add_members">Add Members</string>
<string name="msg_search">Search</string>
<string name="msg_unread_messages">Unread messages</string>
......@@ -122,14 +122,20 @@
<string name="msg_are_typing">\u0020are typing…</string>
<string name="msg_several_users_are_typing">Several users are typing…</string>
<string name="msg_no_search_found">No result found</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_channel_name_required">Please enter a channel name</string>
<!--info for creating a channel-->
<string name="private_channel_type">Private</string>
<string name="public_channel_type">Public</string>
<string name="private_channel_type_description">Visible only to you and those whom you invite</string>
<string name="public_channel_description">Visible to everyone</string>
<!-- Create channel messages -->
<string name="msg_private_channel">Private</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<string name="msg_message_copied">Message copied</string>
<!-- System messages -->
......
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