about: add full library statistics

Split off the "songs loaded" about item into it's own card called
"library statistics"

This card includes the song, album, artist, and genre counts,
alongside a total duration of the music library. This is just more
informative and useful to the user.

Resolves #121.
This commit is contained in:
OxygenCobalt 2022-05-04 20:20:47 -06:00
parent ac6a471318
commit 1a9e55e73b
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 238 additions and 127 deletions

View file

@ -4,9 +4,11 @@
#### What's New
- Added ReplayGain support for below-reference volume tracks [i.e positive ReplayGain values]
- About screen now shows counts for multiple types of library items, alongside a total duration
#### What's Fixed
- Fixed incorrect ellipsizing on song items
- Fixed a variety of esoteric crashes with queue state
#### What's Changed
- Audio focus is no longer configurable

View file

@ -112,7 +112,7 @@ private constructor(
private val genre: Genre,
) : BaseFetcher() {
override suspend fun fetch(): FetchResult? {
// We don't need to sort here, as the way we
// Don't sort here to preserve compatibility with previous variations of this image.
val albums = genre.songs.groupBy { it.album }.keys
val results = albums.mapAtMost(4) { album -> fetchArt(context, album) }

View file

@ -88,9 +88,6 @@ import org.oxycblt.auxio.util.logD
* I wish I was born in the neolithic.
*
* @author OxygenCobalt
*
* TODO: Experiment with making the exoplayer metadata system more effificent so that we could
* leverage it here (maybe)
*/
object Indexer {
/**

View file

@ -65,8 +65,8 @@ import org.oxycblt.auxio.widgets.WidgetProvider
* therefore there's no need to bind to it to deliver commands.
* @author OxygenCobalt
*
* TODO: Synchronize components in a less awful way (Fix issue where rapid-fire updates results
* in a desynced notification)
* TODO: Synchronize components in a less awful way (Fix issue where rapid-fire updates results in a
* desynced notification)
*/
class PlaybackService :
Service(), Player.Listener, PlaybackStateManager.Callback, SettingsManager.Callback {
@ -219,7 +219,9 @@ class PlaybackService :
newPosition: Player.PositionInfo,
reason: Int
) {
playbackManager.synchronizePosition(player.currentPosition)
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
playbackManager.synchronizePosition(player.currentPosition)
}
}
override fun onTracksInfoChanged(tracksInfo: TracksInfo) {

View file

@ -32,6 +32,7 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentAboutBinding
import org.oxycblt.auxio.home.HomeViewModel
import org.oxycblt.auxio.music.toDuration
import org.oxycblt.auxio.ui.ViewBindingFragment
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -62,6 +63,20 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
homeModel.songs.observe(viewLifecycleOwner) { songs ->
binding.aboutSongCount.textSafe = getString(R.string.fmt_songs_loaded, songs.size)
binding.aboutTotalDuration.textSafe =
getString(R.string.fmt_total_duration, songs.sumOf { it.seconds }.toDuration(false))
}
homeModel.albums.observe(viewLifecycleOwner) { albums ->
binding.aboutAlbumCount.textSafe = getString(R.string.fmt_albums_loaded, albums.size)
}
homeModel.artists.observe(viewLifecycleOwner) { artists ->
binding.aboutArtistCount.textSafe = getString(R.string.fmt_artists_loaded, artists.size)
}
homeModel.genres.observe(viewLifecycleOwner) { genres ->
binding.aboutGenreCount.textSafe = getString(R.string.fmt_genres_loaded, genres.size)
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

View file

@ -26,153 +26,229 @@
android:clipToPadding="false"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<com.google.android.material.card.MaterialCardView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="@dimen/spacing_medium">
<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/about_auxio_icon"
style="@style/Widget.Auxio.Image.Small"
android:layout_marginTop="@dimen/spacing_medium"
android:contentDescription="@string/desc_auxio_icon"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toStartOf="@+id/about_app_name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/about_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:text="@string/info_app_name"
android:textAppearance="@style/TextAppearance.Auxio.TitleLarge"
app:layout_constraintBottom_toBottomOf="@+id/about_auxio_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/about_auxio_icon"
app:layout_constraintTop_toTopOf="@+id/about_auxio_icon" />
<TextView
android:id="@+id/about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:gravity="center"
android:paddingStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_medium"
android:text="@string/info_app_desc"
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_auxio_icon" />
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/version_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_medium"
app:layout_constraintTop_toBottomOf="@+id/about_desc"
app:layout_constraintVertical_chainStyle="packed">
android:layout_height="match_parent">
<ImageView
android:id="@+id/about_version_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/lbl_version"
android:src="@drawable/ic_about"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/about_auxio_icon"
style="@style/Widget.Auxio.Image.Small"
android:layout_marginTop="@dimen/spacing_medium"
android:contentDescription="@string/desc_auxio_icon"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toStartOf="@+id/about_app_name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/about_version_title"
android:id="@+id/about_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:text="@string/lbl_version"
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
app:layout_constraintBottom_toTopOf="@+id/about_version"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toTopOf="@+id/about_version_icon" />
android:text="@string/info_app_name"
android:textAppearance="@style/TextAppearance.Auxio.TitleLarge"
app:layout_constraintBottom_toBottomOf="@+id/about_auxio_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/about_auxio_icon"
app:layout_constraintTop_toTopOf="@+id/about_auxio_icon" />
<TextView
android:id="@+id/about_version"
android:layout_width="wrap_content"
android:id="@+id/about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:textAppearance="@style/TextAppearance.Auxio.BodySmall"
app:layout_constraintBottom_toBottomOf="@+id/about_version_icon"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toBottomOf="@+id/about_version_title"
tools:text="16.16.16" />
android:layout_marginTop="@dimen/spacing_small"
android:gravity="center"
android:paddingStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_medium"
android:text="@string/info_app_desc"
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_auxio_icon" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/version_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_medium"
app:layout_constraintTop_toBottomOf="@+id/about_desc"
app:layout_constraintVertical_chainStyle="packed">
<ImageView
android:id="@+id/about_version_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/lbl_version"
android:src="@drawable/ic_about"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/about_version_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:text="@string/lbl_version"
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
app:layout_constraintBottom_toTopOf="@+id/about_version"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toTopOf="@+id/about_version_icon" />
<TextView
android:id="@+id/about_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:textAppearance="@style/TextAppearance.Auxio.BodySmall"
app:layout_constraintBottom_toBottomOf="@+id/about_version_icon"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toBottomOf="@+id/about_version_title"
tools:text="16.16.16" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/about_code"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_code"
app:drawableStartCompat="@drawable/ic_code"
app:layout_constraintBottom_toTopOf="@+id/about_faq"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/version_container" />
<TextView
android:id="@+id/about_faq"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_faq"
app:drawableStartCompat="@drawable/ic_faq"
app:layout_constraintBottom_toTopOf="@+id/about_licenses"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_code" />
<TextView
android:id="@+id/about_licenses"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_licenses"
app:drawableStartCompat="@drawable/ic_license"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_faq" />
<TextView
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_author"
app:drawableStartCompat="@drawable/ic_artist"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/about_code"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_medium">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_code"
app:drawableStartCompat="@drawable/ic_code"
app:layout_constraintBottom_toTopOf="@+id/about_faq"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/version_container" />
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/about_licenses"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_licenses"
app:drawableStartCompat="@drawable/ic_license"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_faq" />
<TextView
android:id="@+id/about_library_counts"
style="@style/Widget.Auxio.TextView.Header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_library_counts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/about_faq"
style="@style/Widget.Auxio.TextView.Icon.Clickable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_faq"
app:drawableStartCompat="@drawable/ic_faq"
app:layout_constraintBottom_toTopOf="@+id/about_licenses"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_code" />
<TextView
android:id="@+id/about_song_count"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_song"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Songs Loaded: 1616" />
<TextView
android:id="@+id/about_song_count"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_album"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Songs Loaded: 1616" />
<TextView
android:id="@+id/about_album_count"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_album"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Albums Loaded: 1616" />
<TextView
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_author"
app:drawableStartCompat="@drawable/ic_artist"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_song_count" />
<TextView
android:id="@+id/about_artist_count"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_artist"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Artists Loaded: 1616" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/about_genre_count"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_genre"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Genres Loaded: 1616" />
<TextView
android:id="@+id/about_total_duration"
style="@style/Widget.Auxio.TextView.Icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:drawableStartCompat="@drawable/ic_time"
app:drawableTint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Total duration: 16:16:16" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</org.oxycblt.auxio.ui.EdgeCoordinatorLayout>

View file

@ -54,6 +54,7 @@
<string name="lbl_faq">FAQ</string>
<string name="lbl_licenses">Licenses</string>
<string name="lbl_author">Developed by OxygenCobalt</string>
<string name="lbl_library_counts">Library statistics</string>
<!-- Settings namespace | Settings-related labels -->
<string name="set_title">Settings</string>
@ -168,6 +169,10 @@
<!-- Format Namespace | Value formatting/plurals -->
<string name="fmt_songs_loaded">Songs loaded: %d</string>
<string name="fmt_albums_loaded">Albums loaded: %d</string>
<string name="fmt_artists_loaded">Artists loaded: %d</string>
<string name="fmt_genres_loaded">Genres loaded: %d</string>
<string name="fmt_total_duration">Total duration: %s</string>
<plurals name="fmt_song_count">
<item quantity="one">%d Song</item>