App layout Home screen empty states (#7007)
1
changelog.d/6835.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
[App Layout] New empty states for home screen
|
@ -3244,4 +3244,14 @@
|
|||||||
<item quantity="other">Consider signing out from old sessions (%1$d days or more) that you don’t use anymore.</item>
|
<item quantity="other">Consider signing out from old sessions (%1$d days or more) that you don’t use anymore.</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Note to translators: %s will be replaces with selected space name -->
|
||||||
|
<string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>
|
||||||
|
<!-- Note to translators: for RTL languages, Spaces will be at the bottom left. Please translate "bottom-left" instead of "bottom-right". Thanks!-->
|
||||||
|
<string name="home_empty_space_no_rooms_message">Spaces are a new way to group rooms and people. Add an existing room, or create a new one, using the bottom-right button.</string>
|
||||||
|
<!-- Note to translators: %s will be replaces with current user displayname -->
|
||||||
|
<string name="home_empty_no_rooms_title">Welcome to ${app_name},\n%s.</string>
|
||||||
|
<string name="home_empty_no_rooms_message">The all-in-one secure chat app for teams, friends and organisations. Create a chat, or join an existing room, to get started.</string>
|
||||||
|
<string name="home_empty_no_unreads_title">Nothing to report.</string>
|
||||||
|
<string name="home_empty_no_unreads_message">This is where your unread messages will show up, when you have some.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.updateConstraintSet
|
import im.vector.app.core.extensions.updateConstraintSet
|
||||||
@ -36,7 +37,8 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
val title: CharSequence? = null,
|
val title: CharSequence? = null,
|
||||||
val image: Drawable? = null,
|
val image: Drawable? = null,
|
||||||
val isBigImage: Boolean = false,
|
val isBigImage: Boolean = false,
|
||||||
val message: CharSequence? = null
|
val message: CharSequence? = null,
|
||||||
|
val imageScaleType: ImageView.ScaleType? = ImageView.ScaleType.FIT_CENTER,
|
||||||
) : State()
|
) : State()
|
||||||
|
|
||||||
data class Error(val message: CharSequence? = null) : State()
|
data class Error(val message: CharSequence? = null) : State()
|
||||||
@ -79,6 +81,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
is State.Content -> Unit
|
is State.Content -> Unit
|
||||||
is State.Loading -> Unit
|
is State.Loading -> Unit
|
||||||
is State.Empty -> {
|
is State.Empty -> {
|
||||||
|
views.emptyImageView.scaleType = newState.imageScaleType
|
||||||
views.emptyImageView.setImageDrawable(newState.image)
|
views.emptyImageView.setImageDrawable(newState.image)
|
||||||
views.emptyView.updateConstraintSet {
|
views.emptyView.updateConstraintSet {
|
||||||
it.constrainPercentHeight(R.id.emptyImageView, if (newState.isBigImage) 0.5f else 0.1f)
|
it.constrainPercentHeight(R.id.emptyImageView, if (newState.isBigImage) 0.5f else 0.1f)
|
||||||
|
@ -199,11 +199,17 @@ class HomeRoomListFragment :
|
|||||||
).also { controller ->
|
).also { controller ->
|
||||||
controller.listener = this
|
controller.listener = this
|
||||||
controller.onFilterChanged = ::onRoomFilterChanged
|
controller.onFilterChanged = ::onRoomFilterChanged
|
||||||
|
roomListViewModel.emptyStateFlow.onEach { emptyStateOptional ->
|
||||||
|
controller.submitEmptyStateData(emptyStateOptional.getOrNull())
|
||||||
|
}.launchIn(lifecycleScope)
|
||||||
section.filtersData.onEach {
|
section.filtersData.onEach {
|
||||||
controller.submitFiltersData(it.getOrNull())
|
controller.submitFiltersData(it.getOrNull())
|
||||||
}.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
section.list.observe(viewLifecycleOwner) { list ->
|
section.list.observe(viewLifecycleOwner) { list ->
|
||||||
controller.submitList(list)
|
controller.submitList(list)
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
controller.requestForcedModelBuild()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.adapter
|
}.adapter
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.list.home
|
package im.vector.app.features.home.room.list.home
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import arrow.core.toOption
|
import arrow.core.toOption
|
||||||
@ -23,11 +24,14 @@ import com.airbnb.mvrx.MavericksViewModelFactory
|
|||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.SpaceStateHandler
|
import im.vector.app.SpaceStateHandler
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.StateView
|
import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.core.resources.DrawableProvider
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
import im.vector.app.features.home.room.list.home.filter.HomeRoomFilter
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -52,6 +56,7 @@ import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
|||||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult
|
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.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.state.isPublic
|
import org.matrix.android.sdk.api.session.room.state.isPublic
|
||||||
@ -63,6 +68,8 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val spaceStateHandler: SpaceStateHandler,
|
private val spaceStateHandler: SpaceStateHandler,
|
||||||
private val preferencesStore: HomeLayoutPreferencesStore,
|
private val preferencesStore: HomeLayoutPreferencesStore,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val drawableProvider: DrawableProvider,
|
||||||
) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
|
) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -82,6 +89,10 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
private val _sections = MutableSharedFlow<Set<HomeRoomSection>>(replay = 1)
|
private val _sections = MutableSharedFlow<Set<HomeRoomSection>>(replay = 1)
|
||||||
val sections = _sections.asSharedFlow()
|
val sections = _sections.asSharedFlow()
|
||||||
|
|
||||||
|
private var currentFilter: HomeRoomFilter = HomeRoomFilter.ALL
|
||||||
|
private val _emptyStateFlow = MutableSharedFlow<Optional<StateView.State.Empty>>(replay = 1)
|
||||||
|
val emptyStateFlow = _emptyStateFlow.asSharedFlow()
|
||||||
|
|
||||||
private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null
|
private var filteredPagedRoomSummariesLive: UpdatableLivePageResult? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -109,6 +120,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
newSections.add(getFilteredRoomsSection())
|
newSections.add(getFilteredRoomsSection())
|
||||||
|
|
||||||
|
emitEmptyState()
|
||||||
_sections.emit(newSections)
|
_sections.emit(newSections)
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
@ -171,6 +183,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
liveResults.queryParams = liveResults.queryParams.copy(
|
liveResults.queryParams = liveResults.queryParams.copy(
|
||||||
spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
|
spaceFilter = selectedSpace?.roomId.toActiveSpaceOrNoFilter()
|
||||||
)
|
)
|
||||||
|
emitEmptyState()
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
|
||||||
return HomeRoomSection.RoomSummaryData(
|
return HomeRoomSection.RoomSummaryData(
|
||||||
@ -179,6 +192,13 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun emitEmptyState() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val emptyState = getEmptyStateData(currentFilter, spaceStateHandler.getCurrentSpace())
|
||||||
|
_emptyStateFlow.emit(Optional.from(emptyState))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
|
private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
|
||||||
val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
|
val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
|
||||||
|
|
||||||
@ -250,6 +270,38 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getEmptyStateData(filter: HomeRoomFilter, selectedSpace: RoomSummary?): StateView.State.Empty? {
|
||||||
|
return when (filter) {
|
||||||
|
HomeRoomFilter.ALL ->
|
||||||
|
if (selectedSpace != null) {
|
||||||
|
StateView.State.Empty(
|
||||||
|
title = stringProvider.getString(R.string.home_empty_space_no_rooms_title, selectedSpace.displayName),
|
||||||
|
message = stringProvider.getString(R.string.home_empty_space_no_rooms_message),
|
||||||
|
image = drawableProvider.getDrawable(R.drawable.ill_empty_space),
|
||||||
|
isBigImage = true
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val userName = session.userService().getUser(session.myUserId)?.displayName ?: ""
|
||||||
|
StateView.State.Empty(
|
||||||
|
title = stringProvider.getString(R.string.home_empty_no_rooms_title, userName),
|
||||||
|
message = stringProvider.getString(R.string.home_empty_no_rooms_message),
|
||||||
|
image = drawableProvider.getDrawable(R.drawable.ill_empty_all_chats),
|
||||||
|
isBigImage = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HomeRoomFilter.UNREADS ->
|
||||||
|
StateView.State.Empty(
|
||||||
|
title = stringProvider.getString(R.string.home_empty_no_unreads_title),
|
||||||
|
message = stringProvider.getString(R.string.home_empty_no_unreads_message),
|
||||||
|
image = drawableProvider.getDrawable(R.drawable.ill_empty_unreads),
|
||||||
|
isBigImage = true,
|
||||||
|
imageScaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||||
|
)
|
||||||
|
else ->
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: HomeRoomListAction) {
|
override fun handle(action: HomeRoomListAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is HomeRoomListAction.SelectRoom -> handleSelectRoom(action)
|
is HomeRoomListAction.SelectRoom -> handleSelectRoom(action)
|
||||||
@ -261,9 +313,12 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleChangeRoomFilter(action: HomeRoomListAction.ChangeRoomFilter) {
|
private fun handleChangeRoomFilter(action: HomeRoomListAction.ChangeRoomFilter) {
|
||||||
|
currentFilter = action.filter
|
||||||
filteredPagedRoomSummariesLive?.let { liveResults ->
|
filteredPagedRoomSummariesLive?.let { liveResults ->
|
||||||
liveResults.queryParams = getFilteredQueryParams(action.filter, liveResults.queryParams)
|
liveResults.queryParams = getFilteredQueryParams(action.filter, liveResults.queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitEmptyState()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPublicRoom(roomId: String): Boolean {
|
fun isPublicRoom(roomId: String): Boolean {
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.room.list.home
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.core.platform.StateView
|
||||||
|
|
||||||
|
@EpoxyModelClass
|
||||||
|
abstract class RoomListEmptyItem : VectorEpoxyModel<RoomListEmptyItem.Holder>(R.layout.item_state_view) {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var emptyData: StateView.State.Empty
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
holder.stateView.state = emptyData
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val stateView by bind<StateView>(R.id.stateView)
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,13 @@ package im.vector.app.features.home.room.list.home.filter
|
|||||||
|
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
import com.airbnb.epoxy.paging.PagedListEpoxyController
|
||||||
|
import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.utils.createUIHandler
|
import im.vector.app.core.utils.createUIHandler
|
||||||
import im.vector.app.features.home.RoomListDisplayMode
|
import im.vector.app.features.home.RoomListDisplayMode
|
||||||
import im.vector.app.features.home.room.list.RoomListListener
|
import im.vector.app.features.home.room.list.RoomListListener
|
||||||
import im.vector.app.features.home.room.list.RoomSummaryItemFactory
|
import im.vector.app.features.home.room.list.RoomSummaryItemFactory
|
||||||
import im.vector.app.features.home.room.list.RoomSummaryItemPlaceHolder_
|
import im.vector.app.features.home.room.list.RoomSummaryItemPlaceHolder_
|
||||||
|
import im.vector.app.features.home.room.list.home.roomListEmptyItem
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
@ -44,6 +46,8 @@ class HomeFilteredRoomsController(
|
|||||||
var onFilterChanged: ((HomeRoomFilter) -> Unit)? = null
|
var onFilterChanged: ((HomeRoomFilter) -> Unit)? = null
|
||||||
|
|
||||||
private var filtersData: List<HomeRoomFilter>? = null
|
private var filtersData: List<HomeRoomFilter>? = null
|
||||||
|
private var emptyStateData: StateView.State.Empty? = null
|
||||||
|
private var currentState: StateView.State = StateView.State.Content
|
||||||
|
|
||||||
override fun addModels(models: List<EpoxyModel<*>>) {
|
override fun addModels(models: List<EpoxyModel<*>>) {
|
||||||
val host = this
|
val host = this
|
||||||
@ -54,14 +58,29 @@ class HomeFilteredRoomsController(
|
|||||||
onFilterChangedListener(host.onFilterChanged)
|
onFilterChangedListener(host.onFilterChanged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.addModels(models)
|
|
||||||
|
if (models.isEmpty() && emptyStateData != null) {
|
||||||
|
emptyStateData?.let { emptyState ->
|
||||||
|
roomListEmptyItem {
|
||||||
|
id("state_item")
|
||||||
|
emptyData(emptyState)
|
||||||
|
}
|
||||||
|
currentState = emptyState
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentState = StateView.State.Content
|
||||||
|
super.addModels(models)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitEmptyStateData(state: StateView.State.Empty?) {
|
||||||
|
this.emptyStateData = state
|
||||||
}
|
}
|
||||||
|
|
||||||
fun submitFiltersData(data: List<HomeRoomFilter>?) {
|
fun submitFiltersData(data: List<HomeRoomFilter>?) {
|
||||||
this.filtersData = data
|
this.filtersData = data
|
||||||
requestForcedModelBuild()
|
requestForcedModelBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
|
override fun buildItemModel(currentPosition: Int, item: RoomSummary?): EpoxyModel<*> {
|
||||||
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
|
item ?: return RoomSummaryItemPlaceHolder_().apply { id(currentPosition) }
|
||||||
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
|
return roomSummaryItemFactory.create(item, roomChangeMembershipStates.orEmpty(), emptySet(), RoomListDisplayMode.ROOMS, listener)
|
||||||
|
BIN
vector/src/main/res/drawable-hdpi/ill_empty_all_chats.webp
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
vector/src/main/res/drawable-hdpi/ill_empty_space.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
vector/src/main/res/drawable-hdpi/ill_empty_unreads.webp
Normal file
After Width: | Height: | Size: 794 B |
BIN
vector/src/main/res/drawable-mdpi/ill_empty_all_chats.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
vector/src/main/res/drawable-mdpi/ill_empty_space.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
vector/src/main/res/drawable-mdpi/ill_empty_unreads.webp
Normal file
After Width: | Height: | Size: 556 B |
BIN
vector/src/main/res/drawable-xhdpi/ill_empty_all_chats.webp
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
vector/src/main/res/drawable-xhdpi/ill_empty_space.webp
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
vector/src/main/res/drawable-xhdpi/ill_empty_unreads.webp
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
vector/src/main/res/drawable-xxhdpi/ill_empty_all_chats.webp
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
vector/src/main/res/drawable-xxhdpi/ill_empty_space.webp
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
vector/src/main/res/drawable-xxhdpi/ill_empty_unreads.webp
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
vector/src/main/res/drawable-xxxhdpi/ill_empty_all_chats.webp
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
vector/src/main/res/drawable-xxxhdpi/ill_empty_space.webp
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
vector/src/main/res/drawable-xxxhdpi/ill_empty_unreads.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
8
vector/src/main/res/layout/item_state_view.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<im.vector.app.core.platform.StateView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/stateView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:colorBackground">
|
||||||
|
|
||||||
|
</im.vector.app.core.platform.StateView>
|