Compare commits
61 Commits
develop
...
experiment
Author | SHA1 | Date |
---|---|---|
NIkita Fedrunov | df25dfc284 | |
NIkita Fedrunov | 2ec468ba27 | |
NIkita Fedrunov | 484afac10b | |
NIkita Fedrunov | d70d110454 | |
NIkita Fedrunov | 7437b71b24 | |
ericdecanini | 6da990d4a5 | |
ericdecanini | 2d32d0da54 | |
ericdecanini | f54da187ca | |
ericdecanini | a961ed7de7 | |
ericdecanini | df0415748f | |
ericdecanini | 43d23e195f | |
ericdecanini | 626c6933a3 | |
ericdecanini | 60adecedd6 | |
ericdecanini | 7f41aa313e | |
ericdecanini | c7efb2cec9 | |
ericdecanini | 10ceb56321 | |
ericdecanini | 3a156cf8c8 | |
ericdecanini | 9618c76dbd | |
ericdecanini | 456b6881d7 | |
ericdecanini | 94e6e1b0d8 | |
ericdecanini | a6564b6466 | |
ericdecanini | c72ba8d651 | |
ericdecanini | a158d9866d | |
ericdecanini | 89db0645a5 | |
ericdecanini | f5851a0bf8 | |
ericdecanini | 39e892dc08 | |
ericdecanini | 0faeada1c6 | |
ericdecanini | f3720d2dce | |
ericdecanini | 1bca6f83ee | |
ericdecanini | 4fe455c47b | |
ericdecanini | 65d5473661 | |
ericdecanini | e533b9f33d | |
ericdecanini | 947cd8fceb | |
ericdecanini | a0baf77438 | |
ericdecanini | fee96b45bc | |
ericdecanini | 20a72e640b | |
ericdecanini | 952e3102d8 | |
ericdecanini | 19fcb6faf1 | |
ericdecanini | 89f69a1062 | |
ericdecanini | 4ee5b90f82 | |
ericdecanini | 051adad0ed | |
ericdecanini | 2d2f8f5479 | |
ericdecanini | 9fc189efce | |
ericdecanini | b6ab0bdc53 | |
ericdecanini | 15c89f918e | |
ericdecanini | b46794d4df | |
ericdecanini | c9b32fec44 | |
ericdecanini | 47493fcfa1 | |
ericdecanini | f70a24d257 | |
ericdecanini | a355b625e9 | |
ericdecanini | 7cc79fef0f | |
ericdecanini | 7e415e82b0 | |
ericdecanini | 962e9abc6b | |
ericdecanini | 4784717b0c | |
ericdecanini | b280358077 | |
ericdecanini | 33475602f8 | |
ericdecanini | 87ad35dca6 | |
ericdecanini | 70cded2733 | |
ericdecanini | 9e53e6cc8f | |
ericdecanini | a3367d4075 | |
ericdecanini | 0250f61d10 |
1802
.editorconfig
1802
.editorconfig
File diff suppressed because it is too large
Load Diff
|
@ -104,7 +104,7 @@ allprojects {
|
|||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||
// Warnings are potential errors, so stop ignoring them
|
||||
// You can override by passing `-PallWarningsAsErrors=false` in the command line
|
||||
kotlinOptions.allWarningsAsErrors = project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean()
|
||||
// kotlinOptions.allWarningsAsErrors = project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean()
|
||||
}
|
||||
|
||||
// Fix "Java heap space" issue
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Adds space or user id as a subtitle under rooms in search
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<bool name="isDarkMode">true</bool>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<color name="modal_background_color">@color/element_system_dark</color>
|
||||
|
||||
</resources>
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
<!-- Created to detect what has to be implemented (especially in the settings) -->
|
||||
<bool name="false_not_implemented">false</bool>
|
||||
<bool name="isDarkMode">false</bool>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
<color name="android_status_bar_background_dark">@color/element_background_dark</color>
|
||||
<color name="android_navigation_bar_background_dark">@color/element_system_dark</color>
|
||||
|
||||
<color name="modal_background_color">#FFFFFF</color>
|
||||
|
||||
<!-- Used for toolbar background -->
|
||||
<attr name="vctr_toolbar_background" format="color" />
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="SpaceListModalImageShapeOverlay">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">8dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -17,6 +17,10 @@
|
|||
<item name="android:textAppearance">@style/TextAppearance.Vector.Title.Medium</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Vector.TextView.Title.Bold">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Vector.Title.Bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Vector.TextView.HeadlineMedium">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Vector.Headline.Medium</item>
|
||||
</style>
|
||||
|
@ -48,4 +52,4 @@
|
|||
<item name="lineHeight">16sp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Vector.Title.Bold">
|
||||
<item name="fontFamily">sans-serif-black</item>
|
||||
<item name="android:fontFamily">sans-serif-black</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Vector.Headline.Medium" parent="TextAppearance.MaterialComponents.Headline1">
|
||||
<item name="fontFamily">sans-serif-medium</item>
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
|
|
|
@ -43,7 +43,6 @@ internal class RoomChildRelationInfo(
|
|||
data class SpaceChildInfo(
|
||||
val roomId: String,
|
||||
val order: String?,
|
||||
// val autoJoin: Boolean,
|
||||
val viaServers: List<String>
|
||||
)
|
||||
|
||||
|
@ -60,18 +59,13 @@ internal class RoomChildRelationInfo(
|
|||
fun getDirectChildrenDescriptions(): List<SpaceChildInfo> {
|
||||
return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_CHILD)
|
||||
.findAll()
|
||||
// .also {
|
||||
// Timber.v("## Space: Found ${it.count()} m.space.child state events for $roomId")
|
||||
// }
|
||||
.mapNotNull {
|
||||
ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.let { scc ->
|
||||
// Timber.v("## Space child desc state event $scc")
|
||||
// Children where via is not present are ignored.
|
||||
scc.via?.let { via ->
|
||||
SpaceChildInfo(
|
||||
roomId = it.stateKey,
|
||||
order = scc.validOrder(),
|
||||
// autoJoin = scc.autoJoin ?: false,
|
||||
viaServers = via
|
||||
)
|
||||
}
|
||||
|
@ -83,17 +77,13 @@ internal class RoomChildRelationInfo(
|
|||
fun getParentDescriptions(): List<SpaceParentInfo> {
|
||||
return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_PARENT)
|
||||
.findAll()
|
||||
// .also {
|
||||
// Timber.v("## Space: Found ${it.count()} m.space.parent state events for $roomId")
|
||||
// }
|
||||
.mapNotNull {
|
||||
ContentMapper.map(it.root?.content).toModel<SpaceParentContent>()?.let { scc ->
|
||||
// Timber.v("## Space parent desc state event $scc")
|
||||
ContentMapper.map(it.root?.content).toModel<SpaceParentContent>()?.let { spaceParentContent ->
|
||||
// Parent where via is not present are ignored.
|
||||
scc.via?.let { via ->
|
||||
spaceParentContent.via?.let { via ->
|
||||
SpaceParentInfo(
|
||||
roomId = it.stateKey,
|
||||
canonical = scc.canonical ?: false,
|
||||
canonical = spaceParentContent.canonical ?: false,
|
||||
viaServers = via,
|
||||
stateEventSender = it.root?.sender ?: ""
|
||||
)
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
|
|||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
|
@ -193,6 +194,18 @@ internal class RoomSummaryDataSource @Inject constructor(
|
|||
}
|
||||
val dataSourceFactory = realmDataSourceFactory.map {
|
||||
roomSummaryMapper.map(it)
|
||||
}.map { roomSummary ->
|
||||
val parents = roomSummary.flattenParentIds.mapNotNull { parentId ->
|
||||
getRoomSummary(parentId)?.let { parentSummary ->
|
||||
SpaceParentInfo(
|
||||
parentId = parentSummary.flattenParentIds.firstOrNull(),
|
||||
roomSummary = parentSummary,
|
||||
canonical = true,
|
||||
viaServers = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
roomSummary.copy(spaceParents = parents)
|
||||
}
|
||||
|
||||
val boundaries = MutableLiveData(ResultBoundaries())
|
||||
|
|
|
@ -18,7 +18,7 @@ ext.versionMinor = 4
|
|||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||
// When creating a hotfix, you should decrease the value, since the current value
|
||||
// is the value for the next regular release.
|
||||
ext.versionPatch = 16
|
||||
ext.versionPatch = 17
|
||||
|
||||
static def getGitTimestamp() {
|
||||
def cmd = 'git show -s --format=%ct'
|
||||
|
@ -262,7 +262,7 @@ android {
|
|||
|
||||
dimension "store"
|
||||
isDefault = true
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}${getGplayVersionSuffix()}"
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}-experiment-spaceSwitching"
|
||||
|
||||
resValue "bool", "isGplay", "true"
|
||||
buildConfigField "boolean", "ALLOW_FCM_USE", "true"
|
||||
|
@ -273,7 +273,7 @@ android {
|
|||
fdroid {
|
||||
dimension "store"
|
||||
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}"
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}-experiment-spaceSwitching"
|
||||
|
||||
resValue "bool", "isGplay", "false"
|
||||
buildConfigField "boolean", "ALLOW_FCM_USE", "false"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app
|
||||
|
||||
class SessionVariables {
|
||||
|
||||
companion object {
|
||||
|
||||
var optionsMenuShouldShow = true
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ import androidx.viewbinding.ViewBinding
|
|||
import com.airbnb.mvrx.MavericksView
|
||||
import com.bumptech.glide.util.Util
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
|
@ -238,6 +239,14 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
|
|||
|
||||
initUiAndData()
|
||||
|
||||
try {
|
||||
val toolbarBackground = MaterialColors.getColor(views.root, R.attr.vctr_toolbar_background)
|
||||
window.statusBarColor = toolbarBackground
|
||||
window.navigationBarColor = toolbarBackground
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
|
||||
val titleRes = getTitleRes()
|
||||
if (titleRes != -1) {
|
||||
supportActionBar?.let {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.analytics.experiment
|
||||
|
||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||
|
||||
data class ExperimentInteraction(
|
||||
/**
|
||||
* The unique name of this element.
|
||||
*/
|
||||
val name: Name,
|
||||
|
||||
val extra: Map<String, Any> = emptyMap()
|
||||
) : VectorAnalyticsEvent {
|
||||
|
||||
enum class Name {
|
||||
SpaceSwitchHeader,
|
||||
SpaceSwitchHeaderAdd,
|
||||
SpaceSwitchHeaderCreate,
|
||||
SpacePanelSwitchSpace
|
||||
}
|
||||
|
||||
override fun getName() = "Interaction"
|
||||
|
||||
override fun getProperties(): Map<String, Any>? {
|
||||
return mutableMapOf<String, Any>().apply {
|
||||
put("name", name.name)
|
||||
putAll(extra)
|
||||
}.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.home
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
|
@ -73,6 +74,15 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
|
|||
)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun render(matrixItem: MatrixItem, context: Context, imageView: ImageView) {
|
||||
render(
|
||||
GlideApp.with(context),
|
||||
matrixItem,
|
||||
DrawableImageViewTarget(imageView)
|
||||
)
|
||||
}
|
||||
|
||||
// fun renderSpace(matrixItem: MatrixItem, imageView: ImageView) {
|
||||
// renderSpace(
|
||||
// matrixItem,
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.AppStateHandler
|
||||
import im.vector.app.R
|
||||
import im.vector.app.SessionVariables
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.extensions.hideKeyboard
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
|
@ -190,7 +191,7 @@ class HomeActivity :
|
|||
views.drawerLayout.addDrawerListener(drawerListener)
|
||||
if (isFirstCreation()) {
|
||||
replaceFragment(views.homeDetailFragmentContainer, HomeDetailFragment::class.java)
|
||||
replaceFragment(views.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
|
||||
// replaceFragment(views.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
|
||||
}
|
||||
|
||||
sharedActionViewModel
|
||||
|
@ -243,7 +244,7 @@ class HomeActivity :
|
|||
}
|
||||
|
||||
private fun openGroup(shouldClearFragment: Boolean) {
|
||||
views.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
// views.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
|
||||
// When switching from space to group or group to space, we need to reload the fragment
|
||||
if (shouldClearFragment) {
|
||||
|
@ -270,7 +271,7 @@ class HomeActivity :
|
|||
}
|
||||
|
||||
private fun closeGroup() {
|
||||
views.drawerLayout.openDrawer(GravityCompat.START)
|
||||
// views.drawerLayout.openDrawer(GravityCompat.START)
|
||||
}
|
||||
|
||||
private fun handleShowAnalyticsOptIn() {
|
||||
|
@ -520,11 +521,15 @@ class HomeActivity :
|
|||
override fun getMenuRes() = R.menu.home
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
menu.findItem(R.id.menu_home_init_sync_legacy).isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_optimized).isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_legacy)?.isVisible = vectorPreferences.developerMode()
|
||||
menu.findItem(R.id.menu_home_init_sync_optimized)?.isVisible = vectorPreferences.developerMode()
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
return if (SessionVariables.optionsMenuShouldShow) super.onCreateOptionsMenu(menu) else false
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.menu_home_suggestion -> {
|
||||
|
|
|
@ -19,11 +19,14 @@ package im.vector.app.features.home
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
|
@ -31,6 +34,7 @@ import com.google.android.material.badge.BadgeDrawable
|
|||
import im.vector.app.AppStateHandler
|
||||
import im.vector.app.R
|
||||
import im.vector.app.RoomGroupingMethod
|
||||
import im.vector.app.SessionVariables
|
||||
import im.vector.app.core.extensions.commitTransaction
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
|
@ -41,6 +45,7 @@ import im.vector.app.core.ui.views.CurrentCallsView
|
|||
import im.vector.app.core.ui.views.CurrentCallsViewPresenter
|
||||
import im.vector.app.core.ui.views.KeysBackupBanner
|
||||
import im.vector.app.databinding.FragmentHomeDetailBinding
|
||||
import im.vector.app.features.analytics.experiment.ExperimentInteraction
|
||||
import im.vector.app.features.call.SharedKnownCallsViewModel
|
||||
import im.vector.app.features.call.VectorCallActivity
|
||||
import im.vector.app.features.call.dialpad.DialPadFragment
|
||||
|
@ -53,12 +58,18 @@ import im.vector.app.features.popup.VerificationVectorAlert
|
|||
import im.vector.app.features.settings.VectorLocale
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS
|
||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||
import im.vector.app.features.spaces.manage.ManageType
|
||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import im.vector.app.features.workers.signout.BannerState
|
||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeDetailFragment @Inject constructor(
|
||||
|
@ -81,6 +92,8 @@ class HomeDetailFragment @Inject constructor(
|
|||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||
private lateinit var sharedCallActionViewModel: SharedKnownCallsViewModel
|
||||
|
||||
private var isInSpace = false
|
||||
|
||||
private var hasUnreadRooms = false
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
|
@ -97,16 +110,39 @@ class HomeDetailFragment @Inject constructor(
|
|||
viewModel.handle(HomeDetailAction.MarkAllRoomsRead)
|
||||
return true
|
||||
}
|
||||
R.id.menu_explore_rooms -> {
|
||||
sharedActionViewModel.space.value?.let { startActivity(SpaceExploreActivity.newIntent(requireContext(), it.roomId)) }
|
||||
return true
|
||||
}
|
||||
R.id.menu_invite_people -> {
|
||||
activity?.let { activity ->
|
||||
sharedActionViewModel.space.value?.let { ShareSpaceBottomSheet.show(activity.supportFragmentManager, it.roomId) }
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.menu_add_rooms -> {
|
||||
sharedActionViewModel.space.value?.let { startActivity(SpaceManageActivity.newIntent(requireActivity(), it.roomId, ManageType.AddRooms)) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
if (SessionVariables.optionsMenuShouldShow) super.onCreateOptionsMenu(menu, inflater) else Unit
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
withState(viewModel) { state ->
|
||||
val isRoomList = state.currentTab is HomeTab.RoomList
|
||||
menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = isRoomList && hasUnreadRooms
|
||||
menu.findItem(R.id.menu_home_mark_all_as_read)?.isVisible = isRoomList && hasUnreadRooms
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_explore_rooms)?.isVisible = isInSpace
|
||||
menu.findItem(R.id.menu_invite_people)?.isVisible = isInSpace
|
||||
menu.findItem(R.id.menu_add_rooms)?.isVisible = isInSpace
|
||||
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
|
@ -132,8 +168,13 @@ class HomeDetailFragment @Inject constructor(
|
|||
|
||||
viewModel.onEach(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod ->
|
||||
when (roomGroupingMethod) {
|
||||
is RoomGroupingMethod.ByLegacyGroup -> onGroupChange(roomGroupingMethod.groupSummary)
|
||||
is RoomGroupingMethod.BySpace -> onSpaceChange(roomGroupingMethod.spaceSummary)
|
||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
||||
onGroupChange(roomGroupingMethod.groupSummary)
|
||||
}
|
||||
is RoomGroupingMethod.BySpace -> {
|
||||
onSpaceChange(roomGroupingMethod.spaceSummary)
|
||||
sharedActionViewModel.space.value = roomGroupingMethod.spaceSummary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,12 +182,40 @@ class HomeDetailFragment @Inject constructor(
|
|||
updateUIForTab(currentTab)
|
||||
}
|
||||
|
||||
viewModel.onEach(HomeDetailViewState::showDialPadTab) { showDialPadTab ->
|
||||
updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab)
|
||||
views.allChatsLayout.setOnClickListener {
|
||||
toggleModalVisibility()
|
||||
}
|
||||
|
||||
views.groupToolbarNavigateUp.setOnClickListener {
|
||||
views.backButtonLayout.setOnClickListener {
|
||||
navigateUpOneSpace()
|
||||
// val currentSpace = sharedActionViewModel.space.value
|
||||
// val directParent = currentSpace?.spaceParents?.firstOrNull()
|
||||
// viewModel.handleSelectSpace(directParent?.roomSummary)
|
||||
// sharedActionViewModel.space.value = directParent?.roomSummary
|
||||
// sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(false))
|
||||
// onSpaceChange(directParent?.roomSummary)
|
||||
}
|
||||
|
||||
views.dimView.setOnClickListener {
|
||||
hideModal()
|
||||
views.toolbarChevron.animate()
|
||||
.rotation(0F)
|
||||
.setDuration(300)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
views.dimViewBottom.setOnClickListener {
|
||||
hideModal()
|
||||
views.toolbarChevron.animate()
|
||||
.rotation(0F)
|
||||
.setDuration(300)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
viewModel.onEach(HomeDetailViewState::showDialPadTab) { showDialPadTab ->
|
||||
updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab)
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents { viewEvent ->
|
||||
|
@ -189,6 +258,57 @@ class HomeDetailFragment @Inject constructor(
|
|||
currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
observeSharedActions()
|
||||
}
|
||||
|
||||
private fun observeSharedActions() = lifecycleScope.launch {
|
||||
sharedActionViewModel.stream().collect { action ->
|
||||
when (action) {
|
||||
is HomeActivitySharedAction.OpenGroup -> hideModal()
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleModalVisibility() {
|
||||
if (views.spaceModalFragment.isVisible) {
|
||||
hideModal()
|
||||
views.toolbarChevron.animate()
|
||||
.rotation(0F)
|
||||
.setDuration(300)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
} else {
|
||||
val extraProperties = if (getCurrentSpace() != null) {
|
||||
mapOf("isSubspace" to true)
|
||||
} else {
|
||||
mapOf("isSubspace" to false)
|
||||
}
|
||||
analyticsTracker.capture(ExperimentInteraction(ExperimentInteraction.Name.SpaceSwitchHeader, extraProperties))
|
||||
showModal()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showModal() {
|
||||
SessionVariables.optionsMenuShouldShow = false
|
||||
invalidateOptionsMenu()
|
||||
views.spaceModalFragment.isVisible = true
|
||||
views.dimView.isVisible = true
|
||||
views.dimViewBottom.isVisible = true
|
||||
views.toolbarChevron.animate()
|
||||
.rotation(90F)
|
||||
.setDuration(300)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
private fun hideModal() {
|
||||
SessionVariables.optionsMenuShouldShow = true
|
||||
invalidateOptionsMenu()
|
||||
views.spaceModalFragment.isVisible = false
|
||||
views.dimView.isVisible = false
|
||||
views.dimViewBottom.isVisible = false
|
||||
}
|
||||
|
||||
private fun navigateUpOneSpace() {
|
||||
|
@ -283,24 +403,45 @@ class HomeDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onGroupChange(groupSummary: GroupSummary?) {
|
||||
hideModal()
|
||||
if (groupSummary == null) {
|
||||
views.backButtonLayout.isVisible = false
|
||||
views.groupToolbarSpaceTitleView.isVisible = false
|
||||
views.groupToolbarSpaceTitleView.text = getString(R.string.all_chats)
|
||||
views.groupToolbarTitleView.text = getString(R.string.all_chats)
|
||||
} else {
|
||||
views.backButtonLayout.isVisible = true
|
||||
views.groupToolbarSpaceTitleView.isVisible = true
|
||||
views.groupToolbarSpaceTitleView.text = groupSummary.displayName
|
||||
views.groupToolbarTitleView.text = groupSummary.displayName
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSpaceChange(spaceSummary: RoomSummary?) {
|
||||
hideModal()
|
||||
views.backButtonText.text = getString(R.string.all_chats)
|
||||
views.toolbarChevron.rotation = 0F
|
||||
if (spaceSummary == null) {
|
||||
isInSpace = false
|
||||
invalidateOptionsMenu()
|
||||
views.backButtonLayout.isVisible = false
|
||||
views.groupToolbarSpaceTitleView.isVisible = false
|
||||
views.groupToolbarAvatarImageView.isVisible = true
|
||||
views.groupToolbarNavigateUp.isVisible = false
|
||||
views.groupToolbarSpaceTitleView.text = getString(R.string.all_chats)
|
||||
views.groupToolbarTitleView.text = getString(R.string.all_chats)
|
||||
views.spaceAvatar.isVisible = false
|
||||
} else {
|
||||
isInSpace = true
|
||||
invalidateOptionsMenu()
|
||||
views.backButtonLayout.isVisible = true
|
||||
views.groupToolbarSpaceTitleView.isVisible = true
|
||||
views.groupToolbarSpaceTitleView.text = spaceSummary.displayName
|
||||
views.groupToolbarAvatarImageView.isVisible = false
|
||||
views.groupToolbarNavigateUp.isVisible = true
|
||||
views.groupToolbarTitleView.text = spaceSummary.displayName
|
||||
views.spaceAvatar.isVisible = true
|
||||
avatarRenderer.render(spaceSummary.toMatrixItem(), requireContext(), views.spaceAvatar)
|
||||
|
||||
spaceSummary.spaceParents?.firstOrNull()?.let { directParent ->
|
||||
views.backButtonText.text = directParent.roomSummary?.name ?: getString(R.string.all_chats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,20 +470,20 @@ class HomeDetailFragment @Inject constructor(
|
|||
sharedActionViewModel.post(HomeActivitySharedAction.OpenDrawer)
|
||||
}
|
||||
|
||||
views.homeToolbarContent.debouncedClicks {
|
||||
withState(viewModel) {
|
||||
when (it.roomGroupingMethod) {
|
||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
||||
// do nothing
|
||||
}
|
||||
is RoomGroupingMethod.BySpace -> {
|
||||
it.roomGroupingMethod.spaceSummary?.let { spaceSummary ->
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(spaceSummary.roomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// views.homeToolbarContent.debouncedClicks {
|
||||
// withState(viewModel) {
|
||||
// when (it.roomGroupingMethod) {
|
||||
// is RoomGroupingMethod.ByLegacyGroup -> {
|
||||
// // do nothing
|
||||
// }
|
||||
// is RoomGroupingMethod.BySpace -> {
|
||||
// it.roomGroupingMethod.spaceSummary?.let { spaceSummary ->
|
||||
// sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(spaceSummary.roomId))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun setupBottomNavigationView() {
|
||||
|
@ -361,7 +502,6 @@ class HomeDetailFragment @Inject constructor(
|
|||
|
||||
private fun updateUIForTab(tab: HomeTab) {
|
||||
views.bottomNavigationView.menu.findItem(tab.toMenuId()).isChecked = true
|
||||
views.groupToolbarTitleView.setText(tab.titleRes)
|
||||
updateSelectedFragment(tab)
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
@ -375,7 +515,9 @@ class HomeDetailFragment @Inject constructor(
|
|||
childFragmentManager.fragments
|
||||
.filter { it != fragmentToShow }
|
||||
.forEach {
|
||||
detach(it)
|
||||
if (it.id != R.id.space_modal_fragment) {
|
||||
detach(it)
|
||||
}
|
||||
}
|
||||
if (fragmentToShow == null) {
|
||||
when (tab) {
|
||||
|
|
|
@ -29,6 +29,10 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
|
|||
import im.vector.app.core.extensions.singletonEntryPoint
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.VectorOverrides
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsViewRoom
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import im.vector.app.features.analytics.plan.ViewRoom
|
||||
import im.vector.app.features.call.dialpad.DialPadLookup
|
||||
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
|
@ -36,7 +40,10 @@ import im.vector.app.features.createdirect.DirectRoomHelper
|
|||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.invite.showInvites
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import im.vector.app.features.spaces.SpaceListAction
|
||||
import im.vector.app.features.spaces.SpaceListViewEvents
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import im.vector.app.space
|
||||
import im.vector.lib.core.utils.flow.throttleFirst
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
@ -52,6 +59,7 @@ import org.matrix.android.sdk.api.session.crypto.NewSessionListener
|
|||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.flow.flow
|
||||
|
@ -70,7 +78,8 @@ class HomeDetailViewModel @AssistedInject constructor(
|
|||
private val directRoomHelper: DirectRoomHelper,
|
||||
private val appStateHandler: AppStateHandler,
|
||||
private val autoAcceptInvites: AutoAcceptInvites,
|
||||
private val vectorOverrides: VectorOverrides
|
||||
private val vectorOverrides: VectorOverrides,
|
||||
private val analyticsTracker: AnalyticsTracker
|
||||
) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
|
||||
CallProtocolsChecker.Listener {
|
||||
|
||||
|
@ -215,8 +224,26 @@ class HomeDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun handleSelectSpace(space: RoomSummary?) {
|
||||
appStateHandler.setCurrentSpace(space?.roomId)
|
||||
}
|
||||
|
||||
private fun trackSpaceSwitch(){
|
||||
when (val groupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) {
|
||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
||||
// TODO!!
|
||||
}
|
||||
is RoomGroupingMethod.BySpace -> {
|
||||
groupingMethod.spaceSummary.toAnalyticsViewRoom(null, groupingMethod).let {
|
||||
analyticsTracker.capture(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeRoomSummaries() {
|
||||
appStateHandler.selectedRoomGroupingFlow.distinctUntilChanged().flatMapLatest {
|
||||
trackSpaceSwitch()
|
||||
// we use it as a trigger to all changes in room, but do not really load
|
||||
// the actual models
|
||||
session.roomService().getPagedRoomSummariesLive(
|
||||
|
|
|
@ -16,8 +16,13 @@
|
|||
|
||||
package im.vector.app.features.home
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import im.vector.app.core.platform.VectorSharedActionViewModel
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeSharedActionViewModel @Inject constructor(val session: Session) : VectorSharedActionViewModel<HomeActivitySharedAction>()
|
||||
class HomeSharedActionViewModel @Inject constructor(val session: Session) : VectorSharedActionViewModel<HomeActivitySharedAction>() {
|
||||
|
||||
val space = MutableLiveData<RoomSummary?>()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.databinding.ListItemInviteBinding
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
||||
class InvitesAdapter(
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val inviteUserTask: ((String) -> String?)?,
|
||||
private val onInviteClicked: ((RoomSummary) -> Unit)?,
|
||||
) : RecyclerView.Adapter<InvitesAdapter.ViewHolder>() {
|
||||
|
||||
private val invites = mutableListOf<RoomSummary>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = ListItemInviteBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(invites[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = invites.size
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateList(invites: List<RoomSummary>) {
|
||||
this.invites.clear()
|
||||
this.invites.addAll(invites)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ViewHolder(private val binding: ListItemInviteBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(invite: RoomSummary) {
|
||||
avatarRenderer.render(invite.toMatrixItem(), binding.avatar)
|
||||
binding.name.text = invite.name
|
||||
binding.root.setOnClickListener { onInviteClicked?.invoke(invite) }
|
||||
|
||||
invite.inviterId?.let {
|
||||
val inviterName = inviteUserTask?.invoke(it)
|
||||
if (inviterName != null) {
|
||||
binding.invitedBy.text = binding.root.context.getString(R.string.invited_by, inviterName)
|
||||
} else {
|
||||
binding.invitedBy.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import im.vector.app.databinding.BottomSheetInvitesBinding
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
class InvitesBottomSheet(
|
||||
private val invites: List<RoomSummary>,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val inviteUserTask: ((String) -> String?)?,
|
||||
private val onInviteClicked: ((RoomSummary) -> Unit)?
|
||||
) : BottomSheetDialogFragment() {
|
||||
|
||||
private lateinit var binding: BottomSheetInvitesBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = BottomSheetInvitesBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupInvitesList()
|
||||
}
|
||||
|
||||
private fun setupInvitesList() {
|
||||
val adapter = InvitesAdapter(avatarRenderer, inviteUserTask) {
|
||||
dismiss()
|
||||
onInviteClicked?.invoke(it)
|
||||
}
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
binding.invitesList.adapter = adapter
|
||||
binding.invitesList.layoutManager = layoutManager
|
||||
adapter.updateList(invites)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "InvitesBottomSheet"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import im.vector.app.databinding.ItemModalSpaceBinding
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
||||
class SpaceListAdapter(
|
||||
private val spaces: MutableList<RoomSummary>,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
) : RecyclerView.Adapter<SpaceListAdapter.ViewHolder>() {
|
||||
|
||||
private var onItemClickListener: ((RoomSummary) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val itemView = ItemModalSpaceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(spaces[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = spaces.size
|
||||
|
||||
fun replaceList(spaces: List<RoomSummary>) {
|
||||
this.spaces.clear()
|
||||
this.spaces.addAll(spaces)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun setOnSpaceClickListener(onClick: (space: RoomSummary) -> Unit) {
|
||||
this.onItemClickListener = onClick
|
||||
}
|
||||
|
||||
inner class ViewHolder(private val binding: ItemModalSpaceBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(space: RoomSummary) {
|
||||
avatarRenderer.render(space.toMatrixItem(), binding.avatar)
|
||||
binding.name.text = space.name
|
||||
binding.root.setOnClickListener { onItemClickListener?.invoke(space) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.app.features.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.allViews
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentSpaceListModalBinding
|
||||
import im.vector.app.features.analytics.experiment.ExperimentInteraction
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
import im.vector.app.features.spaces.SpaceListAction
|
||||
import im.vector.app.features.spaces.SpaceListViewModel
|
||||
import im.vector.app.features.spaces.manage.ManageType
|
||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SpaceListModalFragment : VectorBaseFragment<FragmentSpaceListModalBinding>() {
|
||||
|
||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||
|
||||
@Inject
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
private lateinit var binding: FragmentSpaceListModalBinding
|
||||
|
||||
private val viewModel: SpaceListViewModel by fragmentViewModel()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSpaceListModalBinding {
|
||||
binding = FragmentSpaceListModalBinding.inflate(inflater)
|
||||
return binding
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
||||
setupRecyclerView()
|
||||
setupTopBar()
|
||||
setupAddSpace()
|
||||
setupInvites()
|
||||
observeSpaceChange()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.roomList.layoutManager = LinearLayoutManager(context)
|
||||
binding.roomList.adapter = SpaceListAdapter(mutableListOf(), avatarRenderer).apply {
|
||||
setOnSpaceClickListener { spaceSummary ->
|
||||
viewModel.handle(SpaceListAction.SelectSpace(spaceSummary))
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTopBar() {
|
||||
if (!binding.roomList.canScrollVertically(-1)) {
|
||||
binding.headerBottomShadow.isVisible = false
|
||||
binding.addSpaceTopShadow.isVisible = false
|
||||
binding.addSpaceTopDivider.isVisible = true
|
||||
} else {
|
||||
binding.headerBottomShadow.isVisible = true
|
||||
binding.addSpaceTopShadow.isVisible = true
|
||||
binding.addSpaceTopDivider.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAddSpace() {
|
||||
binding.addSpaceLayout.setOnClickListener {
|
||||
val currentSpace = sharedActionViewModel.space.value
|
||||
if (currentSpace != null) {
|
||||
analyticsTracker.capture(ExperimentInteraction(ExperimentInteraction.Name.SpaceSwitchHeaderAdd))
|
||||
startActivity(SpaceManageActivity.newIntent(requireActivity(), currentSpace.roomId, ManageType.AddRoomsOnlySpaces))
|
||||
} else {
|
||||
analyticsTracker.capture(ExperimentInteraction(ExperimentInteraction.Name.SpaceSwitchHeaderCreate))
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.AddSpace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupInvites() {
|
||||
binding.invitesGroup.referencedIds.map { binding.root.findViewById<View>(it) }.forEach {
|
||||
it.setOnClickListener {
|
||||
withState(viewModel) { state ->
|
||||
val invitesBottomSheet = InvitesBottomSheet(state.inviteSpaces.orEmpty(), avatarRenderer, state.inviteUserTask) { invite ->
|
||||
sharedActionViewModel.post(HomeActivitySharedAction.OpenSpaceInvite(invite.roomId))
|
||||
}
|
||||
invitesBottomSheet.show(requireActivity().supportFragmentManager, InvitesBottomSheet.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeSpaceChange() = sharedActionViewModel.space.observe(viewLifecycleOwner) {
|
||||
viewModel.setSpace(it)
|
||||
binding.headerText.isVisible = it == null
|
||||
binding.headerTextLayout.isVisible = binding.headerText.isVisible || binding.invitesGroup.isVisible
|
||||
binding.addSpaceText.setText(if (it == null) R.string.create_space else R.string.add_space)
|
||||
}
|
||||
|
||||
override fun invalidate() {
|
||||
withState(viewModel) { state ->
|
||||
state.rootSpacesOrdered?.let {
|
||||
(binding.roomList.adapter as SpaceListAdapter).replaceList(it)
|
||||
binding.noSpacesYetGroup.isVisible = it.isEmpty()
|
||||
}
|
||||
|
||||
binding.invitesGroup.isVisible = state.inviteCount > 0
|
||||
binding.headerTextLayout.isVisible = binding.headerText.isVisible || binding.invitesGroup.isVisible
|
||||
binding.counterBadge.render(UnreadCounterBadgeView.State(state.inviteCount, true))
|
||||
setupTopBar()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ abstract class CollapsableTypedEpoxyController<T> :
|
|||
}
|
||||
|
||||
override fun buildModels() {
|
||||
check(isBuildingModels()) {
|
||||
check(isBuildingModels) {
|
||||
("You cannot call `buildModels` directly. Call `setData` instead to trigger a model " +
|
||||
"refresh with new data.")
|
||||
}
|
||||
|
|
|
@ -211,7 +211,7 @@ class RoomListFragment @Inject constructor(
|
|||
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.isVisible = true
|
||||
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.isVisible = true
|
||||
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.isVisible = true
|
||||
else -> Unit // No button in this mode
|
||||
RoomListDisplayMode.FILTERED -> Unit // No button in this mode
|
||||
}
|
||||
|
||||
views.createChatRoomButton.debouncedClicks {
|
||||
|
@ -237,7 +237,7 @@ class RoomListFragment @Inject constructor(
|
|||
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.hide()
|
||||
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.hide()
|
||||
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.hide()
|
||||
else -> Unit
|
||||
RoomListDisplayMode.FILTERED -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ class RoomListFragment @Inject constructor(
|
|||
val contentAdapter =
|
||||
when {
|
||||
section.livePages != null -> {
|
||||
pagedControllerFactory.createRoomSummaryPagedController()
|
||||
pagedControllerFactory.createRoomSummaryPagedController(roomListParams.displayMode)
|
||||
.also { controller ->
|
||||
section.livePages.observe(viewLifecycleOwner) { pl ->
|
||||
controller.submitList(pl)
|
||||
|
@ -316,7 +316,7 @@ class RoomListFragment @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
section.isExpanded.observe(viewLifecycleOwner) { _ ->
|
||||
section.isExpanded.observe(viewLifecycleOwner) {
|
||||
refreshCollapseStates()
|
||||
}
|
||||
controller.listener = this
|
||||
|
@ -337,14 +337,14 @@ class RoomListFragment @Inject constructor(
|
|||
checkEmptyState()
|
||||
}
|
||||
observeItemCount(section, sectionAdapter)
|
||||
section.isExpanded.observe(viewLifecycleOwner) { _ ->
|
||||
section.isExpanded.observe(viewLifecycleOwner) {
|
||||
refreshCollapseStates()
|
||||
}
|
||||
controller.listener = this
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
pagedControllerFactory.createRoomSummaryListController()
|
||||
pagedControllerFactory.createRoomSummaryListController(roomListParams.displayMode)
|
||||
.also { controller ->
|
||||
section.liveList?.observe(viewLifecycleOwner) { list ->
|
||||
controller.setData(list)
|
||||
|
@ -366,7 +366,7 @@ class RoomListFragment @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
section.isExpanded.observe(viewLifecycleOwner) { _ ->
|
||||
section.isExpanded.observe(viewLifecycleOwner) {
|
||||
refreshCollapseStates()
|
||||
}
|
||||
controller.listener = this
|
||||
|
@ -402,7 +402,7 @@ class RoomListFragment @Inject constructor(
|
|||
RoomListDisplayMode.NOTIFICATIONS -> views.createChatFabMenu.show()
|
||||
RoomListDisplayMode.PEOPLE -> views.createChatRoomButton.show()
|
||||
RoomListDisplayMode.ROOMS -> views.createGroupRoomButton.show()
|
||||
else -> Unit
|
||||
RoomListDisplayMode.FILTERED -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ class RoomListFragment @Inject constructor(
|
|||
isBigImage = true,
|
||||
message = getString(R.string.room_list_rooms_empty_body)
|
||||
)
|
||||
else ->
|
||||
RoomListDisplayMode.FILTERED ->
|
||||
// Always display the content in this mode, because if the footer
|
||||
StateView.State.Content
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import im.vector.app.core.ui.views.PresenceStateImageView
|
|||
import im.vector.app.core.ui.views.ShieldImageView
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||
|
@ -45,48 +46,102 @@ import org.matrix.android.sdk.api.util.MatrixItem
|
|||
@EpoxyModelClass(layout = R.layout.item_room)
|
||||
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var typingMessage: String
|
||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||
@EpoxyAttribute
|
||||
lateinit var typingMessage: String
|
||||
|
||||
@EpoxyAttribute lateinit var lastFormattedEvent: EpoxyCharSequence
|
||||
@EpoxyAttribute lateinit var lastEventTime: String
|
||||
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
@EpoxyAttribute var userPresence: UserPresence? = null
|
||||
@EpoxyAttribute var showPresence: Boolean = false
|
||||
@EpoxyAttribute var izPublic: Boolean = false
|
||||
@EpoxyAttribute var unreadNotificationCount: Int = 0
|
||||
@EpoxyAttribute var hasUnreadMessage: Boolean = false
|
||||
@EpoxyAttribute var hasDraft: Boolean = false
|
||||
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||
@EpoxyAttribute var hasFailedSending: Boolean = false
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: ClickListener? = null
|
||||
@EpoxyAttribute var showSelected: Boolean = false
|
||||
@EpoxyAttribute
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var matrixItem: MatrixItem
|
||||
|
||||
@EpoxyAttribute
|
||||
var displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var subtitle: String
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var lastFormattedEvent: EpoxyCharSequence
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var lastEventTime: String
|
||||
|
||||
@EpoxyAttribute
|
||||
var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var userPresence: UserPresence? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var showPresence: Boolean = false
|
||||
|
||||
@EpoxyAttribute @JvmField
|
||||
var isPublic: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var unreadNotificationCount: Int = 0
|
||||
|
||||
@EpoxyAttribute
|
||||
var hasUnreadMessage: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var hasDraft: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var showHighlighted: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var hasFailedSending: Boolean = false
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var itemLongClickListener: View.OnLongClickListener? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var itemClickListener: ClickListener? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var showSelected: Boolean = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
||||
renderDisplayMode(holder)
|
||||
holder.rootView.onClick(itemClickListener)
|
||||
holder.rootView.setOnLongClickListener {
|
||||
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
itemLongClickListener?.onLongClick(it) ?: false
|
||||
}
|
||||
holder.titleView.text = matrixItem.getBestName()
|
||||
holder.lastEventTimeView.text = lastEventTime
|
||||
holder.lastEventView.text = lastFormattedEvent.charSequence
|
||||
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
|
||||
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
|
||||
holder.draftView.isVisible = hasDraft
|
||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||
holder.roomAvatarDecorationImageView.render(encryptionTrustLevel)
|
||||
holder.roomAvatarPublicDecorationImageView.isVisible = izPublic
|
||||
holder.roomAvatarPublicDecorationImageView.isVisible = isPublic
|
||||
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
|
||||
renderSelection(holder, showSelected)
|
||||
holder.typingView.setTextOrHide(typingMessage)
|
||||
holder.lastEventView.isInvisible = holder.typingView.isVisible
|
||||
holder.roomAvatarPresenceImageView.render(showPresence, userPresence)
|
||||
}
|
||||
|
||||
private fun renderDisplayMode(holder: Holder) = when (displayMode) {
|
||||
RoomListDisplayMode.ROOMS,
|
||||
RoomListDisplayMode.PEOPLE,
|
||||
RoomListDisplayMode.NOTIFICATIONS -> renderForDefaultDisplayMode(holder)
|
||||
RoomListDisplayMode.FILTERED -> renderForFilteredDisplayMode(holder)
|
||||
}
|
||||
|
||||
private fun renderForDefaultDisplayMode(holder: Holder) {
|
||||
holder.subtitleView.text = lastFormattedEvent.charSequence
|
||||
holder.lastEventTimeView.text = lastEventTime
|
||||
}
|
||||
|
||||
private fun renderForFilteredDisplayMode(holder: Holder) {
|
||||
holder.subtitleView.text = subtitle
|
||||
holder.typingView.setTextOrHide(typingMessage)
|
||||
holder.subtitleView.isInvisible = holder.typingView.isVisible
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
holder.rootView.setOnClickListener(null)
|
||||
holder.rootView.setOnLongClickListener(null)
|
||||
|
@ -110,7 +165,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||
val titleView by bind<TextView>(R.id.roomNameView)
|
||||
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomUnreadCounterBadgeView)
|
||||
val unreadIndentIndicator by bind<View>(R.id.roomUnreadIndicator)
|
||||
val lastEventView by bind<TextView>(R.id.roomLastEventView)
|
||||
val subtitleView by bind<TextView>(R.id.subtitleView)
|
||||
val typingView by bind<TextView>(R.id.roomTypingView)
|
||||
val draftView by bind<ImageView>(R.id.roomDraftBadge)
|
||||
val lastEventTimeView by bind<TextView>(R.id.roomLastEventTimeView)
|
||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
|
|||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
|
||||
import im.vector.app.features.home.room.typing.TypingHelper
|
||||
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
||||
|
@ -46,13 +47,16 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
fun create(roomSummary: RoomSummary,
|
||||
roomChangeMembershipStates: Map<String, ChangeMembershipState>,
|
||||
selectedRoomIds: Set<String>,
|
||||
displayMode: RoomListDisplayMode,
|
||||
listener: RoomListListener?): VectorEpoxyModel<*> {
|
||||
return when (roomSummary.membership) {
|
||||
Membership.INVITE -> {
|
||||
val changeMembershipState = roomChangeMembershipStates[roomSummary.roomId] ?: ChangeMembershipState.Unknown
|
||||
createInvitationItem(roomSummary, changeMembershipState, listener)
|
||||
}
|
||||
else -> createRoomItem(roomSummary, selectedRoomIds, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked })
|
||||
else -> createRoomItem(
|
||||
roomSummary, selectedRoomIds, displayMode, listener?.let { it::onRoomClicked }, listener?.let { it::onRoomLongClicked }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,9 +109,11 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
fun createRoomItem(
|
||||
roomSummary: RoomSummary,
|
||||
selectedRoomIds: Set<String>,
|
||||
displayMode: RoomListDisplayMode,
|
||||
onClick: ((RoomSummary) -> Unit)?,
|
||||
onLongClick: ((RoomSummary) -> Boolean)?
|
||||
): VectorEpoxyModel<*> {
|
||||
val subtitle = getSearchResultSubtitle(roomSummary)
|
||||
val unreadCount = roomSummary.notificationCount
|
||||
val showHighlighted = roomSummary.highlightCount > 0
|
||||
val showSelected = selectedRoomIds.contains(roomSummary.roomId)
|
||||
|
@ -118,13 +124,16 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect, roomSummary.isDirect.not())
|
||||
latestEventTime = dateFormatter.format(latestEvent.root.originServerTs, DateFormatKind.ROOM_LIST)
|
||||
}
|
||||
|
||||
val typingMessage = typingHelper.getTypingMessage(roomSummary.typingUsers)
|
||||
return RoomSummaryItem_()
|
||||
.id(roomSummary.roomId)
|
||||
.avatarRenderer(avatarRenderer)
|
||||
// We do not display shield in the room list anymore
|
||||
// .encryptionTrustLevel(roomSummary.roomEncryptionTrustLevel)
|
||||
.izPublic(roomSummary.isPublic)
|
||||
.displayMode(displayMode)
|
||||
.subtitle(subtitle)
|
||||
.isPublic(roomSummary.isPublic)
|
||||
.showPresence(roomSummary.isDirect)
|
||||
.userPresence(roomSummary.directUserPresence)
|
||||
.matrixItem(roomSummary.toMatrixItem())
|
||||
|
@ -142,4 +151,12 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
}
|
||||
.itemClickListener { onClick?.invoke(roomSummary) }
|
||||
}
|
||||
|
||||
private fun getSearchResultSubtitle(roomSummary: RoomSummary): String {
|
||||
val userId = roomSummary.directUserId
|
||||
val spaceName = roomSummary.spaceParents?.firstOrNull()?.roomSummary?.name
|
||||
val canonicalAlias = roomSummary.canonicalAlias
|
||||
|
||||
return (userId ?: spaceName ?: canonicalAlias).orEmpty()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,19 @@
|
|||
|
||||
package im.vector.app.features.home.room.list
|
||||
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
class RoomSummaryListController(
|
||||
private val roomSummaryItemFactory: RoomSummaryItemFactory
|
||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
||||
private val displayMode: RoomListDisplayMode
|
||||
) : CollapsableTypedEpoxyController<List<RoomSummary>>() {
|
||||
|
||||
var listener: RoomListListener? = null
|
||||
|
||||
override fun buildModels(data: List<RoomSummary>?) {
|
||||
data?.forEach {
|
||||
add(roomSummaryItemFactory.create(it, emptyMap(), emptySet(), listener))
|
||||
add(roomSummaryItemFactory.create(it, emptyMap(), emptySet(), displayMode, listener))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ package im.vector.app.features.home.room.list
|
|||
import com.airbnb.epoxy.EpoxyModel
|
||||
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
||||
import im.vector.app.core.utils.createUIHandler
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
|
||||
class RoomSummaryPagedController(
|
||||
private val roomSummaryItemFactory: RoomSummaryItemFactory
|
||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
||||
private val displayMode: RoomListDisplayMode
|
||||
) : PagedListEpoxyController<RoomSummary>(
|
||||
// Important it must match the PageList builder notify Looper
|
||||
modelBuildingHandler = createUIHandler()
|
||||
|
@ -57,6 +59,6 @@ class RoomSummaryPagedController(
|
|||
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
|
||||
// for place holder if enabled
|
||||
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
|
||||
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), listener)
|
||||
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), displayMode, listener)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,19 @@
|
|||
|
||||
package im.vector.app.features.home.room.list
|
||||
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomSummaryPagedControllerFactory @Inject constructor(
|
||||
private val roomSummaryItemFactory: RoomSummaryItemFactory
|
||||
) {
|
||||
|
||||
fun createRoomSummaryPagedController(): RoomSummaryPagedController {
|
||||
return RoomSummaryPagedController(roomSummaryItemFactory)
|
||||
fun createRoomSummaryPagedController(displayMode: RoomListDisplayMode): RoomSummaryPagedController {
|
||||
return RoomSummaryPagedController(roomSummaryItemFactory, displayMode)
|
||||
}
|
||||
|
||||
fun createRoomSummaryListController(): RoomSummaryListController {
|
||||
return RoomSummaryListController(roomSummaryItemFactory)
|
||||
fun createRoomSummaryListController(displayMode: RoomListDisplayMode): RoomSummaryListController {
|
||||
return RoomSummaryListController(roomSummaryItemFactory, displayMode)
|
||||
}
|
||||
|
||||
fun createSuggestedRoomListController(): SuggestedRoomListController {
|
||||
|
|
|
@ -22,6 +22,7 @@ import im.vector.app.R
|
|||
import im.vector.app.core.epoxy.loadingItem
|
||||
import im.vector.app.core.epoxy.noResultItem
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import im.vector.app.features.home.room.list.RoomSummaryItemFactory
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
@ -53,7 +54,13 @@ class IncomingShareController @Inject constructor(private val roomSummaryItemFac
|
|||
} else {
|
||||
roomSummaries.forEach { roomSummary ->
|
||||
roomSummaryItemFactory
|
||||
.createRoomItem(roomSummary, data.selectedRoomIds, callback?.let { it::onRoomClicked }, callback?.let { it::onRoomLongClicked })
|
||||
.createRoomItem(
|
||||
roomSummary,
|
||||
data.selectedRoomIds,
|
||||
RoomListDisplayMode.FILTERED,
|
||||
callback?.let { it::onRoomClicked },
|
||||
callback?.let { it::onRoomLongClicked }
|
||||
)
|
||||
.addTo(this)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,19 +76,16 @@ class SpaceListFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onDragReleased(model: SpaceSummaryItem?, itemView: View?) {
|
||||
// Timber.v("VAL: onModelMoved from $fromPositionM to $toPositionM ${model?.matrixItem?.getBestName()}")
|
||||
if (toPositionM == null || fromPositionM == null) return
|
||||
val movingSpace = model?.matrixItem?.id ?: return
|
||||
viewModel.handle(SpaceListAction.MoveSpace(movingSpace, toPositionM!! - fromPositionM!!))
|
||||
}
|
||||
|
||||
override fun clearView(model: SpaceSummaryItem?, itemView: View?) {
|
||||
// Timber.v("VAL: clearView ${model?.matrixItem?.getBestName()}")
|
||||
itemView?.elevation = initialElevation ?: 0f
|
||||
}
|
||||
|
||||
override fun onModelMoved(fromPosition: Int, toPosition: Int, modelBeingMoved: SpaceSummaryItem?, itemView: View?) {
|
||||
// Timber.v("VAL: onModelMoved incremental from $fromPosition to $toPosition ${modelBeingMoved?.matrixItem?.getBestName()}")
|
||||
if (fromPositionM == null) {
|
||||
fromPositionM = fromPosition
|
||||
}
|
||||
|
@ -97,7 +94,6 @@ class SpaceListFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun isDragEnabledForModel(model: SpaceSummaryItem?): Boolean {
|
||||
// Timber.v("VAL: isDragEnabledForModel ${model?.matrixItem?.getBestName()}")
|
||||
return model?.canDrag == true
|
||||
}
|
||||
})
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.app.features.spaces
|
||||
|
||||
import androidx.lifecycle.asFlow
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
|
@ -29,7 +30,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import im.vector.app.features.analytics.experiment.ExperimentInteraction
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
|
@ -50,10 +51,12 @@ import org.matrix.android.sdk.api.session.Session
|
|||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.getUser
|
||||
import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
|
@ -71,6 +74,8 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa
|
|||
private val analyticsTracker: AnalyticsTracker
|
||||
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
||||
|
||||
private var currentSpace: RoomSummary? = null
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<SpaceListViewModel, SpaceListViewState> {
|
||||
override fun create(initialState: SpaceListViewState): SpaceListViewModel
|
||||
|
@ -138,7 +143,7 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa
|
|||
)
|
||||
setState {
|
||||
copy(
|
||||
homeAggregateCount = counts
|
||||
homeAggregateCount = counts,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -229,14 +234,13 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa
|
|||
|
||||
private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state ->
|
||||
val groupingMethod = state.selectedGroupingMethod
|
||||
val isAtSpace = groupingMethod.space() != null
|
||||
if (groupingMethod is RoomGroupingMethod.ByLegacyGroup || groupingMethod.space()?.roomId != action.spaceSummary?.roomId) {
|
||||
analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSwitchSpace))
|
||||
setState { copy(selectedGroupingMethod = RoomGroupingMethod.BySpace(action.spaceSummary)) }
|
||||
appStateHandler.setCurrentSpace(action.spaceSummary?.roomId)
|
||||
_viewEvents.post(SpaceListViewEvents.OpenSpace(groupingMethod is RoomGroupingMethod.ByLegacyGroup))
|
||||
} else {
|
||||
analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSelectedSpace))
|
||||
}
|
||||
analyticsTracker.capture(ExperimentInteraction(ExperimentInteraction.Name.SpacePanelSwitchSpace, mapOf("isSubspace" to isAtSpace)))
|
||||
}
|
||||
|
||||
private fun handleSelectGroup(action: SpaceListAction.SelectLegacyGroup) = withState { state ->
|
||||
|
@ -273,36 +277,43 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa
|
|||
_viewEvents.post(SpaceListViewEvents.AddSpace)
|
||||
}
|
||||
|
||||
var asyncSpaceList: Async<List<RoomSummary>>? = null
|
||||
|
||||
private fun observeSpaceSummaries() {
|
||||
val params = spaceSummaryQueryParams {
|
||||
memberships = listOf(Membership.JOIN, Membership.INVITE)
|
||||
displayName = QueryStringValue.IsNotEmpty
|
||||
}
|
||||
|
||||
combine(
|
||||
session.flow()
|
||||
.liveSpaceSummaries(params),
|
||||
combine(session.flow().liveSpaceSummaries(params),
|
||||
session.accountDataService()
|
||||
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
||||
.asFlow()
|
||||
) { spaces, _ ->
|
||||
spaces
|
||||
) { spaces, _ -> spaces }.execute { async ->
|
||||
asyncSpaceList = async
|
||||
val currentSpaceChildren = currentSpace?.let { space -> async.invoke()?.filter { it.flattenParentIds.contains(space.roomId) } }
|
||||
val rootSpaces = async.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() }
|
||||
val displaySpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId == null }
|
||||
val inviteSpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId != null }
|
||||
val inviteUserTask: (String) -> String? = {
|
||||
session.getUser(it)?.displayName
|
||||
}
|
||||
val orders = displaySpaces.associate {
|
||||
it.roomId to session.getRoom(it.roomId)
|
||||
?.roomAccountDataService()
|
||||
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
|
||||
?.content.toModel<SpaceOrderContent>()
|
||||
?.safeOrder()
|
||||
}
|
||||
copy(
|
||||
asyncSpaces = async,
|
||||
rootSpacesOrdered = displaySpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
inviteSpaces = inviteSpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
inviteCount = inviteSpaces.size,
|
||||
inviteUserTask = inviteUserTask,
|
||||
spaceOrderInfo = orders
|
||||
)
|
||||
}
|
||||
.execute { async ->
|
||||
val rootSpaces = async.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() }
|
||||
val orders = rootSpaces.associate {
|
||||
it.roomId to session.getRoom(it.roomId)
|
||||
?.roomAccountDataService()
|
||||
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
|
||||
?.content.toModel<SpaceOrderContent>()
|
||||
?.safeOrder()
|
||||
}
|
||||
copy(
|
||||
asyncSpaces = async,
|
||||
rootSpacesOrdered = rootSpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
spaceOrderInfo = orders
|
||||
)
|
||||
}
|
||||
|
||||
// clear local echos on update
|
||||
session.accountDataService()
|
||||
|
@ -314,4 +325,36 @@ class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: Spa
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitSpaceViewState() = asyncSpaceList?.let { async ->
|
||||
val currentSpaceChildren = currentSpace?.let { space -> asyncSpaceList?.invoke()?.filter { it.flattenParentIds.contains(space.roomId) } }
|
||||
val rootSpaces = asyncSpaceList?.invoke().orEmpty().filter { it.flattenParentIds.isEmpty() }
|
||||
val displaySpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId == null }
|
||||
val inviteSpaces = (currentSpaceChildren ?: rootSpaces).filter { it.inviterId != null }
|
||||
val inviteUserTask: (String) -> String? = {
|
||||
session.getUser(it)?.displayName
|
||||
}
|
||||
val orders = displaySpaces.associate {
|
||||
it.roomId to session.getRoom(it.roomId)
|
||||
?.roomAccountDataService()
|
||||
?.getAccountDataEvent(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER)
|
||||
?.content.toModel<SpaceOrderContent>()
|
||||
?.safeOrder()
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
asyncSpaces = async,
|
||||
rootSpacesOrdered = displaySpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
inviteSpaces = inviteSpaces.sortedWith(TopLevelSpaceComparator(orders)),
|
||||
inviteCount = inviteSpaces.size,
|
||||
inviteUserTask = inviteUserTask,
|
||||
spaceOrderInfo = orders
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSpace(space: RoomSummary?) {
|
||||
this.currentSpace = space
|
||||
emitSpaceViewState()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,12 @@ data class SpaceListViewState(
|
|||
val asyncSpaces: Async<List<RoomSummary>> = Uninitialized,
|
||||
val selectedGroupingMethod: RoomGroupingMethod = RoomGroupingMethod.BySpace(null),
|
||||
val rootSpacesOrdered: List<RoomSummary>? = null,
|
||||
val inviteSpaces: List<RoomSummary>? = null,
|
||||
val spaceOrderInfo: Map<String, String?>? = null,
|
||||
val spaceOrderLocalEchos: Map<String, String?>? = null,
|
||||
val legacyGroups: List<GroupSummary>? = null,
|
||||
val expandedStates: Map<String, Boolean> = emptyMap(),
|
||||
val inviteCount: Int = 0,
|
||||
val inviteUserTask: ((String) -> String?)? = null,
|
||||
val homeAggregateCount: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0)
|
||||
) : MavericksState
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.spaces
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.RoomGroupingMethod
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
import im.vector.app.core.ui.list.genericHeaderItem
|
||||
import im.vector.app.features.grouplist.groupSummaryItem
|
||||
import im.vector.app.features.grouplist.homeSpaceSummaryItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
import im.vector.app.group
|
||||
import im.vector.app.space
|
||||
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpaceModalController @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
// private val colorProvider: ColorProvider,
|
||||
// private val stringProvider: StringProvider
|
||||
) : EpoxyController() {
|
||||
|
||||
var callback: Callback? = null
|
||||
private var viewState: SpaceListViewState? = null
|
||||
|
||||
fun update(viewState: SpaceListViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
viewState?.apply {
|
||||
buildGroupModels(selectedGroupingMethod, rootSpacesOrdered, expandedStates)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildGroupModels(selected: RoomGroupingMethod,
|
||||
rootSpaces: List<RoomSummary>?,
|
||||
expandedStates: Map<String, Boolean>) {
|
||||
val host = this
|
||||
|
||||
rootSpaces?.forEach { spaceSummary ->
|
||||
val isSelected = selected is RoomGroupingMethod.BySpace && spaceSummary.roomId == selected.space()?.roomId
|
||||
val expanded = expandedStates[spaceSummary.roomId] == true
|
||||
|
||||
spaceSummaryItem {
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
id(spaceSummary.roomId)
|
||||
expanded(expanded)
|
||||
matrixItem(spaceSummary.toMatrixItem())
|
||||
selected(isSelected)
|
||||
canDrag(true)
|
||||
listener { host.callback?.onSpaceSelected(spaceSummary) }
|
||||
countState(
|
||||
UnreadCounterBadgeView.State(
|
||||
spaceSummary.notificationCount,
|
||||
spaceSummary.highlightCount > 0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onSpaceSelected(spaceSummary: RoomSummary?)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?attr/vctr_content_secondary">
|
||||
|
||||
<item android:drawable="@color/modal_background_color"/>
|
||||
|
||||
<!--this creates the mask with the ripple effect-->
|
||||
<item
|
||||
android:id="@+id/mask"
|
||||
android:drawable="?attr/vctr_toolbar_background" />
|
||||
|
||||
</ripple>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/vctr_toolbar_background"/>
|
||||
<corners
|
||||
android:bottomLeftRadius="12dp"
|
||||
android:bottomRightRadius="12dp" />
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12.7822,4.2963C12.7822,3.8641 12.4318,3.5137 11.9996,3.5137C11.5673,3.5137 11.217,3.8641 11.217,4.2963V11.2173L4.2963,11.2173C3.8641,11.2173 3.5137,11.5676 3.5137,11.9999C3.5137,12.4321 3.8641,12.7825 4.2963,12.7825H11.217V19.7038C11.217,20.136 11.5673,20.4864 11.9996,20.4864C12.4318,20.4864 12.7822,20.136 12.7822,19.7038V12.7825H19.7038C20.136,12.7825 20.4864,12.4321 20.4864,11.9999C20.4864,11.5676 20.136,11.2173 19.7038,11.2173L12.7822,11.2173V4.2963Z"
|
||||
android:fillColor="#17191C"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?attr/vctr_content_secondary">
|
||||
|
||||
<!--this creates the mask with the ripple effect-->
|
||||
<item
|
||||
android:id="@+id/mask"
|
||||
android:drawable="?attr/vctr_toolbar_background" />
|
||||
|
||||
</ripple>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#10000000"
|
||||
android:endColor="@android:color/transparent"
|
||||
android:angle="90" />
|
||||
</shape>
|
|
@ -22,10 +22,4 @@
|
|||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/homeDrawerFragmentContainer"
|
||||
android:layout_width="@dimen/navigation_drawer_max_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="360dp"
|
||||
android:background="?attr/vctr_toolbar_background"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/invites_header"
|
||||
style="@style/Widget.Vector.TextView.Title.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/space_invites"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/invites_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/invites_header"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:listitem="@layout/list_item_invite"
|
||||
tools:itemCount="3"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -9,6 +9,7 @@
|
|||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<im.vector.app.core.ui.views.CurrentCallsView
|
||||
|
@ -18,19 +19,64 @@
|
|||
android:minHeight="48dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/homeKeysBackupBanner"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/groupToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Widget.ActionBarTitle"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupToolbarSpaceTitleView"
|
||||
style="@style/TextAppearance.Vector.Widget.ActionBarSubTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:visibility="gone"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/homeToolbarContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/groupToolbarAvatarImageView"
|
||||
|
@ -39,6 +85,7 @@
|
|||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:contentDescription="@string/a11y_open_drawer"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
@ -74,58 +121,83 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupToolbarNavigateUp"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:src="@drawable/ic_arrow_back"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/a11y_navigate_up_space"
|
||||
android:visibility="gone"
|
||||
app:tint="?vctr_content_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/homeToolbarContent"
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/back_button_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp">
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back_button_chevron"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_back_24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?attr/vctr_message_text_color" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/back_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/chats"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/palette_element_green"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintStart_toEndOf="@id/back_button_chevron"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/all_chats_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/back_button_layout">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/space_avatar"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:visibility="gone"
|
||||
app:shapeAppearanceOverlay="@style/SpaceListModalImageShapeOverlay"
|
||||
tools:background="#42A5F5"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupToolbarTitleView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Widget.ActionBarTitle"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupToolbarSpaceTitleView"
|
||||
style="@style/TextAppearance.Vector.Widget.ActionBarSubTitle"
|
||||
style="@style/Widget.Vector.TextView.Title.Bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:visibility="gone"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="@string/all_chats"
|
||||
android:textSize="22sp" />
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/toolbar_chevron"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:tint="@color/palette_element_green"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
|
@ -149,12 +221,14 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/syncStateView"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/roomListContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
|
||||
app:layout_constraintTop_toBottomOf="@id/homeKeysBackupBanner" />
|
||||
|
||||
|
@ -165,6 +239,46 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:menu="@menu/home_bottom_navigation" />
|
||||
app:menu="@menu/home_bottom_navigation"
|
||||
app:labelVisibilityMode="labeled" />
|
||||
|
||||
<View
|
||||
android:id="@+id/dim_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.5"
|
||||
android:background="#000"
|
||||
android:elevation="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
|
||||
|
||||
<View
|
||||
android:id="@+id/dim_view_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:alpha="0.5"
|
||||
android:background="#000"
|
||||
android:elevation="10dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bottomNavigationView"
|
||||
app:layout_constraintTop_toTopOf="@id/bottomNavigationView" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/space_modal_fragment"
|
||||
android:name="im.vector.app.features.home.SpaceListModalFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bottom_guideline"
|
||||
android:elevation="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/bottom_guideline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.75" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
<?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="match_parent"
|
||||
android:background="@drawable/bg_space_modal">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/header_text_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:background="?attr/vctr_toolbar_background"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:textSize="13sp"
|
||||
android:textColor="?attr/vctr_content_tertiary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/choose_a_space"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/invites_group"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:constraint_referenced_ids="invites_text, counter_badge"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/invites_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textSize="13sp"
|
||||
android:textColor="?attr/vctr_content_tertiary"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/invites"
|
||||
android:textAllCaps="true"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/counter_badge" />
|
||||
|
||||
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
android:id="@+id/counter_badge"
|
||||
style="@style/Widget.Vector.TextView.Micro"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:minWidth="16dp"
|
||||
android:minHeight="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:textColor="?colorOnError"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/invites_text"
|
||||
app:layout_constraintBottom_toBottomOf="@id/invites_text"
|
||||
tools:background="@drawable/bg_unread_highlight"
|
||||
tools:text="147" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/room_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text_layout"
|
||||
app:layout_constraintBottom_toTopOf="@id/add_space_layout"
|
||||
tools:itemCount="3"
|
||||
tools:layout_editor_absoluteX="-99dp"
|
||||
tools:listitem="@layout/item_modal_space"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:id="@+id/header_bottom_shadow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:background="@drawable/top_shadow"
|
||||
android:visibility="gone"
|
||||
android:rotation="180"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text_layout" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/no_spaces_yet_group"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="no_spaces_yet_text, no_spaces_yet_message" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_spaces_yet_text"
|
||||
style="@style/Widget.Vector.TextView.Title.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_spaces_yet"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text_layout"
|
||||
app:layout_constraintBottom_toTopOf="@id/no_spaces_yet_message"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_spaces_yet_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/no_spaces_yet_message"
|
||||
android:textColor="?attr/vctr_content_tertiary"
|
||||
app:layout_constraintTop_toBottomOf="@id/no_spaces_yet_text"
|
||||
app:layout_constraintBottom_toTopOf="@id/add_space_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/add_space_top_shadow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:background="@drawable/top_shadow"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/add_space_layout" />
|
||||
|
||||
<View
|
||||
android:id="@+id/add_space_top_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/vctr_content_quinary"
|
||||
app:layout_constraintBottom_toTopOf="@id/add_space_layout" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/add_space_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_modal_ripple_grey"
|
||||
android:elevation="3dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/add_space_icon_background"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:background="#4D8D97A5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/SpaceListModalImageShapeOverlay" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_plus"
|
||||
app:layout_constraintBottom_toBottomOf="@id/add_space_icon_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/add_space_icon_background"
|
||||
app:layout_constraintStart_toStartOf="@id/add_space_icon_background"
|
||||
app:layout_constraintTop_toTopOf="@id/add_space_icon_background"
|
||||
app:tint="?attr/vctr_spoiler_background_color" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/add_space_text"
|
||||
style="@style/Widget.Vector.TextView.Body.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/add_space"
|
||||
android:textSize="17sp"
|
||||
android:textColor="?attr/vctr_message_text_color"
|
||||
app:layout_constraintBottom_toBottomOf="@id/add_space_icon_background"
|
||||
app:layout_constraintStart_toEndOf="@id/add_space_icon_background"
|
||||
app:layout_constraintTop_toTopOf="@id/add_space_icon_background" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/add_space_icon_background" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -36,13 +36,13 @@
|
|||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/groupBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@id/groupAvatarChevron"
|
||||
app:layout_constraintEnd_toStartOf="@id/chevron"
|
||||
app:layout_constraintStart_toEndOf="@id/groupAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupAvatarChevron"
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="21dp"
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?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="@drawable/ripple_grey"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/SpaceListModalImageShapeOverlay"
|
||||
tools:background="#42A5F5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
style="@style/Widget.Vector.TextView.Body.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
tools:text="Space name" />
|
||||
|
||||
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||
android:id="@+id/counter_badge"
|
||||
style="@style/Widget.Vector.TextView.Micro"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:minWidth="16dp"
|
||||
android:minHeight="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:textColor="?colorOnError"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toStartOf="@id/chevron"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:background="@drawable/bg_unread_highlight"
|
||||
tools:text="147"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?vctr_content_primary"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -183,7 +183,7 @@
|
|||
tools:text="@tools:sample/date/hhmm" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomLastEventView"
|
||||
android:id="@+id/subtitleView"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -213,7 +213,8 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/roomNameView"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomNameView"
|
||||
tools:text="Alice is typing…" />
|
||||
tools:text="Alice is typing…"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<!-- Margin bottom does not work, so I use space -->
|
||||
<Space
|
||||
|
@ -221,7 +222,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="7dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomLastEventView"
|
||||
app:layout_constraintTop_toBottomOf="@id/subtitleView"
|
||||
tools:layout_marginStart="120dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
|
|
|
@ -120,12 +120,12 @@
|
|||
android:padding="4dp"
|
||||
android:src="@drawable/ic_more_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/groupAvatarChevron"
|
||||
app:layout_constraintEnd_toStartOf="@id/chevron"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?vctr_content_secondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupAvatarChevron"
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="21dp"
|
||||
|
|
|
@ -101,12 +101,12 @@
|
|||
android:padding="4dp"
|
||||
android:src="@drawable/ic_more_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/groupAvatarChevron"
|
||||
app:layout_constraintEnd_toStartOf="@id/chevron"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?vctr_content_secondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupAvatarChevron"
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="21dp"
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@drawable/ripple_grey"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/space_avatars"
|
||||
android:layout_marginStart="16dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/Widget.Vector.TextView.Body.Medium"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar"
|
||||
app:layout_constraintBottom_toTopOf="@id/invited_by"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintVertical_chainStyle="spread_inside"
|
||||
tools:text="Element Corp."/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:id="@+id/invited_by"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?attr/vctr_content_secondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"
|
||||
app:layout_constraintStart_toStartOf="@id/name"
|
||||
tools:text="Invited by John from Cornwall"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -7,26 +7,31 @@
|
|||
android:id="@+id/menu_home_setting"
|
||||
android:icon="@drawable/ic_settings_x"
|
||||
android:title="@string/settings"
|
||||
android:orderInCategory="100"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_home_suggestion"
|
||||
android:icon="@drawable/ic_material_bug_report"
|
||||
android:orderInCategory="110"
|
||||
android:title="@string/send_suggestion" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_home_report_bug"
|
||||
android:icon="@drawable/ic_material_bug_report"
|
||||
android:orderInCategory="120"
|
||||
android:title="@string/send_bug_report" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_home_init_sync_legacy"
|
||||
android:title="Do a legacy init sync"
|
||||
android:orderInCategory="130"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_home_init_sync_optimized"
|
||||
android:title="Do an optimized init sync"
|
||||
android:orderInCategory="140"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<item
|
||||
|
|
|
@ -1,9 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_home_mark_all_as_read"
|
||||
android:icon="@drawable/ic_material_done"
|
||||
android:orderInCategory="50"
|
||||
android:title="@string/action_mark_all_as_read" />
|
||||
|
||||
</menu>
|
||||
<item
|
||||
android:id="@+id/menu_explore_rooms"
|
||||
android:title="@string/space_explore_activity_title"
|
||||
android:orderInCategory="10"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_invite_people"
|
||||
android:title="@string/invite_people_menu"
|
||||
android:orderInCategory="20"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_add_rooms"
|
||||
android:title="@string/space_add_child_title"
|
||||
android:orderInCategory="30"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
|
|
@ -620,8 +620,6 @@
|
|||
<string name="room_participants_leave_prompt_msg">Are you sure you want to leave the room?</string>
|
||||
<string name="room_participants_leave_private_warning">This room is not public. You will not be able to rejoin without an invite.</string>
|
||||
|
||||
<string name="room_participants_header_direct_chats">Direct Messages</string>
|
||||
|
||||
<string name="room_participants_action_invite">Invite</string>
|
||||
<string name="room_participants_action_cancel_invite">Cancel invite</string>
|
||||
<string name="room_participants_action_ban">Ban</string>
|
||||
|
@ -897,7 +895,6 @@
|
|||
|
||||
<string name="settings_messages_containing_display_name">My display name</string>
|
||||
<string name="settings_messages_containing_username">My username</string>
|
||||
<string name="settings_messages_direct_messages">Direct messages</string>
|
||||
<string name="settings_encrypted_direct_messages">Encrypted direct messages</string>
|
||||
<string name="settings_group_messages">Group messages</string>
|
||||
<string name="settings_encrypted_group_messages">Encrypted group messages</string>
|
||||
|
@ -1607,7 +1604,6 @@
|
|||
<string name="room_preview_not_found">This room is not accessible at this time.\nTry again later, or ask a room admin to check if you have access.</string>
|
||||
<string name="room_preview_no_preview_join">"This room can't be previewed. Do you want to join it?"</string>
|
||||
<string name="fab_menu_create_room">"Rooms"</string>
|
||||
<string name="fab_menu_create_chat">"Direct Messages"</string>
|
||||
|
||||
<!-- Create room screen -->
|
||||
<string name="create_room_action_create">"CREATE"</string>
|
||||
|
@ -1680,7 +1676,11 @@
|
|||
<string name="settings_labs_show_hidden_events_in_timeline">Show hidden events in timeline</string>
|
||||
<string name="settings_labs_show_complete_history_in_encrypted_room">"Show complete history in encrypted rooms"</string>
|
||||
|
||||
<string name="bottom_action_people_x">Direct Messages</string>
|
||||
<string name="fab_menu_create_chat">"Direct Messages"</string>
|
||||
<string name="room_participants_header_direct_chats">Direct Messages</string>
|
||||
|
||||
<string name="bottom_action_people_x">People</string>
|
||||
<string name="settings_messages_direct_messages">Direct messages</string>
|
||||
|
||||
<string name="send_file_step_idle">Waiting…</string>
|
||||
<string name="send_file_step_encrypting_thumbnail">Encrypting thumbnail…</string>
|
||||
|
@ -3042,4 +3042,13 @@
|
|||
<!-- Screen sharing -->
|
||||
<string name="screen_sharing_notification_title">${app_name} Screen Sharing</string>
|
||||
<string name="screen_sharing_notification_description">Screen sharing is in progress</string>
|
||||
<string name="all_chats">All Chats</string>
|
||||
<string name="choose_a_space">Spaces</string>
|
||||
<string name="invites">Invites</string>
|
||||
<string name="chats">Chats</string>
|
||||
<string name="no_spaces_yet">No spaces yet</string>
|
||||
<string name="no_spaces_yet_message">Add spaces to group your chats</string>
|
||||
<string name="no_subspaces_yet">No subspaces yet</string>
|
||||
<string name="no_subspaces_yet_message">Add subspaces to group your chats</string>
|
||||
<string name="space_invites">Space Invites</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue