From c10b3d4a75038c50f1a78d1b2cd8b7f189c95e7d Mon Sep 17 00:00:00 2001
From: Ryan Harg <3821-ryan_harg@users.noreply.dev.funkwhale.audio>
Date: Tue, 10 Jan 2023 12:56:20 +0000
Subject: [PATCH] Keep the player always on top
---
.gitignore | 2 +-
app/build.gradle.kts | 17 +-
app/src/main/AndroidManifest.xml | 4 -
.../funkwhale/ffa/activities/MainActivity.kt | 108 +++-----
.../ffa/activities/SearchActivity.kt | 195 -------------
.../funkwhale/ffa/adapters/SearchAdapter.kt | 258 ++++++++++--------
.../funkwhale/ffa/fragments/AlbumsFragment.kt | 88 +-----
.../ffa/fragments/AlbumsGridFragment.kt | 31 +--
.../ffa/fragments/ArtistsFragment.kt | 54 +---
.../funkwhale/ffa/fragments/BrowseFragment.kt | 14 +-
.../ffa/fragments/PlaylistTracksFragment.kt | 41 +--
.../ffa/fragments/PlaylistsFragment.kt | 31 +--
.../funkwhale/ffa/fragments/SearchFragment.kt | 136 +++++++++
.../funkwhale/ffa/fragments/TracksFragment.kt | 38 +--
.../java/audio/funkwhale/ffa/model/Album.kt | 9 +-
.../java/audio/funkwhale/ffa/model/Artist.kt | 9 +-
.../audio/funkwhale/ffa/model/CoverUrls.kt | 6 +-
.../java/audio/funkwhale/ffa/model/Covers.kt | 6 +-
.../audio/funkwhale/ffa/model/Playlist.kt | 6 +-
.../java/audio/funkwhale/ffa/model/Track.kt | 16 +-
.../audio/funkwhale/ffa/utils/Extensions.kt | 62 ++++-
.../ffa/viewmodel/SearchViewModel.kt | 118 ++++++++
app/src/main/res/anim/delayed_fade_out.xml | 9 +
app/src/main/res/anim/fade_in.xml | 8 +
app/src/main/res/anim/fade_out.xml | 8 +
app/src/main/res/anim/none.xml | 7 +
app/src/main/res/anim/slide_down.xml | 9 +
app/src/main/res/anim/slide_up.xml | 9 +
.../main/res/layout-land/activity_main.xml | 17 +-
app/src/main/res/layout/activity_main.xml | 7 +-
app/src/main/res/layout/activity_search.xml | 100 -------
app/src/main/res/layout/fragment_search.xml | 96 +++++++
app/src/main/res/navigation/main_nav.xml | 103 +++++++
app/src/main/res/values/config.xml | 4 +
app/src/main/res/values/strings.xml | 3 +
build.gradle.kts | 10 +-
changes/changelog.d/107.bugfix | 1 +
37 files changed, 857 insertions(+), 783 deletions(-)
delete mode 100644 app/src/main/java/audio/funkwhale/ffa/activities/SearchActivity.kt
create mode 100644 app/src/main/java/audio/funkwhale/ffa/fragments/SearchFragment.kt
create mode 100644 app/src/main/java/audio/funkwhale/ffa/viewmodel/SearchViewModel.kt
create mode 100644 app/src/main/res/anim/delayed_fade_out.xml
create mode 100644 app/src/main/res/anim/fade_in.xml
create mode 100644 app/src/main/res/anim/fade_out.xml
create mode 100644 app/src/main/res/anim/none.xml
create mode 100644 app/src/main/res/anim/slide_down.xml
create mode 100644 app/src/main/res/anim/slide_up.xml
delete mode 100644 app/src/main/res/layout/activity_search.xml
create mode 100644 app/src/main/res/layout/fragment_search.xml
create mode 100644 app/src/main/res/navigation/main_nav.xml
create mode 100644 app/src/main/res/values/config.xml
create mode 100644 changes/changelog.d/107.bugfix
diff --git a/.gitignore b/.gitignore
index 26ed3fc..db0d46a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
*.iml
-**.gradle
+**/.gradle
/local.properties
/.idea
.DS_Store
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c198c16..5037a63 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,12 +5,15 @@ import java.util.Properties
plugins {
id("com.android.application")
id("kotlin-android")
+ id("androidx.navigation.safeargs.kotlin")
+ id("kotlin-parcelize")
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
id("com.gladed.androidgitversion") version "0.4.14"
id("com.github.triplet.play") version "3.7.0"
id("de.mobilej.unmock")
id("com.github.ben-manes.versions")
+ id("org.jetbrains.kotlin.android")
jacoco
}
@@ -48,6 +51,7 @@ android {
buildFeatures {
viewBinding = true
+ dataBinding = true
}
packagingOptions {
@@ -158,6 +162,9 @@ play {
}
dependencies {
+ val navVersion: String by rootProject.extra
+ val lifecycleVersion: String by rootProject.extra
+
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0")
@@ -166,7 +173,8 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.core:core-ktx:1.9.0")
- implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")
+ implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
@@ -189,7 +197,7 @@ dependencies {
isTransitive = false
}
- implementation("com.aliassadi:power-preference-lib:2.0.0")
+ implementation("com.github.AliAsadi:PowerPreference:2.1.0")
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")
@@ -199,6 +207,10 @@ dependencies {
implementation("jp.wasabeef:picasso-transformations:2.4.0")
implementation("net.openid:appauth:0.11.1")
+ implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")
+ implementation("androidx.navigation:navigation-ui-ktx:$navVersion")
+ implementation("androidx.navigation:navigation-dynamic-features-fragment:$navVersion")
+
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.3")
testImplementation("androidx.test:core:1.5.0")
@@ -206,6 +218,7 @@ dependencies {
testImplementation("org.robolectric:robolectric:4.9.2")
androidTestImplementation("io.mockk:mockk-android:1.13.3")
+ androidTestImplementation("androidx.navigation:navigation-testing:$navVersion")
}
project.afterEvaluate {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3dd0c98..63ffef8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -44,10 +44,6 @@
android:name=".activities.MainActivity"
android:screenOrientation="portrait" />
-
-
diff --git a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt
index 6feffda..7647e96 100644
--- a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt
+++ b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt
@@ -16,23 +16,23 @@ import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.SeekBar
+import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
+import androidx.navigation.NavController
+import androidx.navigation.fragment.NavHostFragment
import audio.funkwhale.ffa.FFA
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.ActivityMainBinding
import audio.funkwhale.ffa.fragments.AddToPlaylistDialog
-import audio.funkwhale.ffa.fragments.AlbumsFragment
-import audio.funkwhale.ffa.fragments.ArtistsFragment
-import audio.funkwhale.ffa.fragments.BrowseFragment
+import audio.funkwhale.ffa.fragments.BrowseFragmentDirections
import audio.funkwhale.ffa.fragments.LandscapeQueueFragment
import audio.funkwhale.ffa.fragments.QueueFragment
import audio.funkwhale.ffa.fragments.TrackInfoDetailsFragment
@@ -89,40 +89,47 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val oAuth: OAuth by inject(OAuth::class.java)
+ private val navigation: NavController by lazy {
+ val navHost = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
+ navHost.navController
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
AppContext.init(this)
-
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.appbar)
+ onBackPressedDispatcher.addCallback(this) {
+ if (binding.nowPlaying.isOpened()) {
+ binding.nowPlaying.close()
+ } else {
+ navigation.navigateUp()
+ }
+ }
+
when (intent.action) {
MediaControlsManager.NOTIFICATION_ACTION_OPEN_QUEUE.toString() -> launchDialog(QueueFragment())
}
- supportFragmentManager
- .beginTransaction()
- .replace(R.id.container, BrowseFragment())
- .commit()
-
watchEventBus()
}
override fun onResume() {
super.onResume()
- (binding.container as? DisableableFrameLayout)?.setShouldRegisterTouch { _ ->
- if (binding.nowPlaying.isOpened()) {
- binding.nowPlaying.close()
-
- return@setShouldRegisterTouch false
+ findViewById(R.id.container)?.apply {
+ setShouldRegisterTouch {
+ if (binding.nowPlaying.isOpened()) {
+ binding.nowPlaying.close()
+ false
+ } else {
+ true
+ }
}
-
- true
}
favoritedRepository.update(this, lifecycleScope)
@@ -178,15 +185,6 @@ class MainActivity : AppCompatActivity() {
}
}
- override fun onBackPressed() {
- if (binding.nowPlaying.isOpened()) {
- binding.nowPlaying.close()
- return
- }
-
- super.onBackPressed()
- }
-
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
this.menu = menu
@@ -226,18 +224,11 @@ class MainActivity : AppCompatActivity() {
when (item.itemId) {
android.R.id.home -> {
binding.nowPlaying.close()
-
- (supportFragmentManager.fragments.last() as? BrowseFragment)?.let {
- it.selectTabAt(0)
-
- return true
- }
-
- launchFragment(BrowseFragment())
+ navigation.popBackStack(R.id.browseFragment, false)
}
R.id.nav_queue -> launchDialog(QueueFragment())
- R.id.nav_search -> startActivity(Intent(this, SearchActivity::class.java))
+ R.id.nav_search -> navigation.navigate(BrowseFragmentDirections.browseToSearch())
R.id.nav_all_music, R.id.nav_my_music, R.id.nav_followed -> {
menu?.let { menu ->
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW)
@@ -300,26 +291,8 @@ class MainActivity : AppCompatActivity() {
return true
}
- private fun launchFragment(fragment: Fragment) {
- supportFragmentManager.fragments.lastOrNull()?.also { oldFragment ->
- oldFragment.enterTransition = null
- oldFragment.exitTransition = null
-
- supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
- }
-
- supportFragmentManager
- .beginTransaction()
- .setCustomAnimations(0, 0, 0, 0)
- .replace(R.id.container, fragment)
- .commit()
- }
-
- private fun launchDialog(fragment: DialogFragment) {
- supportFragmentManager.beginTransaction().let {
- fragment.show(it, "")
- }
- }
+ private fun launchDialog(fragment: DialogFragment) =
+ fragment.show(supportFragmentManager.beginTransaction(), "")
@SuppressLint("NewApi")
private fun watchEventBus() {
@@ -343,7 +316,7 @@ class MainActivity : AppCompatActivity() {
}
} else if (event is Event.PlaybackStopped) {
if (binding.nowPlaying.visibility == View.VISIBLE) {
- (binding.container.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
+ (binding.navHostFragment.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
it.bottomMargin = it.bottomMargin / 2
}
@@ -368,15 +341,17 @@ class MainActivity : AppCompatActivity() {
} else if (event is Event.StateChanged) {
when (event.playing) {
true -> {
- binding.nowPlayingContainer?.nowPlayingToggle?.icon = getDrawable(R.drawable.pause)
+ binding.nowPlayingContainer?.nowPlayingToggle?.icon =
+ AppCompatResources.getDrawable(this@MainActivity, R.drawable.pause)
binding.nowPlayingContainer?.nowPlayingDetailsToggle?.icon =
- getDrawable(R.drawable.pause)
+ AppCompatResources.getDrawable(this@MainActivity, R.drawable.pause)
}
false -> {
- binding.nowPlayingContainer?.nowPlayingToggle?.icon = getDrawable(R.drawable.play)
+ binding.nowPlayingContainer?.nowPlayingToggle?.icon =
+ AppCompatResources.getDrawable(this@MainActivity, R.drawable.play)
binding.nowPlayingContainer?.nowPlayingDetailsToggle?.icon =
- getDrawable(R.drawable.play)
+ AppCompatResources.getDrawable(this@MainActivity, R.drawable.play)
}
}
} else if (event is Event.QueueChanged) {
@@ -459,7 +434,7 @@ class MainActivity : AppCompatActivity() {
.setListener(null)
.start()
- (binding.container.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
+ (binding.navHostFragment?.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
it.bottomMargin = it.bottomMargin * 2
}
@@ -534,12 +509,11 @@ class MainActivity : AppCompatActivity() {
setOnMenuItemClickListener {
when (it.itemId) {
- R.id.track_info_artist -> ArtistsFragment.openAlbums(
- this@MainActivity,
+ R.id.track_info_artist -> BrowseFragmentDirections.browseToAlbums(
track.artist,
- art = track.album?.cover()
+ track.album?.cover()
)
- R.id.track_info_album -> AlbumsFragment.openTracks(this@MainActivity, track.album)
+ R.id.track_info_album -> track.album?.let(BrowseFragmentDirections::browseToTracks)
R.id.track_info_details -> TrackInfoDetailsFragment.new(track)
.show(supportFragmentManager, "dialog")
}
diff --git a/app/src/main/java/audio/funkwhale/ffa/activities/SearchActivity.kt b/app/src/main/java/audio/funkwhale/ffa/activities/SearchActivity.kt
deleted file mode 100644
index d5ac646..0000000
--- a/app/src/main/java/audio/funkwhale/ffa/activities/SearchActivity.kt
+++ /dev/null
@@ -1,195 +0,0 @@
-package audio.funkwhale.ffa.activities
-
-import android.os.Bundle
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.SearchView
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.LinearLayoutManager
-import audio.funkwhale.ffa.adapters.FavoriteListener
-import audio.funkwhale.ffa.adapters.SearchAdapter
-import audio.funkwhale.ffa.databinding.ActivitySearchBinding
-import audio.funkwhale.ffa.fragments.AddToPlaylistDialog
-import audio.funkwhale.ffa.fragments.AlbumsFragment
-import audio.funkwhale.ffa.fragments.ArtistsFragment
-import audio.funkwhale.ffa.model.Album
-import audio.funkwhale.ffa.model.Artist
-import audio.funkwhale.ffa.repositories.AlbumsSearchRepository
-import audio.funkwhale.ffa.repositories.ArtistsSearchRepository
-import audio.funkwhale.ffa.repositories.FavoritesRepository
-import audio.funkwhale.ffa.repositories.Repository
-import audio.funkwhale.ffa.repositories.TracksSearchRepository
-import audio.funkwhale.ffa.utils.Command
-import audio.funkwhale.ffa.utils.CommandBus
-import audio.funkwhale.ffa.utils.Event
-import audio.funkwhale.ffa.utils.EventBus
-import audio.funkwhale.ffa.utils.getMetadata
-import audio.funkwhale.ffa.utils.untilNetwork
-import com.google.android.exoplayer2.offline.Download
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import java.net.URLEncoder
-import java.util.Locale
-
-class SearchActivity : AppCompatActivity() {
- private lateinit var adapter: SearchAdapter
-
- private lateinit var artistsRepository: ArtistsSearchRepository
- private lateinit var albumsRepository: AlbumsSearchRepository
- private lateinit var tracksRepository: TracksSearchRepository
- private lateinit var favoritesRepository: FavoritesRepository
- private lateinit var binding: ActivitySearchBinding
-
- var done = 0
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- artistsRepository = ArtistsSearchRepository(this@SearchActivity, "")
- albumsRepository = AlbumsSearchRepository(this@SearchActivity, "")
- tracksRepository = TracksSearchRepository(this@SearchActivity, "")
- favoritesRepository = FavoritesRepository(this@SearchActivity)
-
- binding = ActivitySearchBinding.inflate(layoutInflater)
-
- setContentView(binding.root)
-
- binding.search.requestFocus()
- }
-
- override fun onResume() {
- super.onResume()
-
- lifecycleScope.launch(Dispatchers.Main) {
- CommandBus.get().collect { command ->
- if (command is Command.AddToPlaylist) {
-
- if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
- AddToPlaylistDialog.show(
- layoutInflater,
- this@SearchActivity,
- lifecycleScope,
- command.tracks
- )
- }
- }
- }
- }
-
- lifecycleScope.launch(Dispatchers.IO) {
- EventBus.get().collect { event ->
- if (event is Event.DownloadChanged) refreshDownloadedTrack(event.download)
- }
- }
-
- adapter =
- SearchAdapter(
- layoutInflater,
- this,
- SearchResultClickListener(),
- FavoriteListener(favoritesRepository)
- ).also {
- binding.results.layoutManager = LinearLayoutManager(this)
- binding.results.adapter = it
- }
-
- binding.search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
-
- override fun onQueryTextSubmit(rawQuery: String?): Boolean {
- binding.search.clearFocus()
-
- rawQuery?.let {
- done = 0
-
- val query = URLEncoder.encode(it, "UTF-8")
-
- artistsRepository.query = query.lowercase(Locale.ROOT)
- albumsRepository.query = query.lowercase(Locale.ROOT)
- tracksRepository.query = query.lowercase(Locale.ROOT)
-
- binding.searchSpinner.visibility = View.VISIBLE
- binding.searchEmpty.visibility = View.GONE
- binding.searchNoResults.visibility = View.GONE
-
- adapter.artists.clear()
- adapter.albums.clear()
- adapter.tracks.clear()
- adapter.notifyDataSetChanged()
-
- artistsRepository.fetch(Repository.Origin.Network.origin)
- .untilNetwork(lifecycleScope) { artists, _, _, _ ->
- done++
-
- adapter.artists.addAll(artists)
- refresh()
- }
-
- albumsRepository.fetch(Repository.Origin.Network.origin)
- .untilNetwork(lifecycleScope) { albums, _, _, _ ->
- done++
-
- adapter.albums.addAll(albums)
- refresh()
- }
-
- tracksRepository.fetch(Repository.Origin.Network.origin)
- .untilNetwork(lifecycleScope) { tracks, _, _, _ ->
- done++
-
- adapter.tracks.addAll(tracks)
- refresh()
- }
- }
-
- return true
- }
-
- override fun onQueryTextChange(newText: String?) = true
- })
- }
-
- private fun refresh() {
- adapter.notifyDataSetChanged()
-
- if (adapter.artists.size + adapter.albums.size + adapter.tracks.size == 0) {
- binding.searchNoResults.visibility = View.VISIBLE
- } else {
- binding.searchNoResults.visibility = View.GONE
- }
-
- if (done == 3) {
- binding.searchSpinner.visibility = View.INVISIBLE
- }
- }
-
- private suspend fun refreshDownloadedTrack(download: Download) {
- if (download.state == Download.STATE_COMPLETED) {
- download.getMetadata()?.let { info ->
- adapter.tracks.withIndex().associate { it.value to it.index }
- .filter { it.key.id == info.id }.toList().getOrNull(0)?.let { match ->
- withContext(Dispatchers.Main) {
- adapter.tracks[match.second].downloaded = true
- adapter.notifyItemChanged(
- adapter.getPositionOf(
- SearchAdapter.ResultType.Track,
- match.second
- )
- )
- }
- }
- }
- }
- }
-
- inner class SearchResultClickListener : SearchAdapter.OnSearchResultClickListener {
- override fun onArtistClick(holder: View?, artist: Artist) {
- ArtistsFragment.openAlbums(this@SearchActivity, artist)
- }
-
- override fun onAlbumClick(holder: View?, album: Album) {
- AlbumsFragment.openTracks(this@SearchActivity, album)
- }
- }
-}
diff --git a/app/src/main/java/audio/funkwhale/ffa/adapters/SearchAdapter.kt b/app/src/main/java/audio/funkwhale/ffa/adapters/SearchAdapter.kt
index 7d8c987..9710ef9 100644
--- a/app/src/main/java/audio/funkwhale/ffa/adapters/SearchAdapter.kt
+++ b/app/src/main/java/audio/funkwhale/ffa/adapters/SearchAdapter.kt
@@ -7,10 +7,10 @@ import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.os.Build
import android.view.Gravity
-import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
+import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowSearchHeaderBinding
@@ -24,11 +24,13 @@ import audio.funkwhale.ffa.utils.CoverArt
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.onApi
import audio.funkwhale.ffa.utils.toast
+import audio.funkwhale.ffa.viewmodel.SearchViewModel
+import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
class SearchAdapter(
- private val layoutInflater: LayoutInflater,
- private val context: Context?,
+ viewModel: SearchViewModel,
+ private val fragment: Fragment,
private val listener: OnSearchResultClickListener,
private val favoriteListener: FavoriteListener
) : RecyclerView.Adapter() {
@@ -50,12 +52,27 @@ class SearchAdapter(
val sectionCount = 3
- var artists: MutableList = mutableListOf()
- var albums: MutableList = mutableListOf()
- var tracks: MutableList