loading: improve new ui

Improve the loading UI somewhat, reverting the progress bar and
applying some padding to the icon so that it looks a bit better
on Android 12.
This commit is contained in:
OxygenCobalt 2021-08-09 19:42:01 -06:00
parent fe29e01311
commit a5f65d39a5
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 65 additions and 65 deletions

View file

@ -54,10 +54,7 @@ class LibraryFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val binding = FragmentLibraryBinding.inflate(inflater) val binding = FragmentLibraryBinding.inflate(inflater)
val libraryAdapter = LibraryAdapter(::navToDetail, ::newMenu)
val libraryAdapter = LibraryAdapter(::navToDetail) { view, data ->
newMenu(view, data)
}
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -33,6 +33,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentLoadingBinding import org.oxycblt.auxio.databinding.FragmentLoadingBinding
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.ui.isLandscape
/** /**
* Fragment that handles what to display during the loading process. * Fragment that handles what to display during the loading process.
@ -58,6 +59,10 @@ class LoadingFragment : Fragment() {
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.loadingModel = loadingModel binding.loadingModel = loadingModel
// The loading panel shouldn't fit the system window on landscape as that will cause it
// to be mis-aligned with the Auxio icon.
binding.loadingPanel.fitsSystemWindows = !isLandscape(resources)
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
loadingModel.doGrant.observe(viewLifecycleOwner) { doGrant -> loadingModel.doGrant.observe(viewLifecycleOwner) { doGrant ->
@ -141,7 +146,6 @@ class LoadingFragment : Fragment() {
*/ */
private fun showLoading(binding: FragmentLoadingBinding) { private fun showLoading(binding: FragmentLoadingBinding) {
binding.apply { binding.apply {
loadingErrorIcon.visibility = View.INVISIBLE
loadingErrorText.visibility = View.INVISIBLE loadingErrorText.visibility = View.INVISIBLE
loadingActionButton.visibility = View.INVISIBLE loadingActionButton.visibility = View.INVISIBLE
loadingCircle.visibility = View.VISIBLE loadingCircle.visibility = View.VISIBLE
@ -155,7 +159,6 @@ class LoadingFragment : Fragment() {
*/ */
private fun showError(binding: FragmentLoadingBinding, error: MusicStore.Response) { private fun showError(binding: FragmentLoadingBinding, error: MusicStore.Response) {
binding.loadingCircle.visibility = View.GONE binding.loadingCircle.visibility = View.GONE
binding.loadingErrorIcon.visibility = View.VISIBLE
binding.loadingErrorText.visibility = View.VISIBLE binding.loadingErrorText.visibility = View.VISIBLE
binding.loadingActionButton.visibility = View.VISIBLE binding.loadingActionButton.visibility = View.VISIBLE

View file

@ -32,16 +32,34 @@ sealed class BaseModel {
abstract val name: String abstract val name: String
} }
/**
* Provides a versatile static hash for a music item that will not change when
* MediaStore changes.
*
* The reason why this is used is down a couple of reasons:
* - MediaStore will refresh the unique ID of a piece of media whenever the library
* changes, which creates bad UX
* - Using song names makes collisions too common to be reliable
* - Hashing into an integer makes databases both smaller and more efficent
*
* This does lock me into a "Load everything at once, lol" architecture for Auxio, but I
* think its worth it.
*
* @property hash A unique-ish hash for this media item
*
* TODO: Make this hash stronger
*/
sealed interface Hashable {
val hash: Int
}
/** /**
* [BaseModel] variant that denotes that this object is a parent of other data objects, such * [BaseModel] variant that denotes that this object is a parent of other data objects, such
* as an [Album] or [Artist] * as an [Album] or [Artist]
* @property hash A versatile, unique(ish) hash used for databases
* @property displayName Name that handles the usage of [Genre.resolvedName] * @property displayName Name that handles the usage of [Genre.resolvedName]
* and the normal [BaseModel.name] * and the normal [BaseModel.name]
*/ */
sealed class Parent : BaseModel() { sealed class Parent : BaseModel(), Hashable {
abstract val hash: Int
val displayName: String get() = if (this is Genre) { val displayName: String get() = if (this is Genre) {
resolvedName resolvedName
} else { } else {
@ -61,7 +79,6 @@ sealed class Parent : BaseModel() {
* These are not ensured to be linked due to possible quirks in the genre loading system. * These are not ensured to be linked due to possible quirks in the genre loading system.
* @property seconds The Song's duration in seconds * @property seconds The Song's duration in seconds
* @property formattedDuration The Song's duration as a duration string. * @property formattedDuration The Song's duration as a duration string.
* @property hash A versatile, unique(ish) hash used for databases
*/ */
data class Song( data class Song(
override val id: Long, override val id: Long,
@ -70,7 +87,7 @@ data class Song(
val albumId: Long, val albumId: Long,
val track: Int, val track: Int,
val duration: Long val duration: Long
) : BaseModel() { ) : BaseModel(), Hashable {
private var mAlbum: Album? = null private var mAlbum: Album? = null
private var mGenre: Genre? = null private var mGenre: Genre? = null
@ -80,7 +97,12 @@ data class Song(
val seconds = duration / 1000 val seconds = duration / 1000
val formattedDuration = seconds.toDuration() val formattedDuration = seconds.toDuration()
val hash = songHash() override val hash: Int get() {
var result = name.hashCode()
result = 31 * result + track
result = 31 * result + duration.hashCode()
return result
}
fun linkAlbum(album: Album) { fun linkAlbum(album: Album) {
if (mAlbum == null) { if (mAlbum == null) {
@ -93,13 +115,6 @@ data class Song(
mGenre = genre mGenre = genre
} }
} }
private fun songHash(): Int {
var result = name.hashCode()
result = 31 * result + track
result = 31 * result + duration.hashCode()
return result
}
} }
/** /**
@ -127,7 +142,12 @@ data class Album(
val totalDuration: String get() = val totalDuration: String get() =
songs.sumOf { it.seconds }.toDuration() songs.sumOf { it.seconds }.toDuration()
override val hash = albumHash() override val hash: Int get() {
var result = name.hashCode()
result = 31 * result + artistName.hashCode()
result = 31 * result + year
return result
}
fun linkArtist(artist: Artist) { fun linkArtist(artist: Artist) {
mArtist = artist mArtist = artist
@ -139,13 +159,6 @@ data class Album(
mSongs.add(song) mSongs.add(song)
} }
} }
private fun albumHash(): Int {
var result = name.hashCode()
result = 31 * result + artistName.hashCode()
result = 31 * result + year
return result
}
} }
/** /**

View file

@ -60,9 +60,7 @@ class SearchFragment : Fragment() {
): View { ): View {
val binding = FragmentSearchBinding.inflate(inflater) val binding = FragmentSearchBinding.inflate(inflater)
val searchAdapter = SearchAdapter(::onItemSelection) { view, data -> val searchAdapter = SearchAdapter(::onItemSelection, ::newMenu)
newMenu(view, data)
}
val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams val toolbarParams = binding.searchToolbar.layoutParams as AppBarLayout.LayoutParams
val defaultParams = toolbarParams.scrollFlags val defaultParams = toolbarParams.scrollFlags

View file

@ -49,9 +49,7 @@ class SongsFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val binding = FragmentSongsBinding.inflate(inflater) val binding = FragmentSongsBinding.inflate(inflater)
val songAdapter = SongsAdapter(musicStore.songs, playbackModel::playSong) { view, data -> val songAdapter = SongsAdapter(musicStore.songs, playbackModel::playSong, ::newMenu)
newMenu(view, data)
}
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -16,73 +16,61 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<!-- TODO: Get this splash icon working with Android 12 splashes --> <!--
TODO: Get this to line up with Android 12's splash screen when I can figure
out what they do
-->
<ImageView <ImageView
android:id="@+id/loading_splash" android:id="@+id/loading_splash"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="@string/desc_auxio_icon" android:contentDescription="@string/desc_auxio_icon"
android:scaleType="fitCenter" android:padding="@dimen/spacing_huge"
android:src="@drawable/ic_launcher_foreground" /> android:src="@drawable/ic_launcher_foreground" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout" android:id="@+id/loading_panel"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_large" android:layout_margin="@dimen/spacing_large"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:fitsSystemWindows="true"
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.progressindicator.LinearProgressIndicator <ProgressBar
android:id="@+id/loading_circle" android:id="@+id/loading_circle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="@+id/loading_action_button" app:layout_constraintBottom_toBottomOf="@+id/loading_action_button"
app:layout_constraintTop_toTopOf="@+id/loading_error_icon" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/loading_error_text" android:id="@+id/loading_error_text"
android:fontFamily="@font/inter_semibold"
android:textSize="@dimen/text_size_mid_large"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_small" android:layout_marginBottom="@dimen/spacing_small"
android:fontFamily="@font/inter_semibold"
android:textAlignment="center" android:textAlignment="center"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_mid_large"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@+id/loading_action_button" app:layout_constraintBottom_toTopOf="@+id/loading_action_button"
tools:text="No Music Found" /> tools:text="No Music Found" />
<ImageView
android:id="@+id/loading_error_icon"
android:layout_width="@dimen/size_error_icon"
android:layout_height="@dimen/size_error_icon"
android:layout_marginBottom="@dimen/spacing_small"
android:contentDescription="@string/desc_error"
android:src="@drawable/ic_error"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/loading_error_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/loading_action_button" android:id="@+id/loading_action_button"
style="@style/Widget.Button.Vibrant.Primary" style="@style/Widget.Button.Vibrant.Primary"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_mid_large" android:paddingStart="@dimen/spacing_insane"
android:layout_marginEnd="@dimen/spacing_mid_large" android:paddingEnd="@dimen/spacing_insane"
android:text="@string/lbl_retry" android:text="@string/lbl_retry"
android:visibility="gone" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" />
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> </FrameLayout>

View file

@ -8,6 +8,7 @@
<dimen name="spacing_mid_large">24dp</dimen> <dimen name="spacing_mid_large">24dp</dimen>
<dimen name="spacing_large">32dp</dimen> <dimen name="spacing_large">32dp</dimen>
<dimen name="spacing_mid_huge">48dp</dimen> <dimen name="spacing_mid_huge">48dp</dimen>
<dimen name="spacing_huge">64dp</dimen>
<dimen name="spacing_insane">128dp</dimen> <dimen name="spacing_insane">128dp</dimen>
<!-- Height Namespace | Height for UI elements --> <!-- Height Namespace | Height for UI elements -->

View file

@ -2,7 +2,6 @@
<resources> <resources>
<!-- COMPONENT-SPECIFIC STYLES. NOT RE-USABLE. --> <!-- COMPONENT-SPECIFIC STYLES. NOT RE-USABLE. -->
<!-- Style for the play/pause circle button -->
<style name="Widget.Component.Playback.PlayPause" parent=""> <style name="Widget.Component.Playback.PlayPause" parent="">
<item name="android:layout_height">@dimen/size_play_pause</item> <item name="android:layout_height">@dimen/size_play_pause</item>
<item name="android:layout_width">@dimen/size_play_pause</item> <item name="android:layout_width">@dimen/size_play_pause</item>
@ -18,7 +17,6 @@
<item name="android:padding">@dimen/spacing_medium</item> <item name="android:padding">@dimen/spacing_medium</item>
</style> </style>
<!-- Style for text used in widgets -->
<style name="Widget.Component.AppWidget.TextView" parent="Widget.AppCompat.TextView"> <style name="Widget.Component.AppWidget.TextView" parent="Widget.AppCompat.TextView">
<item name="android:layout_width">match_parent</item> <item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
@ -77,4 +75,8 @@
<style name="Widget.Component.AppWidget.Panel" parent="Widget.Component.AppWidget.Panel.Base"> <style name="Widget.Component.AppWidget.Panel" parent="Widget.Component.AppWidget.Panel.Base">
<item name="android:elevation">@dimen/elevation_normal</item> <item name="android:elevation">@dimen/elevation_normal</item>
</style> </style>
<style name="Widget.Component.Loading.ImageView.Icon" parent="">
<item name="android:src">@drawable/ic_launcher_foreground</item>
</style>
</resources> </resources>