Home: start reworking room list.
This commit is contained in:
parent
275521db70
commit
c0fd06fd2d
@ -23,6 +23,7 @@ import im.vector.riotredesign.core.error.ErrorFormatter
|
|||||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||||
import im.vector.riotredesign.core.resources.StringArrayProvider
|
import im.vector.riotredesign.core.resources.StringArrayProvider
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||||
import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator
|
import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator
|
||||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||||
@ -52,6 +53,10 @@ class AppModule(private val context: Context) {
|
|||||||
SelectedGroupStore()
|
SelectedGroupStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
HomeRoomListObservableStore()
|
||||||
|
}
|
||||||
|
|
||||||
single {
|
single {
|
||||||
RoomSummaryComparator()
|
RoomSummaryComparator()
|
||||||
}
|
}
|
||||||
|
@ -29,4 +29,9 @@ object DateProvider {
|
|||||||
return LocalDateTime.ofInstant(instant, zoneId)
|
return LocalDateTime.ofInstant(instant, zoneId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun currentLocalDateTime(): LocalDateTime {
|
||||||
|
val instant = Instant.now()
|
||||||
|
return LocalDateTime.ofInstant(instant, zoneId)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,14 +25,7 @@ import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserControl
|
|||||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||||
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.CallItemFactory
|
import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.DefaultItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.MessageItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomHistoryVisibilityItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomMemberItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomNameItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.RoomTopicItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
|
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||||
import im.vector.riotredesign.features.home.room.list.RoomSummaryController
|
import im.vector.riotredesign.features.home.room.list.RoomSummaryController
|
||||||
@ -56,36 +49,36 @@ class HomeModule {
|
|||||||
HomeNavigator()
|
HomeNavigator()
|
||||||
}
|
}
|
||||||
|
|
||||||
scope(HOME_SCOPE) {
|
|
||||||
HomeRoomListObservableStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
scope(HOME_SCOPE) {
|
scope(HOME_SCOPE) {
|
||||||
HomePermalinkHandler(get())
|
HomePermalinkHandler(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment scopes
|
// Fragment scopes
|
||||||
|
|
||||||
|
factory {
|
||||||
|
TimelineDateFormatter(get())
|
||||||
|
}
|
||||||
|
|
||||||
factory { (fragment: Fragment) ->
|
factory { (fragment: Fragment) ->
|
||||||
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
|
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
|
||||||
val timelineDateFormatter = TimelineDateFormatter(get())
|
|
||||||
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
|
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
|
||||||
val colorProvider = ColorProvider(fragment.requireContext())
|
val colorProvider = ColorProvider(fragment.requireContext())
|
||||||
|
val timelineDateFormatter = get<TimelineDateFormatter>()
|
||||||
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer)
|
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer)
|
||||||
|
|
||||||
val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory,
|
val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory,
|
||||||
roomNameItemFactory = RoomNameItemFactory(get()),
|
roomNameItemFactory = RoomNameItemFactory(get()),
|
||||||
roomTopicItemFactory = RoomTopicItemFactory(get()),
|
roomTopicItemFactory = RoomTopicItemFactory(get()),
|
||||||
roomMemberItemFactory = RoomMemberItemFactory(get()),
|
roomMemberItemFactory = RoomMemberItemFactory(get()),
|
||||||
roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()),
|
roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()),
|
||||||
callItemFactory = CallItemFactory(get()),
|
callItemFactory = CallItemFactory(get()),
|
||||||
defaultItemFactory = DefaultItemFactory()
|
defaultItemFactory = DefaultItemFactory()
|
||||||
)
|
)
|
||||||
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
|
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
factory {
|
factory {
|
||||||
RoomSummaryController(get())
|
RoomSummaryController(get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
factory {
|
factory {
|
||||||
|
@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
|||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.core.utils.LiveEvent
|
import im.vector.riotredesign.core.utils.LiveEvent
|
||||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||||
|
import io.reactivex.Observable
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
|
|
||||||
typealias RoomListFilterName = CharSequence
|
typealias RoomListFilterName = CharSequence
|
||||||
@ -50,7 +51,7 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val displayMode = initialState.displayMode
|
||||||
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())
|
private val roomListFilter = BehaviorRelay.createDefault<Option<RoomListFilterName>>(Option.empty())
|
||||||
|
|
||||||
private val _openRoomLiveData = MutableLiveData<LiveEvent<String>>()
|
private val _openRoomLiveData = MutableLiveData<LiveEvent<String>>()
|
||||||
@ -86,15 +87,27 @@ class RoomListViewModel(initialState: RoomListViewState,
|
|||||||
|
|
||||||
|
|
||||||
private fun observeRoomSummaries() {
|
private fun observeRoomSummaries() {
|
||||||
homeRoomListObservableSource.observe()
|
homeRoomListObservableSource
|
||||||
|
.observe()
|
||||||
|
.flatMapSingle {
|
||||||
|
Observable.fromIterable(it)
|
||||||
|
.filter(filterByDisplayMode(displayMode))
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
.map { buildRoomSummaries(it) }
|
.map { buildRoomSummaries(it) }
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(
|
copy(asyncRooms = async)
|
||||||
asyncRooms = async
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun filterByDisplayMode(displayMode: RoomListFragment.DisplayMode) = { roomSummary: RoomSummary ->
|
||||||
|
when (displayMode) {
|
||||||
|
RoomListFragment.DisplayMode.HOME -> roomSummary.notificationCount > 0
|
||||||
|
RoomListFragment.DisplayMode.PEOPLE -> roomSummary.isDirect
|
||||||
|
RoomListFragment.DisplayMode.ROOMS -> !roomSummary.isDirect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
private fun buildRoomSummaries(rooms: List<RoomSummary>): RoomSummaries {
|
||||||
val invites = ArrayList<RoomSummary>()
|
val invites = ArrayList<RoomSummary>()
|
||||||
val favourites = ArrayList<RoomSummary>()
|
val favourites = ArrayList<RoomSummary>()
|
||||||
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
|
|||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
data class RoomListViewState(
|
data class RoomListViewState(
|
||||||
|
val displayMode: RoomListFragment.DisplayMode,
|
||||||
val asyncRooms: Async<RoomSummaries> = Uninitialized,
|
val asyncRooms: Async<RoomSummaries> = Uninitialized,
|
||||||
val isInviteExpanded: Boolean = true,
|
val isInviteExpanded: Boolean = true,
|
||||||
val isFavouriteRoomsExpanded: Boolean = true,
|
val isFavouriteRoomsExpanded: Boolean = true,
|
||||||
@ -33,6 +34,8 @@ data class RoomListViewState(
|
|||||||
val isServerNoticeRoomsExpanded: Boolean = true
|
val isServerNoticeRoomsExpanded: Boolean = true
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
|
constructor(args: RoomListParams) : this(displayMode = args.displayMode)
|
||||||
|
|
||||||
fun isCategoryExpanded(roomCategory: RoomCategory): Boolean {
|
fun isCategoryExpanded(roomCategory: RoomCategory): Boolean {
|
||||||
return when (roomCategory) {
|
return when (roomCategory) {
|
||||||
RoomCategory.INVITE -> isInviteExpanded
|
RoomCategory.INVITE -> isInviteExpanded
|
||||||
|
@ -19,9 +19,13 @@ package im.vector.riotredesign.features.home.room.list
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.riotredesign.core.extensions.localDateTime
|
||||||
|
import im.vector.riotredesign.core.resources.DateProvider
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||||
|
|
||||||
class RoomSummaryController(private val stringProvider: StringProvider
|
class RoomSummaryController(private val stringProvider: StringProvider,
|
||||||
|
private val timelineDateFormatter: TimelineDateFormatter
|
||||||
) : TypedEpoxyController<RoomListViewState>() {
|
) : TypedEpoxyController<RoomListViewState>() {
|
||||||
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
@ -76,9 +80,29 @@ class RoomSummaryController(private val stringProvider: StringProvider
|
|||||||
val unreadCount = roomSummary.notificationCount
|
val unreadCount = roomSummary.notificationCount
|
||||||
val showHighlighted = roomSummary.highlightCount > 0
|
val showHighlighted = roomSummary.highlightCount > 0
|
||||||
|
|
||||||
|
var lastMessageFormatted: CharSequence = ""
|
||||||
|
var lastMessageTime: CharSequence = ""
|
||||||
|
val lastMessage = roomSummary.lastMessage
|
||||||
|
if (lastMessage != null) {
|
||||||
|
val date = lastMessage.localDateTime()
|
||||||
|
val currentData = DateProvider.currentLocalDateTime()
|
||||||
|
val isSameDay = date.toLocalDate() == currentData.toLocalDate()
|
||||||
|
//TODO: get formatted
|
||||||
|
lastMessageFormatted = lastMessage.content?.toString() ?: ""
|
||||||
|
lastMessageTime = if (isSameDay) {
|
||||||
|
timelineDateFormatter.formatMessageHour(date)
|
||||||
|
} else {
|
||||||
|
//TODO: change this
|
||||||
|
timelineDateFormatter.formatMessageDay(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
roomSummaryItem {
|
roomSummaryItem {
|
||||||
id(roomSummary.roomId)
|
id(roomSummary.roomId)
|
||||||
roomId(roomSummary.roomId)
|
roomId(roomSummary.roomId)
|
||||||
|
lastEventTime(lastMessageTime)
|
||||||
|
lastFormattedEvent(lastMessageFormatted)
|
||||||
roomName(roomSummary.displayName)
|
roomName(roomSummary.displayName)
|
||||||
avatarUrl(roomSummary.avatarUrl)
|
avatarUrl(roomSummary.avatarUrl)
|
||||||
showHighlighted(showHighlighted)
|
showHighlighted(showHighlighted)
|
||||||
|
@ -32,6 +32,8 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||||||
|
|
||||||
@EpoxyAttribute lateinit var roomName: CharSequence
|
@EpoxyAttribute lateinit var roomName: CharSequence
|
||||||
@EpoxyAttribute lateinit var roomId: String
|
@EpoxyAttribute lateinit var roomId: String
|
||||||
|
@EpoxyAttribute lateinit var lastFormattedEvent: CharSequence
|
||||||
|
@EpoxyAttribute lateinit var lastEventTime: CharSequence
|
||||||
@EpoxyAttribute var avatarUrl: String? = null
|
@EpoxyAttribute var avatarUrl: String? = null
|
||||||
@EpoxyAttribute var unreadCount: Int = 0
|
@EpoxyAttribute var unreadCount: Int = 0
|
||||||
@EpoxyAttribute var showHighlighted: Boolean = false
|
@EpoxyAttribute var showHighlighted: Boolean = false
|
||||||
@ -40,15 +42,17 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
|
|||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.unreadCounterBadgeView.render(unreadCount, showHighlighted)
|
|
||||||
holder.rootView.setOnClickListener { listener?.invoke() }
|
holder.rootView.setOnClickListener { listener?.invoke() }
|
||||||
holder.titleView.text = roomName
|
holder.titleView.text = roomName
|
||||||
|
holder.lastEventTimeView.text = lastEventTime
|
||||||
|
holder.lastEventView.text = lastFormattedEvent
|
||||||
AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomUnreadCounterBadgeView)
|
|
||||||
val titleView by bind<TextView>(R.id.roomNameView)
|
val titleView by bind<TextView>(R.id.roomNameView)
|
||||||
|
val lastEventView by bind<TextView>(R.id.roomLastEventView)
|
||||||
|
val lastEventTimeView by bind<TextView>(R.id.roomLastEventTimeView)
|
||||||
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
||||||
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
|
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:minHeight="48dp"
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
@ -17,8 +18,8 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/roomAvatarImageView"
|
android:id="@+id/roomAvatarImageView"
|
||||||
android:layout_width="32dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="40dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
@ -33,30 +34,38 @@
|
|||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:duplicateParentState="true"
|
android:duplicateParentState="true"
|
||||||
android:textColor="@color/color_room_title"
|
android:textColor="@color/black_87"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/roomLastEventTimeView"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/roomAvatarImageView"
|
app:layout_constraintStart_toEndOf="@id/roomAvatarImageView"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/full_names" />
|
tools:text="@tools:sample/full_names" />
|
||||||
|
|
||||||
<im.vector.riotredesign.features.home.room.list.UnreadCounterBadgeView
|
<TextView
|
||||||
android:id="@+id/roomUnreadCounterBadgeView"
|
android:id="@+id/roomLastEventView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/black_38"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/roomNameView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomNameView"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/roomLastEventTimeView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:layout_marginStart="8dp"
|
||||||
android:minWidth="24dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:minHeight="24dp"
|
android:textColor="@color/black_38"
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:paddingRight="4dp"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBaseline_toBaselineOf="@id/messageMemberNameView"
|
||||||
app:layout_constraintDimensionRatio="1:1"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintStart_toEndOf="@id/messageMemberNameView"
|
||||||
tools:background="@drawable/bg_unread_highlight"
|
tools:text="@tools:sample/date/hhmm" />
|
||||||
tools:text="115" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
<color name="pale_grey_two">#ebedf8</color>
|
<color name="pale_grey_two">#ebedf8</color>
|
||||||
<color name="brown_grey">#a5a5a5</color>
|
<color name="brown_grey">#a5a5a5</color>
|
||||||
<color name="grey_lynch">#61708B</color>
|
<color name="grey_lynch">#61708B</color>
|
||||||
|
<color name="black_87">#de000000</color>
|
||||||
|
<color name="black_38">#61000000</color>
|
||||||
|
<color name="black_37">#5d000000</color>
|
||||||
|
|
||||||
<color name="black">#000000</color>
|
<color name="black">#000000</color>
|
||||||
<color name="black_87">#de000000</color>
|
<color name="black_87">#de000000</color>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user