diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
index 7030a611c..cb494ef61 100644
--- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
+++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
@@ -35,6 +35,7 @@ import org.oxycblt.auxio.settings.SettingsManager
/**
* The single [AppCompatActivity] for Auxio.
+ * TODO: Improve edge-to-edge everywhere
*/
class MainActivity : AppCompatActivity() {
private val playbackModel: PlaybackViewModel by viewModels()
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
index 64cad21c3..56bbce306 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
@@ -18,12 +18,14 @@
package org.oxycblt.auxio.detail
+import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
+import androidx.recyclerview.widget.LinearSmoothScroller
import org.oxycblt.auxio.R
import org.oxycblt.auxio.canScroll
import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter
@@ -34,7 +36,6 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.PlaybackMode
-import org.oxycblt.auxio.recycler.CenterSmoothScroller
import org.oxycblt.auxio.showToast
import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.newMenu
@@ -136,7 +137,8 @@ class AlbumDetailFragment : DetailFragment() {
)
}
- else -> {}
+ else -> {
+ }
}
}
@@ -187,4 +189,27 @@ class AlbumDetailFragment : DetailFragment() {
}
}
}
+
+ /**
+ * [LinearSmoothScroller] subclass that centers the item on the screen instead of
+ * snapping to the top or bottom.
+ */
+ private class CenterSmoothScroller(
+ context: Context,
+ target: Int
+ ) : LinearSmoothScroller(context) {
+ init {
+ targetPosition = target
+ }
+
+ override fun calculateDtToFit(
+ viewStart: Int,
+ viewEnd: Int,
+ boxStart: Int,
+ boxEnd: Int,
+ snapPreference: Int
+ ): Int {
+ return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
+ }
+ }
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt
index 4bfea26a6..eddcc6b43 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/AlbumDetailAdapter.kt
@@ -33,9 +33,8 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
+import org.oxycblt.auxio.recycler.BaseViewHolder
import org.oxycblt.auxio.recycler.DiffCallback
-import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
-import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.setTextColorResource
/**
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt
index 4e2d59e66..605e9efe0 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistDetailAdapter.kt
@@ -37,9 +37,8 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
+import org.oxycblt.auxio.recycler.BaseViewHolder
import org.oxycblt.auxio.recycler.DiffCallback
-import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
-import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.setTextColorResource
/**
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt
index e8f345dc3..d73bd0f94 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreDetailAdapter.kt
@@ -33,9 +33,8 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
+import org.oxycblt.auxio.recycler.BaseViewHolder
import org.oxycblt.auxio.recycler.DiffCallback
-import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
-import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.setTextColorResource
/**
diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/Highlightable.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt
similarity index 94%
rename from app/src/main/java/org/oxycblt/auxio/recycler/viewholders/Highlightable.kt
rename to app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt
index 5d255419a..4701b1e51 100644
--- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/Highlightable.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/Highlightable.kt
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.recycler.viewholders
+package org.oxycblt.auxio.detail.adapters
/**
* Interface that allows the highlighting of certain ViewHolders
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeAdapter.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeAdapter.kt
index 5dee2f24f..bcb5f4781 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeAdapter.kt
@@ -27,10 +27,10 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
-import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
-import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
-import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
+import org.oxycblt.auxio.recycler.AlbumViewHolder
+import org.oxycblt.auxio.recycler.ArtistViewHolder
+import org.oxycblt.auxio.recycler.GenreViewHolder
+import org.oxycblt.auxio.recycler.SongViewHolder
class HomeAdapter(
private val doOnClick: (data: BaseModel) -> Unit,
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
index c7c260aed..2591d2ded 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
@@ -47,8 +47,7 @@ import org.oxycblt.auxio.recycler.DisplayMode
* views for each respective fragment.
* TODO: Re-add sorting (but new and improved)
* TODO: Add lift-on-scroll eventually [when I can file a bug report or hack it into working]
- * FIXME: Fix issue where for the toolbar will default to its collapsed state for basically no
- * reason
+ * FIXME: Keep the collapsed state in the ViewModel so we can make sure it stays consistent
* @author OxygenCobalt
*/
class HomeFragment : Fragment() {
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeListFragment.kt
index f43adb210..6636d44aa 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeListFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeListFragment.kt
@@ -39,6 +39,8 @@ import org.oxycblt.auxio.ui.newMenu
/*
* Fragment that contains a list of items specified by a [DisplayMode].
+ * TODO: Fix crash from not saving the display mode. This is getting really tiring.
+ * Just keep the index for the tab we're working with and then just use that w/homeModel.
*/
class HomeListFragment : Fragment() {
private val homeModel: HomeViewModel by viewModels()
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index 13b416ae0..60c10233a 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -33,12 +33,6 @@ import org.oxycblt.auxio.logD
/**
* Class that loads/constructs [Genre]s, [Artist]s, [Album]s, and [Song] objects from the filesystem
* @author OxygenCobalt
- *
- * FIXME: Here's a catalog of problems that I already know about with this abomination
- * - All loading is done at startup [Not efficent for large libraries, would require massive arch retooling to fix]
- * - Does not support the album artist tag [Nothing I can do that doesn't involve rolling my own loader]
- * - Genre system is a bottleneck [See Above]
- * Blame MediaStore, loading anything on this platform is a nightmare.
*/
class MusicLoader(private val context: Context) {
var genres = mutableListOf()
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
index 61c71a53a..7c5085ef5 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
@@ -34,9 +34,9 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
+import org.oxycblt.auxio.recycler.BaseViewHolder
import org.oxycblt.auxio.recycler.DiffCallback
-import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
-import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
+import org.oxycblt.auxio.recycler.HeaderViewHolder
/**
* The single adapter for both the Next Queue and the User Queue.
diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/BaseViewHolder.kt b/app/src/main/java/org/oxycblt/auxio/recycler/BaseViewHolder.kt
similarity index 98%
rename from app/src/main/java/org/oxycblt/auxio/recycler/viewholders/BaseViewHolder.kt
rename to app/src/main/java/org/oxycblt/auxio/recycler/BaseViewHolder.kt
index 8fa9d8f46..eea6ee3a5 100644
--- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/BaseViewHolder.kt
+++ b/app/src/main/java/org/oxycblt/auxio/recycler/BaseViewHolder.kt
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.recycler.viewholders
+package org.oxycblt.auxio.recycler
import android.view.View
import androidx.databinding.ViewDataBinding
diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/CenterSmoothScroller.kt b/app/src/main/java/org/oxycblt/auxio/recycler/CenterSmoothScroller.kt
deleted file mode 100644
index 17624e117..000000000
--- a/app/src/main/java/org/oxycblt/auxio/recycler/CenterSmoothScroller.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2021 Auxio Project
- * CenterSmoothScroller.kt is part of Auxio.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.oxycblt.auxio.recycler
-
-import android.content.Context
-import androidx.recyclerview.widget.LinearSmoothScroller
-
-/**
- * [LinearSmoothScroller] subclass that centers the item on the screen instead of snapping to the
- * top or bottom.
- * @author OxygenCobalt
- */
-class CenterSmoothScroller(context: Context, target: Int) : LinearSmoothScroller(context) {
- init {
- targetPosition = target
- }
-
- override fun calculateDtToFit(
- viewStart: Int,
- viewEnd: Int,
- boxStart: Int,
- boxEnd: Int,
- snapPreference: Int
- ): Int {
- return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
- }
-}
diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt
index 4c82c5bbc..31d8f055d 100644
--- a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt
+++ b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt
@@ -18,25 +18,22 @@
package org.oxycblt.auxio.recycler
-import androidx.annotation.DrawableRes
-import org.oxycblt.auxio.R
-
/**
* An enum for determining what items to show in a given list.
* @author OxygenCobalt
*/
-enum class DisplayMode(@DrawableRes val iconRes: Int) {
- SHOW_GENRES(R.drawable.ic_genre),
- SHOW_ARTISTS(R.drawable.ic_artist),
- SHOW_ALBUMS(R.drawable.ic_album),
- SHOW_SONGS(R.drawable.ic_song);
+enum class DisplayMode {
+ SHOW_GENRES,
+ SHOW_ARTISTS,
+ SHOW_ALBUMS,
+ SHOW_SONGS;
companion object {
- const val CONST_SHOW_ALL = 0xA107
- const val CONST_SHOW_GENRES = 0xA108
- const val CONST_SHOW_ARTISTS = 0xA109
- const val CONST_SHOW_ALBUMS = 0xA10A
- const val CONST_SHOW_SONGS = 0xA10B
+ private const val CONST_SHOW_ALL = 0xA107
+ private const val CONST_SHOW_GENRES = 0xA108
+ private const val CONST_SHOW_ARTISTS = 0xA109
+ private const val CONST_SHOW_ALBUMS = 0xA10A
+ private const val CONST_SHOW_SONGS = 0xA10B
fun toSearchInt(value: DisplayMode?): Int {
return when (value) {
diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt b/app/src/main/java/org/oxycblt/auxio/recycler/ModelHolders.kt
similarity index 99%
rename from app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt
rename to app/src/main/java/org/oxycblt/auxio/recycler/ModelHolders.kt
index 6882aeb77..7099fe6ed 100644
--- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt
+++ b/app/src/main/java/org/oxycblt/auxio/recycler/ModelHolders.kt
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.recycler.viewholders
+package org.oxycblt.auxio.recycler
import android.content.Context
import android.view.View
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
index b5201e0ac..2228eaf14 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
@@ -28,12 +28,12 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
+import org.oxycblt.auxio.recycler.AlbumViewHolder
+import org.oxycblt.auxio.recycler.ArtistViewHolder
import org.oxycblt.auxio.recycler.DiffCallback
-import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
-import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
-import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
-import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
-import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
+import org.oxycblt.auxio.recycler.GenreViewHolder
+import org.oxycblt.auxio.recycler.HeaderViewHolder
+import org.oxycblt.auxio.recycler.SongViewHolder
/**
* A Multi-ViewHolder adapter that displays the results of a search query.
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
index 447f22e67..0ada1817c 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
@@ -52,7 +52,7 @@ import org.oxycblt.auxio.ui.newMenu
* @author OxygenCobalt
*/
class SearchFragment : Fragment() {
- // SearchViewModel only scoped to this Fragment
+ // SearchViewModel is only scoped to this Fragment
private val searchModel: SearchViewModel by viewModels()
private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@@ -64,10 +64,15 @@ class SearchFragment : Fragment() {
): View {
val binding = FragmentSearchBinding.inflate(inflater)
- val searchAdapter = SearchAdapter(::onItemSelection, ::newMenu)
-
val imm = requireContext().getSystemServiceSafe(InputMethodManager::class)
+ val searchAdapter = SearchAdapter(
+ doOnClick = { item ->
+ onItemSelection(item, imm)
+ },
+ ::newMenu
+ )
+
val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams
val defaultParams = toolbarParams.scrollFlags
@@ -87,7 +92,7 @@ class SearchFragment : Fragment() {
menu.findItem(itemId).isChecked = true
setNavigationOnClickListener {
- requireView().rootView.clearFocus()
+ imm.hide()
findNavController().navigateUp()
}
@@ -158,6 +163,8 @@ class SearchFragment : Fragment() {
else -> return@observe
}
)
+
+ imm.hide()
}
logD("Fragment created.")
@@ -171,19 +178,22 @@ class SearchFragment : Fragment() {
searchModel.setNavigating(false)
}
+ private fun InputMethodManager.hide() {
+ hideSoftInputFromWindow(requireView().windowToken, InputMethodManager.HIDE_IMPLICIT_ONLY)
+ }
+
/**
* Function that handles when an [item] is selected.
* Handles all datatypes that are selectable.
*/
- private fun onItemSelection(item: BaseModel) {
+ private fun onItemSelection(item: BaseModel, imm: InputMethodManager) {
if (item is Song) {
playbackModel.playSong(item)
return
}
- // Get rid of the keyboard if we are navigating
- requireView().rootView.clearFocus()
+ imm.hide()
if (!searchModel.isNavigating) {
searchModel.setNavigating(true)
diff --git a/app/src/main/res/layout/item_action_header.xml b/app/src/main/res/layout/item_action_header.xml
index 4ba795879..fb1ebfae3 100644
--- a/app/src/main/res/layout/item_action_header.xml
+++ b/app/src/main/res/layout/item_action_header.xml
@@ -2,7 +2,7 @@
+ tools:context=".recycler.HeaderViewHolder">
diff --git a/app/src/main/res/layout/item_album.xml b/app/src/main/res/layout/item_album.xml
index d1ff4cc86..0e502143f 100644
--- a/app/src/main/res/layout/item_album.xml
+++ b/app/src/main/res/layout/item_album.xml
@@ -2,7 +2,7 @@
+ tools:context=".recycler.AlbumViewHolder">
diff --git a/app/src/main/res/layout/item_album_song.xml b/app/src/main/res/layout/item_album_song.xml
index 35c2b21e4..17e2b0f7c 100644
--- a/app/src/main/res/layout/item_album_song.xml
+++ b/app/src/main/res/layout/item_album_song.xml
@@ -16,7 +16,7 @@
+ tools:context=".recycler.ArtistViewHolder">
diff --git a/app/src/main/res/layout/item_artist_song.xml b/app/src/main/res/layout/item_artist_song.xml
index b8d22867a..1305d5519 100644
--- a/app/src/main/res/layout/item_artist_song.xml
+++ b/app/src/main/res/layout/item_artist_song.xml
@@ -2,7 +2,7 @@
+ tools:context=".recycler.SongViewHolder">
diff --git a/app/src/main/res/layout/item_genre.xml b/app/src/main/res/layout/item_genre.xml
index 7ec5daddc..fce33d841 100644
--- a/app/src/main/res/layout/item_genre.xml
+++ b/app/src/main/res/layout/item_genre.xml
@@ -2,7 +2,7 @@
+ tools:context=".recycler.GenreViewHolder">
diff --git a/app/src/main/res/layout/item_header.xml b/app/src/main/res/layout/item_header.xml
index eb12a8b98..a7d73be74 100644
--- a/app/src/main/res/layout/item_header.xml
+++ b/app/src/main/res/layout/item_header.xml
@@ -1,7 +1,7 @@
+ tools:context=".recycler.HeaderViewHolder">
diff --git a/app/src/main/res/layout/item_song.xml b/app/src/main/res/layout/item_song.xml
index d09fd4f14..62bbe5172 100644
--- a/app/src/main/res/layout/item_song.xml
+++ b/app/src/main/res/layout/item_song.xml
@@ -2,7 +2,7 @@
+ tools:context=".recycler.SongViewHolder">
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index ea3854e66..7c6509a03 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,10 +1,5 @@
-
-
8dp
16dp
@@ -13,10 +8,6 @@
48dp
128dp
-
- 32dp
- 20dp
-
48dp
64dp
@@ -32,6 +23,8 @@
20dp
24dp
+ 32dp
+
16sp
18sp