minor code cleanup

Signed-off-by: Marijan Šikić <marijansikic77@gmail.com>
This commit is contained in:
Marijan Šikić 2020-04-02 19:52:47 +02:00 committed by Mario Đanić
parent c1066b6c1d
commit 80628e48e0
14 changed files with 112 additions and 111 deletions

View File

@ -44,7 +44,7 @@ import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryView import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryView
import com.nextcloud.talk.newarch.features.contactsflow.contacts.ContactsView import com.nextcloud.talk.newarch.features.contactsflow.contacts.ContactsView
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView
import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.SecurityUtils import com.nextcloud.talk.utils.SecurityUtils

View File

@ -50,7 +50,7 @@ import com.nextcloud.talk.newarch.di.module.StorageModule
import com.nextcloud.talk.newarch.domain.di.module.UseCasesModule import com.nextcloud.talk.newarch.domain.di.module.UseCasesModule
import com.nextcloud.talk.newarch.features.account.di.module.AccountModule import com.nextcloud.talk.newarch.features.account.di.module.AccountModule
import com.nextcloud.talk.newarch.features.contactsflow.di.module.ContactsFlowModule import com.nextcloud.talk.newarch.features.contactsflow.di.module.ContactsFlowModule
import com.nextcloud.talk.newarch.features.conversationslist.di.module.ConversationsListModule import com.nextcloud.talk.newarch.features.conversationsList.di.module.ConversationsListModule
import com.nextcloud.talk.newarch.local.dao.UsersDao import com.nextcloud.talk.newarch.local.dao.UsersDao
import com.nextcloud.talk.newarch.local.models.User import com.nextcloud.talk.newarch.local.models.User
import com.nextcloud.talk.newarch.local.models.other.UserStatus.* import com.nextcloud.talk.newarch.local.models.other.UserStatus.*

View File

@ -26,6 +26,7 @@ import androidx.lifecycle.LiveData
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
interface ConversationsRepository { interface ConversationsRepository {
fun getConversationsForUser(userId: Long, filter: CharSequence?): LiveData<List<Conversation>> fun getConversationsForUser(userId: Long, filter: CharSequence?): LiveData<List<Conversation>>
fun getShortcutTargetConversations(userId: Long): LiveData<List<Conversation>> fun getShortcutTargetConversations(userId: Long): LiveData<List<Conversation>>
suspend fun getConversationForUserWithToken(internalUserId: Long, token: String): Conversation? suspend fun getConversationForUserWithToken(internalUserId: Long, token: String): Conversation?

View File

@ -37,7 +37,7 @@ import com.bluelinelabs.conductor.archlifecycle.ControllerLifecycleOwner
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView
import com.nextcloud.talk.newarch.mvvm.BaseView import com.nextcloud.talk.newarch.mvvm.BaseView
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.uber.autodispose.lifecycle.LifecycleScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider

View File

@ -41,7 +41,7 @@ import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationStateWrapper import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationStateWrapper
import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement import com.nextcloud.talk.newarch.features.contactsflow.ParticipantElement
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView
import com.nextcloud.talk.newarch.local.models.canUserCreateGroupConversations import com.nextcloud.talk.newarch.local.models.canUserCreateGroupConversations
import com.nextcloud.talk.newarch.services.GlobalService import com.nextcloud.talk.newarch.services.GlobalService
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking

View File

@ -36,7 +36,7 @@ import com.nextcloud.talk.newarch.domain.usecases.SetConversationPasswordUseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationState
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationStateWrapper import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewOperationStateWrapper
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView
import com.nextcloud.talk.newarch.services.GlobalService import com.nextcloud.talk.newarch.services.GlobalService
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf

View File

@ -20,7 +20,7 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
import android.app.Application import android.app.Application
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel

View File

@ -20,7 +20,7 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable

View File

@ -20,7 +20,7 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation

View File

@ -20,9 +20,8 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -30,7 +29,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.observe import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.LayoutMode import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.bottomsheets.BottomSheet
@ -51,12 +49,12 @@ import com.nextcloud.talk.newarch.features.contactsflow.contacts.ContactsView
import com.nextcloud.talk.newarch.features.search.DebouncingTextWatcher import com.nextcloud.talk.newarch.features.search.DebouncingTextWatcher
import com.nextcloud.talk.newarch.local.models.toUser import com.nextcloud.talk.newarch.local.models.toUser
import com.nextcloud.talk.newarch.mvvm.BaseView import com.nextcloud.talk.newarch.mvvm.BaseView
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.animations.SharedElementTransition import com.nextcloud.talk.utils.animations.SharedElementTransition
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.otaliastudios.elements.Adapter import com.otaliastudios.elements.Adapter
import com.otaliastudios.elements.Adapter.Companion.AUTOSCROLL_POSITION_0
import com.otaliastudios.elements.Element import com.otaliastudios.elements.Element
import com.otaliastudios.elements.Page import com.otaliastudios.elements.Page
import com.otaliastudios.elements.Presenter import com.otaliastudios.elements.Presenter
@ -76,67 +74,73 @@ class ConversationsListView : BaseView() {
val factory: ConversationListViewModelFactory by inject() val factory: ConversationListViewModelFactory by inject()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup container: ViewGroup
): View { ): View {
viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java) viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java)
val view = super.onCreateView(inflater, container) val view = super.onCreateView(inflater, container)
val adapter = Adapter.builder(this) val adapter = Adapter.builder(this)
.addSource(ConversationsListSource(viewModel.conversationsLiveData)) .addSource(ConversationsListSource(viewModel.conversationsLiveData))
.addPresenter(ConversationPresenter(activity as Context, ::onElementClick, ::onElementLongClick)) .addPresenter(ConversationPresenter(context, ::onElementClick, ::onElementLongClick))
.addPresenter(Presenter.forLoadingIndicator(activity as Context, R.layout.loading_state)) .addPresenter(Presenter.forLoadingIndicator(context, R.layout.loading_state))
.addPresenter(AdvancedEmptyPresenter(activity as Context, R.layout.message_state, ::openNewConversationScreen) { view -> .addPresenter(AdvancedEmptyPresenter(context, R.layout.message_state, ::openNewConversationScreen) { view ->
view.messageStateImageView.imageTintList = resources?.getColor(R.color.colorPrimary)?.let { ColorStateList.valueOf(it) } view.messageStateImageView.imageTintList = resources?.getColor(R.color.colorPrimary)?.let { ColorStateList.valueOf(it) }
}) })
.addPresenter(Presenter.forErrorIndicator(activity as Context, R.layout.message_state) { view, throwable -> .addPresenter(Presenter.forErrorIndicator(context, R.layout.message_state) { view, _ ->
view.messageStateTextView.setText(R.string.nc_oops) with(view) {
view.messageStateImageView.setImageDrawable((activity as Context).getDrawable(drawable.ic_announcement_white_24dp)) messageStateTextView.setText(R.string.nc_oops)
view.messageStateImageView.imageTintList = resources?.getColor(R.color.colorPrimary)?.let { ColorStateList.valueOf(it) } messageStateImageView.setImageDrawable(context.getDrawable(drawable.ic_announcement_white_24dp))
}) messageStateImageView.imageTintList = resources?.getColor(R.color.colorPrimary)?.let { ColorStateList.valueOf(it) }
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_0, true) }
.into(view.recyclerView) }
)
.setAutoScrollMode(AUTOSCROLL_POSITION_0, true)
.into(view.recyclerView)
view.apply { view.apply {
recyclerView.initRecyclerView(LinearLayoutManager(activity), adapter, true) recyclerView.adapter = adapter
swipeRefreshLayoutView.setOnRefreshListener { with(swipeRefreshLayoutView) {
view.swipeRefreshLayoutView.isRefreshing = false setColorSchemeResources(R.color.colorPrimary)
viewModel.loadConversations() setOnRefreshListener {
view.swipeRefreshLayoutView.isRefreshing = false
viewModel.loadConversations()
}
} }
swipeRefreshLayoutView.setColorSchemeResources(R.color.colorPrimary)
} }
activity?.inputEditText?.addTextChangedListener(DebouncingTextWatcher(lifecycle, ::setSearchQuery)) activity?.inputEditText?.addTextChangedListener(DebouncingTextWatcher(lifecycle, ::setSearchQuery))
activity?.settingsButton?.setOnClickListener { activity?.settingsButton?.setOnClickListener {
val settingsTransitionName = "userAvatar.transitionTag" val settingsTransitionName = "userAvatar.transitionTag"
router.pushController( router.pushController(
RouterTransaction.with(SettingsController()) RouterTransaction.with(SettingsController())
.pushChangeHandler( .pushChangeHandler(
TransitionChangeHandlerCompat( TransitionChangeHandlerCompat(
SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler() SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler()
) )
) )
.popChangeHandler( .popChangeHandler(
TransitionChangeHandlerCompat( TransitionChangeHandlerCompat(
SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler() SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler()
) )
) )
) )
} }
viewModel.apply { viewModel.apply {
avatar.observe(this@ConversationsListView) { avatar -> avatar.observe(this@ConversationsListView) {
activity?.settingsButton?.imageTintList = null with(activity) {
activity?.settingsButton?.setImageDrawable(avatar) this?.settingsButton?.imageTintList = null
this?.settingsButton?.setImageDrawable(it)
}
} }
filterLiveData.observe(this@ConversationsListView) { query -> filterLiveData.observe(this@ConversationsListView) { query ->
if (!transitionInProgress) { if (!transitionInProgress) {
activity?.settingsButton?.isVisible = query.isNullOrEmpty() with(activity) {
activity?.clearButton?.isVisible = !query.isNullOrEmpty() this?.settingsButton?.isVisible = query.isNullOrEmpty()
this?.clearButton?.isVisible = !query.isNullOrEmpty()
}
} }
} }
} }
@ -149,9 +153,7 @@ class ConversationsListView : BaseView() {
appBar?.isVisible = true appBar?.isVisible = true
} }
private fun setSearchQuery(query: CharSequence?) { private fun setSearchQuery(query: CharSequence?) = viewModel.filterLiveData.postValue(query)
viewModel.filterLiveData.postValue(query)
}
private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element<Conversation>) { private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element<Conversation>) {
val conversation = element.data val conversation = element.data
@ -160,16 +162,18 @@ class ConversationsListView : BaseView() {
user?.let { user -> user?.let { user ->
conversation?.let { conversation -> conversation?.let { conversation ->
val bundle = Bundle() val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user) with(bundle) {
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversation.token) putParcelable(BundleKeys.KEY_USER_ENTITY, user)
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId) putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversation.token)
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation)) putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId)
putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation))
}
ConductorRemapping.remapChatController( ConductorRemapping.remapChatController(
router, user.id, conversation.token!!, router, user.id, conversation.token!!,
bundle, false bundle, false
) )
} }
} }
} }
@ -182,7 +186,7 @@ class ConversationsListView : BaseView() {
cornerRadius(res = R.dimen.corner_radius) cornerRadius(res = R.dimen.corner_radius)
title(text = conversation.displayName) title(text = conversation.displayName)
listItemsWithImage(getConversationMenuItemsForConversation(conversation)) { dialog, index, item -> listItemsWithImage(getConversationMenuItemsForConversation(conversation)) { _, _, item ->
when (item.iconRes) { when (item.iconRes) {
drawable.ic_star_border_black_24dp -> { drawable.ic_star_border_black_24dp -> {
viewModel.changeFavoriteValueForConversation(conversation, false) viewModel.changeFavoriteValueForConversation(conversation, false)
@ -211,10 +215,10 @@ class ConversationsListView : BaseView() {
} }
negativeButton(R.string.nc_cancel) negativeButton(R.string.nc_cancel)
icon( icon(
drawable = DisplayUtils.getTintedDrawable( drawable = DisplayUtils.getTintedDrawable(
resources!!, drawable resources!!, drawable
.ic_delete_grey600_24dp, R.color.nc_darkRed .ic_delete_grey600_24dp, R.color.nc_darkRed
) )
) )
} }
} }
@ -227,78 +231,69 @@ class ConversationsListView : BaseView() {
} }
} }
override fun getLayoutId(): Int { override fun getLayoutId(): Int = R.layout.conversations_list_view
return R.layout.conversations_list_view
}
private fun openNewConversationScreen() { private fun openNewConversationScreen() =
router.pushController( router.pushController(
RouterTransaction.with(ContactsView()) RouterTransaction.with(ContactsView())
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
} )
private fun getConversationMenuItemsForConversation(conversation: Conversation): MutableList<BasicListItemWithImage> { private fun getConversationMenuItemsForConversation(conversation: Conversation): MutableList<BasicListItemWithImage> {
val items = mutableListOf<BasicListItemWithImage>() val items = mutableListOf<BasicListItemWithImage>()
if (conversation.favorite) { if (conversation.favorite) {
items.add( items.add(
BasicListItemWithImage( BasicListItemWithImage(
drawable.ic_star_border_black_24dp, drawable.ic_star_border_black_24dp,
context.getString(R.string.nc_remove_from_favorites) context.getString(R.string.nc_remove_from_favorites)
) )
) )
} else { } else {
items.add( items.add(
BasicListItemWithImage( BasicListItemWithImage(
drawable.ic_star_black_24dp, drawable.ic_star_black_24dp,
context.getString(R.string.nc_add_to_favorites) context.getString(R.string.nc_add_to_favorites)
) )
) )
} }
if (conversation.isPublic) { if (conversation.isPublic) {
items.add( items.add(
(BasicListItemWithImage( (BasicListItemWithImage(
drawable drawable
.ic_share_black_24dp, context.getString(R.string.nc_share_link) .ic_share_black_24dp, context.getString(R.string.nc_share_link)
)) ))
) )
} }
if (conversation.canLeave(viewModel.globalService.currentUserLiveData.value!!.toUser())) { if (conversation.canLeave(viewModel.globalService.currentUserLiveData.value!!.toUser())) {
items.add( items.add(
BasicListItemWithImage( BasicListItemWithImage(
drawable.ic_exit_to_app_black_24dp, context.getString drawable.ic_exit_to_app_black_24dp, context.getString
(R.string.nc_leave) (R.string.nc_leave)
) )
) )
} }
if (conversation.canModerate(viewModel.globalService.currentUserLiveData.value!!.toUser())) { if (conversation.canModerate(viewModel.globalService.currentUserLiveData.value!!.toUser())) {
items.add( items.add(
BasicListItemWithImage( BasicListItemWithImage(
drawable.ic_delete_grey600_24dp, context.getString( drawable.ic_delete_grey600_24dp, context.getString(
R.string.nc_delete_call R.string.nc_delete_call
) )
) )
) )
} }
return items return items
} }
override fun onFloatingActionButtonClick() { override fun onFloatingActionButtonClick() = openNewConversationScreen()
openNewConversationScreen()
}
override fun getAppBarLayoutType(): AppBarLayoutType { override fun getAppBarLayoutType(): AppBarLayoutType = AppBarLayoutType.SEARCH_BAR
return AppBarLayoutType.SEARCH_BAR
}
override fun getTitle(): String? { override fun getTitle(): String? = resources?.getString(R.string.nc_search_conversations)
return resources?.getString(R.string.nc_search_conversations)
}
override fun onRestoreViewState(view: View, savedViewState: Bundle) { override fun onRestoreViewState(view: View, savedViewState: Bundle) {
super.onRestoreViewState(view, savedViewState) super.onRestoreViewState(view, savedViewState)

View File

@ -20,7 +20,7 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
import android.app.Application import android.app.Application
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@ -49,7 +49,7 @@ import org.koin.core.parameter.parametersOf
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
class ConversationsListViewModel constructor( class ConversationsListViewModel (
application: Application, application: Application,
private val getConversationsUseCase: GetConversationsUseCase, private val getConversationsUseCase: GetConversationsUseCase,
private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase,

View File

@ -20,7 +20,7 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist package com.nextcloud.talk.newarch.features.conversationsList
enum class ConversationsListViewNetworkState { enum class ConversationsListViewNetworkState {
LOADING, LOADING,

View File

@ -20,15 +20,17 @@
* *
*/ */
package com.nextcloud.talk.newarch.features.conversationslist.di.module package com.nextcloud.talk.newarch.features.conversationsList.di.module
import android.app.Application import android.app.Application
import android.os.CountDownTimer
import android.util.Log
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase
import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase
import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase
import com.nextcloud.talk.newarch.features.conversationslist.ConversationListViewModelFactory import com.nextcloud.talk.newarch.features.conversationsList.ConversationListViewModelFactory
import com.nextcloud.talk.newarch.services.GlobalService import com.nextcloud.talk.newarch.services.GlobalService
import org.koin.android.ext.koin.androidApplication import org.koin.android.ext.koin.androidApplication
import org.koin.dsl.module import org.koin.dsl.module

View File

@ -18,8 +18,10 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/generic_rv_layout" android:id="@+id/generic_rv_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -32,6 +34,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"