From 9171f9a3b45596bb1acb6ca8ee53e2384192177a Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Thu, 24 Feb 2022 14:55:35 -0700 Subject: [PATCH] coil: make mosaics use squareframetransform Change createMosaic to use SquareFrameTransform. This should help fix a theoretical bug where non-1:1 album covers will be oddly stretched in a generated mosaic, as all album covers will be forcefully cropped to a 1:1 ratio. --- .../java/org/oxycblt/auxio/MainActivity.kt | 6 +++-- .../java/org/oxycblt/auxio/MainFragment.kt | 1 + .../org/oxycblt/auxio/coil/AuxioFetcher.kt | 23 +++++++++++-------- .../java/org/oxycblt/auxio/coil/CoilUtils.kt | 3 +-- .../oxycblt/auxio/coil/RoundableImageView.kt | 9 +++++--- .../auxio/coil/SquareFrameTransform.kt | 4 ++++ .../org/oxycblt/auxio/home/HomeFragment.kt | 3 ++- .../org/oxycblt/auxio/music/MusicStore.kt | 1 + .../oxycblt/auxio/playback/PlaybackSeekBar.kt | 1 + .../java/org/oxycblt/auxio/ui/ActionMenu.kt | 3 +++ 10 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 4c05f784d..7bcc57ce3 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -40,6 +40,8 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat /** * The single [AppCompatActivity] for Auxio. + * TODO: Add a new view for crashes with a stack trace + * TODO: Custom language support */ class MainActivity : AppCompatActivity() { private val playbackModel: PlaybackViewModel by viewModels() @@ -104,10 +106,10 @@ class MainActivity : AppCompatActivity() { // The black theme has a completely separate set of styles since style attributes cannot // be modified at runtime. if (isNight && settingsManager.useBlackTheme) { - logD("Applying black theme [with accent $accent]") + logD("Applying black theme [accent $accent]") setTheme(accent.blackTheme) } else { - logD("Applying normal theme [with accent $accent]") + logD("Applying normal theme [accent $accent]") setTheme(accent.theme) } } diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index b61c315c8..ac0657cf3 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -42,6 +42,7 @@ import org.oxycblt.auxio.util.logW * A wrapper around the home fragment that shows the playback fragment and controls * the more high-level navigation features. * @author OxygenCobalt + * TODO: Add a new view with a stack trace whenever the music loading process fails. */ class MainFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() diff --git a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt index 266227ae2..65f964af3 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt @@ -35,6 +35,7 @@ import android.util.Size as AndroidSize /** * The base implementation for all image fetchers in Auxio. * @author OxygenCobalt + * TODO: Artist images */ abstract class AuxioFetcher : Fetcher { private val settingsManager = SettingsManager.getInstance() @@ -204,7 +205,7 @@ abstract class AuxioFetcher : Fetcher { * Create a mosaic image from multiple streams of image data, Code adapted from Phonograph * https://github.com/kabouzeid/Phonograph */ - protected fun createMosaic(context: Context, streams: List, size: Size): FetchResult? { + protected suspend fun createMosaic(context: Context, streams: List, size: Size): FetchResult? { if (streams.size < 4) { return streams.firstOrNull()?.let { stream -> return SourceResult( @@ -219,7 +220,9 @@ abstract class AuxioFetcher : Fetcher { // get a symmetrical mosaic [and to prevent bugs]. If there is no size, default to a // 512x512 mosaic. val mosaicSize = AndroidSize(size.width.mosaicSize(), size.height.mosaicSize()) - val increment = AndroidSize(mosaicSize.width / 2, mosaicSize.height / 2) + val mosaicFrameSize = Size( + Dimension(mosaicSize.width / 2), Dimension(mosaicSize.height / 2) + ) val mosaicBitmap = Bitmap.createBitmap( mosaicSize.width, @@ -238,20 +241,20 @@ abstract class AuxioFetcher : Fetcher { break } - val bitmap = Bitmap.createScaledBitmap( - BitmapFactory.decodeStream(stream), - increment.width, - increment.height, - true - ) + // Run the bitmap through a transform to make sure it's square + val bitmap = SquareFrameTransform.INSTANCE + .transform( + BitmapFactory.decodeStream(stream), + mosaicFrameSize + ) canvas.drawBitmap(bitmap, x.toFloat(), y.toFloat(), null) - x += increment.width + x += bitmap.width if (x == mosaicSize.width) { x = 0 - y += increment.height + y += bitmap.height } } diff --git a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt index c65aff867..256ff621f 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt @@ -64,10 +64,9 @@ fun ImageView.bindGenreImage(genre: Genre?) = load(genre, R.drawable.ic_genre) fun ImageView.load(music: T?, @DrawableRes error: Int) { dispose() - load(music) { error(error) - transformations(SquareFrameTransform()) + transformations(SquareFrameTransform.INSTANCE) } } diff --git a/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt b/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt index 61ff16ac3..c06194c0a 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt @@ -10,6 +10,11 @@ import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.util.getColorSafe import org.oxycblt.auxio.util.stateList +/** + * An [AppCompatImageView] that applies the specified cornerRadius attribute if the user + * has enabled the "Round album covers" option. We don't round album covers by default as + * it desecrates album artwork, but if the user desires it we do have an option to enable it. + */ class RoundableImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -17,7 +22,7 @@ class RoundableImageView @JvmOverloads constructor( ) : AppCompatImageView(context, attrs, defStyleAttr) { init { val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.RoundableImageView) - val cornerRadius = styledAttrs.getDimension(R.styleable.RoundableImageView_cornerRadius, 0.0f) + val cornerRadius = styledAttrs.getDimension(R.styleable.RoundableImageView_cornerRadius, 0f) styledAttrs.recycle() background = MaterialShapeDrawable().apply { @@ -29,8 +34,6 @@ class RoundableImageView @JvmOverloads constructor( override fun onAttachedToWindow() { super.onAttachedToWindow() - // We don't round album covers by default as it desecrates album artwork, but we do - // provide an option if one wants it. // As for why we use clipToOutline instead of coils RoundedCornersTransformation, the radii // of an image's corners is dependent on the actual dimensions of the image, which would // force us to resize all images to a fixed size. clipToOutline is pretty much always diff --git a/app/src/main/java/org/oxycblt/auxio/coil/SquareFrameTransform.kt b/app/src/main/java/org/oxycblt/auxio/coil/SquareFrameTransform.kt index 34875c602..1287cd409 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/SquareFrameTransform.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/SquareFrameTransform.kt @@ -37,4 +37,8 @@ class SquareFrameTransform : Transformation { return dst } + + companion object { + val INSTANCE = SquareFrameTransform() + } } 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 c89ccb972..9f79c787c 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -55,6 +55,7 @@ import org.oxycblt.auxio.util.logTraceOrThrow * The main "Launching Point" fragment of Auxio, allowing navigation to the detail * views for each respective item. * @author OxygenCobalt + * TODO: Make tabs invisible when there is only one */ class HomeFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() @@ -163,7 +164,7 @@ class HomeFragment : Fragment() { playbackModel.shuffleAll() } -// --- VIEWMODEL SETUP --- + // --- VIEWMODEL SETUP --- // There is no way a fast scrolling event can continue across a re-create. Reset it. homeModel.updateFastScrolling(false) diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index 7cc5782bc..e867dc906 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -36,6 +36,7 @@ import java.lang.Exception * The main storage for music items. * Getting an instance of this object is more complicated as it loads asynchronously. * See the companion object for more. + * TODO: Add automatic rescanning [major change] * @author OxygenCobalt */ class MusicStore private constructor() { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt index 4fae1f8e0..6b7002141 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt @@ -36,6 +36,7 @@ import org.oxycblt.auxio.util.stateList * A custom view that bundles together a seekbar with a current duration and a total duration. * The sub-views are specifically laid out so that the seekbar has an adequate touch height while * still not having gobs of whitespace everywhere. + * TODO: Add smooth seeking [i.e seeking in sub-second values] * @author OxygenCobalt */ @SuppressLint("RestrictedApi") diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt index 222870782..7f8892f43 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt @@ -55,6 +55,9 @@ fun Fragment.newMenu(anchor: View, data: Item, flag: Int = ActionMenu.FLAG_NONE) * @param flag Any extra flags to accompany the data. See [FLAG_NONE], [FLAG_IN_ALBUM], [FLAG_IN_ARTIST], [FLAG_IN_GENRE] for more details. * @throws IllegalStateException When there is no menu for this specific datatype/flag * @author OxygenCobalt + * TODO: Stop scrolling when a menu is open + * TODO: Prevent duplicate menus from showing up + * TODO: Maybe replace this with a bottom sheet? */ class ActionMenu( activity: AppCompatActivity,