Merge branch 'develop' into feature/crypto

This commit is contained in:
Benoit Marty 2019-06-11 10:11:53 +02:00
commit 6dacb9894e
39 changed files with 902 additions and 90 deletions

View file

@ -100,7 +100,7 @@ class CreateRoomParams {
* private_chat => join_rules is set to invite. history_visibility is set to shared. * private_chat => join_rules is set to invite. history_visibility is set to shared.
* trusted_private_chat => join_rules is set to invite. history_visibility is set to shared. All invitees are given the same power level as the * trusted_private_chat => join_rules is set to invite. history_visibility is set to shared. All invitees are given the same power level as the
* room creator. * room creator.
* public_chat: => join_rules is set to public. history_visibility is set to shared. One of: ["private_chat", "public_chat", "trusted_private_chat"] * public_chat: => join_rules is set to public. history_visibility is set to shared.
*/ */
var preset: CreateRoomPreset? = null var preset: CreateRoomPreset? = null

View file

@ -18,12 +18,7 @@ package im.vector.matrix.android.internal.database
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import io.realm.Realm import io.realm.*
import io.realm.RealmChangeListener
import io.realm.RealmConfiguration
import io.realm.RealmObject
import io.realm.RealmQuery
import io.realm.RealmResults
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
private const val THREAD_NAME = "REALM_QUERY_LATCH" private const val THREAD_NAME = "REALM_QUERY_LATCH"
@ -39,6 +34,7 @@ class RealmQueryLatch<E : RealmObject>(private val realmConfiguration: RealmConf
val runnable = Runnable { val runnable = Runnable {
val realm = Realm.getInstance(realmConfiguration) val realm = Realm.getInstance(realmConfiguration)
val result = realmQueryBuilder(realm).findAllAsync() val result = realmQueryBuilder(realm).findAllAsync()
result.addChangeListener(object : RealmChangeListener<RealmResults<E>> { result.addChangeListener(object : RealmChangeListener<RealmResults<E>> {
override fun onChange(t: RealmResults<E>) { override fun onChange(t: RealmResults<E>) {
if (t.isNotEmpty()) { if (t.isNotEmpty()) {

View file

@ -66,7 +66,7 @@ class NetworkModule {
single { single {
OkHttpClient.Builder() OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(get<StethoInterceptor>()) .addNetworkInterceptor(get<StethoInterceptor>())

View file

@ -16,23 +16,16 @@
package im.vector.matrix.android.internal.session.room.create package im.vector.matrix.android.internal.session.room.create
import android.os.Handler
import android.os.HandlerThread
import arrow.core.Try import arrow.core.Try
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
import im.vector.matrix.android.internal.database.RealmQueryLatch
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomEntityFields import im.vector.matrix.android.internal.database.model.RoomEntityFields
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import io.realm.Realm
import io.realm.RealmChangeListener
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults
import java.util.concurrent.CountDownLatch
private const val THREAD_NAME = "CREATE_ROOM_"
internal interface CreateRoomTask : Task<CreateRoomParams, String> internal interface CreateRoomTask : Task<CreateRoomParams, String>
@ -47,41 +40,15 @@ internal class DefaultCreateRoomTask(private val roomAPI: RoomAPI,
}.flatMap { createRoomResponse -> }.flatMap { createRoomResponse ->
val roomId = createRoomResponse.roomId!! val roomId = createRoomResponse.roomId!!
val latch = CountDownLatch(1) // TODO Maybe do the same code for join room request ?
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
val handlerThread = HandlerThread(THREAD_NAME + hashCode()) val rql = RealmQueryLatch<RoomEntity>(realmConfiguration) { realm ->
handlerThread.start() realm.where(RoomEntity::class.java)
val handler = Handler(handlerThread.looper) .equalTo(RoomEntityFields.ROOM_ID, roomId)
handler.post {
val realm = Realm.getInstance(realmConfiguration)
if (realm.where(RoomEntity::class.java)
.equalTo(RoomEntityFields.ROOM_ID, roomId)
.findAll()
.isEmpty()) {
val result = realm.where(RoomEntity::class.java)
.equalTo(RoomEntityFields.ROOM_ID, roomId)
.findAllAsync()
result.addChangeListener(object : RealmChangeListener<RealmResults<RoomEntity>> {
override fun onChange(t: RealmResults<RoomEntity>) {
if (t.isNotEmpty()) {
result.removeChangeListener(this)
realm.close()
latch.countDown()
}
}
})
} else {
realm.close()
latch.countDown()
}
} }
latch.await() rql.await()
handlerThread.quit()
return Try.just(roomId) return Try.just(roomId)
} }
} }

View file

@ -0,0 +1,34 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.core.mvrx
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.riotredesign.core.utils.LiveEvent
abstract class NavigationViewModel<NavigationClass> : ViewModel() {
private val _navigateTo = MutableLiveData<LiveEvent<NavigationClass>>()
val navigateTo: LiveData<LiveEvent<NavigationClass>>
get() = _navigateTo
fun goTo(navigation: NavigationClass) {
_navigateTo.postValue(LiveEvent(navigation))
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.form
import android.text.Editable
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_text_input)
abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
@EpoxyAttribute
var hint: String? = null
@EpoxyAttribute
var value: String? = null
@EpoxyAttribute
var enabled: Boolean = true
@EpoxyAttribute
var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
onTextChange?.invoke(s.toString())
}
}
override fun bind(holder: Holder) {
holder.textInputLayout.isEnabled = enabled
holder.textInputLayout.hint = hint
// Update only if text is different
if (holder.textInputEditText.text.toString() != value) {
holder.textInputEditText.setText(value)
}
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
}
override fun shouldSaveViewState(): Boolean {
return false
}
override fun unbind(holder: Holder) {
super.unbind(holder)
holder.textInputEditText.removeTextChangedListener(onTextChangeListener)
}
class Holder : VectorEpoxyHolder() {
val textInputLayout by bind<TextInputLayout>(R.id.formTextInputTextInputLayout)
val textInputEditText by bind<TextInputEditText>(R.id.formTextInputTextInputEditText)
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.form
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.google.android.material.switchmaterial.SwitchMaterial
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_form_switch)
abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
@EpoxyAttribute
var listener: ((Boolean) -> Unit)? = null
@EpoxyAttribute
var enabled: Boolean = true
@EpoxyAttribute
var switchChecked: Boolean = false
@EpoxyAttribute
var title: String? = null
@EpoxyAttribute
var summary: String? = null
override fun bind(holder: Holder) {
holder.titleView.text = title
holder.summaryView.setTextOrHide(summary)
holder.switchView.isEnabled = enabled
holder.switchView.isChecked = switchChecked
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
listener?.invoke(isChecked)
}
}
override fun shouldSaveViewState(): Boolean {
return false
}
override fun unbind(holder: Holder) {
super.unbind(holder)
holder.switchView.setOnCheckedChangeListener(null)
}
class Holder : VectorEpoxyHolder() {
val titleView by bind<TextView>(R.id.formSwitchTitle)
val summaryView by bind<TextView>(R.id.formSwitchSummary)
val switchView by bind<SwitchMaterial>(R.id.formSwitchSwitch)
}
}

View file

@ -27,10 +27,12 @@ import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.hideKeyboard import im.vector.riotredesign.core.extensions.hideKeyboard
import im.vector.riotredesign.core.extensions.observeEvent
import im.vector.riotredesign.core.extensions.replaceFragment import im.vector.riotredesign.core.extensions.replaceFragment
import im.vector.riotredesign.core.platform.OnBackPressed import im.vector.riotredesign.core.platform.OnBackPressed
import im.vector.riotredesign.core.platform.ToolbarConfigurable import im.vector.riotredesign.core.platform.ToolbarConfigurable
@ -47,7 +49,13 @@ import org.koin.android.scope.ext.android.getOrCreateScope
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
// Supported navigation actions for this Activity
sealed class Navigation {
object OpenDrawer : Navigation()
}
private val homeActivityViewModel: HomeActivityViewModel by viewModel() private val homeActivityViewModel: HomeActivityViewModel by viewModel()
private lateinit var navigationViewModel: HomeNavigationViewModel
private val homeNavigator by inject<HomeNavigator>() private val homeNavigator by inject<HomeNavigator>()
// TODO Move this elsewhere // TODO Move this elsewhere
@ -67,6 +75,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindScope(getOrCreateScope(HomeModule.HOME_SCOPE)) bindScope(getOrCreateScope(HomeModule.HOME_SCOPE))
homeNavigator.activity = this homeNavigator.activity = this
navigationViewModel = ViewModelProviders.of(this).get(HomeNavigationViewModel::class.java)
drawerLayout.addDrawerListener(drawerListener) drawerLayout.addDrawerListener(drawerListener)
if (isFirstCreation()) { if (isFirstCreation()) {
val homeDrawerFragment = HomeDrawerFragment.newInstance() val homeDrawerFragment = HomeDrawerFragment.newInstance()
@ -87,6 +98,12 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
} }
}) })
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
when (navigation) {
is Navigation.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
}
}
incomingVerificationRequestHandler.ensureStarted() incomingVerificationRequestHandler.ensureStarted()
} }
@ -119,10 +136,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> {
drawerLayout.openDrawer(GravityCompat.START)
return true
}
R.id.sliding_menu_sign_out -> { R.id.sliding_menu_sign_out -> {
SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!) SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!)
return true return true

View file

@ -20,6 +20,7 @@ import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.core.view.forEachIndexed import androidx.core.view.forEachIndexed
import androidx.lifecycle.ViewModelProviders
import com.airbnb.mvrx.args import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -56,6 +57,7 @@ class HomeDetailFragment : VectorBaseFragment() {
private lateinit var currentDisplayMode: RoomListFragment.DisplayMode private lateinit var currentDisplayMode: RoomListFragment.DisplayMode
private val viewModel: HomeDetailViewModel by fragmentViewModel() private val viewModel: HomeDetailViewModel by fragmentViewModel()
private lateinit var navigationViewModel: HomeNavigationViewModel
override fun getLayoutResId(): Int { override fun getLayoutResId(): Int {
return R.layout.fragment_home_detail return R.layout.fragment_home_detail
@ -65,6 +67,9 @@ class HomeDetailFragment : VectorBaseFragment() {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
currentDisplayMode = savedInstanceState?.getSerializable(CURRENT_DISPLAY_MODE) as? RoomListFragment.DisplayMode currentDisplayMode = savedInstanceState?.getSerializable(CURRENT_DISPLAY_MODE) as? RoomListFragment.DisplayMode
?: RoomListFragment.DisplayMode.HOME ?: RoomListFragment.DisplayMode.HOME
navigationViewModel = ViewModelProviders.of(requireActivity()).get(HomeNavigationViewModel::class.java)
switchDisplayMode(currentDisplayMode) switchDisplayMode(currentDisplayMode)
setupBottomNavigationView() setupBottomNavigationView()
setupToolbar() setupToolbar()
@ -89,7 +94,7 @@ class HomeDetailFragment : VectorBaseFragment() {
groupToolbarAvatarImageView groupToolbarAvatarImageView
) )
groupToolbarAvatarImageView.setOnClickListener { groupToolbarAvatarImageView.setOnClickListener {
vectorBaseActivity.notImplemented("Group click in toolbar") navigationViewModel.goTo(HomeActivity.Navigation.OpenDrawer)
} }
} }

View file

@ -52,7 +52,7 @@ class HomeDetailViewModel(initialState: HomeDetailViewState,
.filter { it.isDirect } .filter { it.isDirect }
.map { it.notificationCount } .map { it.notificationCount }
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?.reduce { acc, i -> acc + i } ?.sumBy { i -> i }
?: 0 ?: 0
val peopleHasHighlight = summaries val peopleHasHighlight = summaries
.filter { it.isDirect } .filter { it.isDirect }
@ -62,7 +62,7 @@ class HomeDetailViewModel(initialState: HomeDetailViewState,
.filter { !it.isDirect } .filter { !it.isDirect }
.map { it.notificationCount } .map { it.notificationCount }
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?.reduce { acc, i -> acc + i } ?.sumBy { i -> i }
?: 0 ?: 0
val roomsHasHighlight = summaries val roomsHasHighlight = summaries
.filter { !it.isDirect } .filter { !it.isDirect }

View file

@ -0,0 +1,21 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.home
import im.vector.riotredesign.core.mvrx.NavigationViewModel
class HomeNavigationViewModel : NavigationViewModel<HomeActivity.Navigation>()

View file

@ -64,7 +64,7 @@ class RoomSummaryController(private val stringProvider: StringProvider,
val unreadCount = if (summaries.isEmpty()) { val unreadCount = if (summaries.isEmpty()) {
0 0
} else { } else {
summaries.map { it.notificationCount }.reduce { acc, i -> acc + i } summaries.map { it.notificationCount }.sumBy { i -> i }
} }
val showHighlighted = summaries.any { it.highlightCount > 0 } val showHighlighted = summaries.any { it.highlightCount > 0 }
roomCategoryItem { roomCategoryItem {

View file

@ -16,10 +16,8 @@
package im.vector.riotredesign.features.navigation package im.vector.riotredesign.features.navigation
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.fragment.app.Fragment
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.riotredesign.features.debug.DebugMenuActivity import im.vector.riotredesign.features.debug.DebugMenuActivity
import im.vector.riotredesign.features.home.room.detail.RoomDetailActivity import im.vector.riotredesign.features.home.room.detail.RoomDetailActivity

View file

@ -20,6 +20,7 @@ import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.epoxy.EpoxyVisibilityTracker
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
@ -29,9 +30,7 @@ import com.jakewharton.rxbinding2.widget.RxTextView
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.error.ErrorFormatter import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotredesign.features.themes.ThemeUtils import im.vector.riotredesign.features.themes.ThemeUtils
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
import kotlinx.android.synthetic.main.fragment_public_rooms.* import kotlinx.android.synthetic.main.fragment_public_rooms.*
@ -49,6 +48,7 @@ import java.util.concurrent.TimeUnit
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback { class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {
private val viewModel: RoomDirectoryViewModel by activityViewModel() private val viewModel: RoomDirectoryViewModel by activityViewModel()
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
private val publicRoomsController: PublicRoomsController by inject() private val publicRoomsController: PublicRoomsController by inject()
private val errorFormatter: ErrorFormatter by inject() private val errorFormatter: ErrorFormatter by inject()
@ -76,9 +76,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
.disposeOnDestroy() .disposeOnDestroy()
publicRoomsCreateNewRoom.setOnClickListener { publicRoomsCreateNewRoom.setOnClickListener {
// TODO homeActivityViewModel.createRoom() navigationViewModel.goTo(RoomDirectoryActivity.Navigation.CreateRoom)
vectorBaseActivity.notImplemented()
} }
viewModel.joinRoomErrorLiveData.observe(this, Observer { viewModel.joinRoomErrorLiveData.observe(this, Observer {
@ -92,7 +90,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.menu_room_directory_change_protocol -> { R.id.menu_room_directory_change_protocol -> {
vectorBaseActivity.addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer) navigationViewModel.goTo(RoomDirectoryActivity.Navigation.ChangeProtocol)
true true
} }
else -> else ->
@ -104,6 +102,8 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE)) bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
setupRecyclerView() setupRecyclerView()
} }

View file

@ -17,14 +17,29 @@
package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.ViewModelProviders
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragment import im.vector.riotredesign.core.extensions.addFragment
import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.extensions.observeEvent
import im.vector.riotredesign.core.platform.VectorBaseActivity import im.vector.riotredesign.core.platform.VectorBaseActivity
import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import org.koin.android.scope.ext.android.bindScope import org.koin.android.scope.ext.android.bindScope
import org.koin.android.scope.ext.android.getOrCreateScope import org.koin.android.scope.ext.android.getOrCreateScope
class RoomDirectoryActivity : VectorBaseActivity() { class RoomDirectoryActivity : VectorBaseActivity() {
// Supported navigation actions for this Activity
sealed class Navigation {
object Back : Navigation()
object CreateRoom : Navigation()
object Close : Navigation()
object ChangeProtocol : Navigation()
}
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
override fun getLayoutRes() = R.layout.activity_simple override fun getLayoutRes() = R.layout.activity_simple
@ -32,6 +47,17 @@ class RoomDirectoryActivity : VectorBaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE)) bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
navigationViewModel = ViewModelProviders.of(this).get(RoomDirectoryNavigationViewModel::class.java)
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
when (navigation) {
is Navigation.Back -> onBackPressed()
is Navigation.CreateRoom -> addFragmentToBackstack(CreateRoomFragment(), R.id.simpleFragmentContainer)
is Navigation.ChangeProtocol -> addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer)
is Navigation.Close -> finish()
}
}
} }
override fun initUiAndData() { override fun initUiAndData() {

View file

@ -17,11 +17,11 @@
package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.riotredesign.features.roomdirectory.createroom.CreateRoomController
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryListCreator import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryListCreator
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerController import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerController
import org.koin.dsl.module.module import org.koin.dsl.module.module
// TODO Ganfra: When do we create a new module?
class RoomDirectoryModule { class RoomDirectoryModule {
companion object { companion object {
@ -41,5 +41,14 @@ class RoomDirectoryModule {
scope(ROOM_DIRECTORY_SCOPE) { scope(ROOM_DIRECTORY_SCOPE) {
PublicRoomsController(get(), get()) PublicRoomsController(get(), get())
} }
/* ==========================================================================================
* Create room
* ========================================================================================== */
scope(ROOM_DIRECTORY_SCOPE) {
CreateRoomController(get(), get())
}
} }
} }

View file

@ -0,0 +1,21 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory
import im.vector.riotredesign.core.mvrx.NavigationViewModel
class RoomDirectoryNavigationViewModel : NavigationViewModel<RoomDirectoryActivity.Navigation>()

View file

@ -0,0 +1,112 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory.createroom
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.form.formEditTextItem
import im.vector.riotredesign.features.form.formSwitchItem
class CreateRoomController(private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter
) : TypedEpoxyController<CreateRoomViewState>() {
var listener: Listener? = null
var index = 0
override fun buildModels(viewState: CreateRoomViewState) {
val asyncCreateRoom = viewState.asyncCreateRoomRequest
when (asyncCreateRoom) {
is Success -> {
// Nothing to display, the screen will be closed
}
is Loading -> {
// display the form
buildForm(viewState, false)
loadingItem {
id("loading")
}
}
is Uninitialized -> {
// display the form
buildForm(viewState, true)
}
is Fail -> {
// display the form
buildForm(viewState, true)
errorWithRetryItem {
id("error")
text(errorFormatter.toHumanReadable(asyncCreateRoom.error))
listener { listener?.retry() }
}
}
}
}
private fun buildForm(viewState: CreateRoomViewState, enableFormElement: Boolean) {
formEditTextItem {
id("name")
enabled(enableFormElement)
value(viewState.roomName)
hint(stringProvider.getString(R.string.create_room_name_hint))
onTextChange { text ->
listener?.onNameChange(text)
}
}
formSwitchItem {
id("public")
enabled(enableFormElement)
title(stringProvider.getString(R.string.create_room_public_title))
summary(stringProvider.getString(R.string.create_room_public_description))
switchChecked(viewState.isPublic)
listener { value ->
listener?.setIsPublic(value)
}
}
formSwitchItem {
id("directory")
enabled(enableFormElement)
title(stringProvider.getString(R.string.create_room_directory_title))
summary(stringProvider.getString(R.string.create_room_directory_description))
switchChecked(viewState.isInRoomDirectory)
listener { value ->
listener?.setIsInRoomDirectory(value)
}
}
}
interface Listener {
fun onNameChange(newName: String)
fun setIsPublic(isPublic: Boolean)
fun setIsInRoomDirectory(isInRoomDirectory: Boolean)
fun retry()
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory.createroom
import android.os.Bundle
import android.view.MenuItem
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
import kotlinx.android.synthetic.main.fragment_create_room.*
import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope
import org.koin.android.scope.ext.android.getOrCreateScope
import timber.log.Timber
class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
private val viewModel: CreateRoomViewModel by fragmentViewModel()
private val createRoomController: CreateRoomController by inject()
override fun getLayoutResId() = R.layout.fragment_create_room
override fun getMenuRes() = R.menu.vector_room_creation
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
vectorBaseActivity.setSupportActionBar(createRoomToolbar)
navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
setupRecyclerView()
createRoomClose.setOnClickListener {
navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_create_room -> {
viewModel.doCreateRoom()
true
}
else ->
super.onOptionsItemSelected(item)
}
}
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
createRoomForm.layoutManager = layoutManager
createRoomController.listener = this
createRoomForm.setController(createRoomController)
}
override fun onNameChange(newName: String) {
viewModel.setName(newName)
}
override fun setIsPublic(isPublic: Boolean) {
viewModel.setIsPublic(isPublic)
}
override fun setIsInRoomDirectory(isInRoomDirectory: Boolean) {
viewModel.setIsInRoomDirectory(isInRoomDirectory)
}
override fun retry() {
Timber.v("Retry")
viewModel.doCreateRoom()
}
override fun invalidate() = withState(viewModel) { state ->
val async = state.asyncCreateRoomRequest
if (async is Success) {
// Navigate to freshly created room
navigator.openRoom(async(), requireActivity())
navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Close)
} else {
// Populate list with Epoxy
createRoomController.setData(state)
}
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory.createroom
import com.airbnb.mvrx.*
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
import im.vector.riotredesign.core.platform.VectorViewModel
import org.koin.android.ext.android.get
class CreateRoomViewModel(initialState: CreateRoomViewState,
private val session: Session) : VectorViewModel<CreateRoomViewState>(initialState) {
companion object : MvRxViewModelFactory<CreateRoomViewModel, CreateRoomViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? {
val currentSession = viewModelContext.activity.get<Session>()
return CreateRoomViewModel(state, currentSession)
}
}
fun setName(newName: String) = setState { copy(roomName = newName) }
fun setIsPublic(isPublic: Boolean) = setState { copy(isPublic = isPublic) }
fun setIsInRoomDirectory(isInRoomDirectory: Boolean) = setState { copy(isInRoomDirectory = isInRoomDirectory) }
fun doCreateRoom() = withState { state ->
if (state.asyncCreateRoomRequest is Loading || state.asyncCreateRoomRequest is Success) {
return@withState
}
setState {
copy(asyncCreateRoomRequest = Loading())
}
val createRoomParams = CreateRoomParams().apply {
name = state.roomName.takeIf { it.isNotBlank() }
// Directory visibility
visibility = if (state.isInRoomDirectory) RoomDirectoryVisibility.PUBLIC else RoomDirectoryVisibility.PRIVATE
// Public room
preset = if (state.isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT
}
session.createRoom(createRoomParams, object : MatrixCallback<String> {
override fun onSuccess(data: String) {
setState {
copy(asyncCreateRoomRequest = Success(data))
}
}
override fun onFailure(failure: Throwable) {
setState {
copy(asyncCreateRoomRequest = Fail(failure))
}
}
})
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.roomdirectory.createroom
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
data class CreateRoomViewState(
val roomName: String = "",
val isPublic: Boolean = false,
val isInRoomDirectory: Boolean = false,
val asyncCreateRoomRequest: Async<String> = Uninitialized
) : MvRxState

View file

@ -19,6 +19,7 @@ package im.vector.riotredesign.features.roomdirectory.picker
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
@ -26,7 +27,9 @@ import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
import kotlinx.android.synthetic.main.fragment_room_directory_picker.* import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -39,6 +42,7 @@ import timber.log.Timber
class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback { class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback {
private val viewModel: RoomDirectoryViewModel by activityViewModel() private val viewModel: RoomDirectoryViewModel by activityViewModel()
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel() private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()
private val roomDirectoryPickerController: RoomDirectoryPickerController by inject() private val roomDirectoryPickerController: RoomDirectoryPickerController by inject()
@ -71,6 +75,8 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE)) bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
navigationViewModel = ViewModelProviders.of(requireActivity()).get(RoomDirectoryNavigationViewModel::class.java)
setupRecyclerView() setupRecyclerView()
} }
@ -88,8 +94,7 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
Timber.v("onRoomDirectoryClicked: $roomDirectoryData") Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData) viewModel.setRoomDirectoryData(roomDirectoryData)
// TODO Not the best way to manage Fragment Backstack... navigationViewModel.goTo(RoomDirectoryActivity.Navigation.Back)
vectorBaseActivity.onBackPressed()
} }
override fun retry() { override fun retry() {

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M16,2L2,16M2,2l14,14"
android:strokeLineJoin="round"
android:strokeWidth="2.333"
android:fillColor="#00000000"
android:fillType="evenOdd"
android:strokeColor="#03B381"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,71 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/createRoomToolbar"
android:layout_width="0dp"
android:layout_height="?actionBarSize"
android:elevation="4dp"
app:contentInsetStartWithNavigation="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/createRoomClose"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:scaleType="center"
android:src="@drawable/ic_x_18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/createRoomTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/create_room_title"
android:textColor="?riotx_text_primary"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/createRoomClose"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/createRoomForm"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/createRoomToolbar"
tools:listitem="@layout/item_form_switch" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -5,22 +5,33 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/publicRoomsCoordinator" android:id="@+id/publicRoomsCoordinator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="?riotx_header_panel_background">
<androidx.constraintlayout.widget.ConstraintLayout <com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/publicRoomsList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="?riotx_header_panel_background"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/item_public_room" />
<com.google.android.material.appbar.AppBarLayout
style="@style/VectorAppBarLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/publicRoomsToolbar" android:id="@+id/publicRoomsToolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="?attr/actionBarSize"
android:elevation="4dp" android:elevation="4dp"
android:minHeight="0dp"
app:contentInsetStartWithNavigation="0dp" app:contentInsetStartWithNavigation="0dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways">
<!-- Note: Background is modified in the code for other themes --> <!-- Note: Background is modified in the code for other themes -->
<EditText <EditText
@ -52,6 +63,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:minHeight="@dimen/layout_touch_size" android:minHeight="@dimen/layout_touch_size"
android:text="@string/create_new_room" android:text="@string/create_new_room"
@ -61,16 +73,6 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/publicRoomsToolbar" /> app:layout_constraintTop_toBottomOf="@+id/publicRoomsToolbar" />
<com.airbnb.epoxy.EpoxyRecyclerView </com.google.android.material.appbar.AppBarLayout>
android:id="@+id/publicRoomsList"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/publicRoomsCreateNewRoom"
tools:listitem="@layout/item_public_room" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -5,6 +5,7 @@
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?riotx_background"
android:padding="16dp"> android:padding="16dp">
<TextView <TextView

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:minHeight="@dimen/item_form_min_height">
<TextView
android:id="@+id/formSwitchTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginLeft="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginRight="@dimen/layout_horizontal_margin"
android:duplicateParentState="true"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_primary"
android:textSize="15sp"
app:layout_constraintBottom_toTopOf="@+id/formSwitchSummary"
app:layout_constraintEnd_toStartOf="@+id/formSwitchSwitch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@string/create_room_public_title" />
<TextView
android:id="@+id/formSwitchSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?riotx_text_secondary"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/formSwitchDivider"
app:layout_constraintStart_toStartOf="@+id/formSwitchTitle"
app:layout_constraintTop_toBottomOf="@+id/formSwitchTitle"
tools:text="@string/create_room_public_description" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/formSwitchSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="19dp"
android:layout_marginRight="19dp"
app:layout_constraintBottom_toTopOf="@+id/formSwitchDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/formSwitchDivider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?riotx_header_panel_border_mobile"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:minHeight="@dimen/item_form_min_height">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/formTextInputTextInputLayout"
style="@style/VectorTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
app:layout_constraintBottom_toTopOf="@+id/formTextInputDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/formTextInputTextInputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:hint="@string/create_room_name_hint" />
</com.google.android.material.textfield.TextInputLayout>
<View
android:id="@+id/formTextInputDivider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?riotx_header_panel_border_mobile"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -3,6 +3,7 @@
android:id="@+id/itemNoResultText" android:id="@+id/itemNoResultText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?riotx_background"
android:gravity="center" android:gravity="center"
android:padding="16dp" android:padding="16dp"
android:text="@string/no_result_placeholder" /> android:text="@string/no_result_placeholder" />

View file

@ -9,6 +9,7 @@
android:background="?riotx_background" android:background="?riotx_background"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="97dp"> android:minHeight="97dp">
<ImageView <ImageView

View file

@ -7,7 +7,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?riotx_background" android:background="?riotx_background"
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true"
android:foreground="?attr/selectableItemBackground">
<ImageView <ImageView
android:id="@+id/roomAvatarImageView" android:id="@+id/roomAvatarImageView"

View file

@ -13,6 +13,7 @@
android:paddingLeft="@dimen/layout_horizontal_margin" android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="@dimen/layout_vertical_margin" android:paddingTop="@dimen/layout_vertical_margin"
android:paddingEnd="@dimen/layout_horizontal_margin" android:paddingEnd="@dimen/layout_horizontal_margin"
android:foreground="?attr/selectableItemBackground"
android:paddingRight="@dimen/layout_horizontal_margin" android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp"> android:paddingBottom="8dp">

View file

@ -6,6 +6,7 @@
android:id="@+id/itemRoomDirectoryLayout" android:id="@+id/itemRoomDirectoryLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?riotx_background"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground"> android:foreground="?attr/selectableItemBackground">

View file

@ -2,13 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.matrix.vector.activity.VectorRoomCreationActivity"> tools:context=".features.roomdirectory.RoomDirectoryActivity">
<item <item
android:id="@+id/action_create_room" android:id="@+id/action_create_room"
android:icon="@drawable/ic_material_done_white" android:title="@string/create_room_action_create"
android:orderInCategory="0"
android:title="@string/room_participants_create"
app:showAsAction="always" /> app:showAsAction="always" />
</menu> </menu>

View file

@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme.Status" parent="AppTheme.Base.Status"> <style name="AppTheme.Status.v21" parent="AppTheme.Base.Status">
<item name="android:statusBarColor">@color/riotx_header_panel_background_light</item>
<item name="android:navigationBarColor">@color/riotx_header_panel_background_light</item>
<!-- enable window content transitions --> <!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item> <item name="android:windowContentTransitions">true</item>
</style> </style>
<style name="AppTheme.Status" parent="AppTheme.Status.v21" />
</resources> </resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Status.v23" parent="AppTheme.Status.v21">
<item name="android:windowLightStatusBar">false</item>
</style>
<style name="AppTheme.Status" parent="AppTheme.Status.v23"/>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Status.v27" parent="AppTheme.Status.v23">
<item name="android:windowLightNavigationBar">true</item>
</style>
<style name="AppTheme.Status" parent="AppTheme.Status.v27" />
</resources>

View file

@ -27,4 +27,7 @@
<dimen name="pill_min_height">20dp</dimen> <dimen name="pill_min_height">20dp</dimen>
<dimen name="pill_text_padding">4dp</dimen> <dimen name="pill_text_padding">4dp</dimen>
<dimen name="item_form_min_height">76dp</dimen>
</resources> </resources>

View file

@ -42,4 +42,13 @@
<string name="fab_menu_create_room">"Rooms"</string> <string name="fab_menu_create_room">"Rooms"</string>
<string name="fab_menu_create_chat">"Direct Messages"</string> <string name="fab_menu_create_chat">"Direct Messages"</string>
<!-- Create room screen -->
<string name="create_room_title">"New Room"</string>
<string name="create_room_action_create">"CREATE"</string>
<string name="create_room_name_hint">"Room name"</string>
<string name="create_room_public_title">"Public"</string>
<string name="create_room_public_description">"Anyone will be able to join this room"</string>
<string name="create_room_directory_title">"Room Directory"</string>
<string name="create_room_directory_description">"Publish this room in the room directory"</string>
</resources> </resources>