Add a "Last Added" sorting option to the home UI's song list.
I don't know if there is any demand for last added in other contexts.
That will be resolved later.
Remove the excluded directory migration code, as it causes far more
issues than it fixes.
Due to an unfixable logic bug that occurs when trying to read the
setting, Auxio will always try to migrate the database when there is
no music folders, causing a hang in some situations. Fix it by just
removing the migration.
Add an abstraction over the generation system called GenerationGuard.
This allows cheap cooperative threading to be implemented consistently
in many places.
Add automatic rescanning, for real.
This is the culmination of 6 months of work to make Auxio respond to
music updates and to research the most versatile implementation of
such. Is it the best system? No. It's actually a bit messy by necessity
in order to prevent bugs. Does it work well? Yes.
This will not be enabled by default, as it does require a battery
draining foreground service and is generally not useful for most
circumstances.
So glad to be done with this.
Resolves#72.
Move the switch from IO to Main to within Indexer itself, through
withContext.
This is much easier to work with than the previous system of a separate
"update" coroutine, which isn't really needed anymore given that I no
longer need to do IO work when sanitizing the playback state.
Edge case I thought existed did not. PlaybackService must have saved
before dying, and thus if it did not foreground after a sanitization,
the saved state would still sanitize in a similar manner.
Save, but do not read the playback state when sanitizing.
Turns out there is an edge-case where we want to save the state. Still
keep the runtime sanitization, as that greatly reduces the time it
takes to rescan the library.
Do not save the playback state when sanitizing.
After some thought, there is no situation where re-saving the playback
state is desirable. A previously saved state will be consistent with
a sanitized state, and there is no need to save when the service is
active. Thus, save on speed and reduce insane race conditions by just
sanitizing the current runtime state and not saving at all.
Remove the ripple resources and replace them with their system
counterparts.
This is to remove redundancy in-app and make the widget more consistent
with other android widgets.
Finally unify all icon activation states under the new grade modifier
in Material Symbols.
This provides similar clarify to the dot/greyed state, but without
inconsistent meanings. The shuffle icon did have to be bolded a little
more though, as the grade did not seem to do much.
Rework the UI flow for music loading to be more coherent with runtime
rescanning.
The loading progress is now shown as a card on the bottom of the
screen. This way, app use is not completely crippled when the app has
to rescan the music library, albeit the shuffle button still has to
be disabled during this period.
Revert the optional rounded corners on the bottom sheet.
It was causing too many bugs to be a sensible addition. I will only add
it if Google moves WindowInset application to layout time AND only on
devices with the stretch overscroll implemented.
Audit usages of Synchronized throughout the app to prevent deadlocks.
This is primarily composed of not making long-running work
synchronized, instead only making mutations and reads synchronized.
This does cause minor issues, such as during a sanitization event
the playback state could be feasibly saved, changed, and then restored
back to the previous state unintentionally. However, preventing
deadlocks is generally better than trying to fix those.
Use the @Synchronized annotation instead of synchronized.
Makes my ability to manage thread-safety on the shared objects much
easier. Because I don't have to think about what I should guard
and what I shouldn't.
Finally enable runtime rescanning, opening the door for a ton of new
features and automatic rescanning later on.
More work needs to be done on making the shared mutable state in-app
safer to use.
Improve the indexer callback system to be more coherent and efficient.
This delegates the old Callback role to a new singular Callback and
Controller roles. IndexerService also handles the loading process
more gracefully, reducing the amount of time music loads take.
Make notification updates entirely reliant on the MediaSession.
Android 11 and onwards automatically populate the notification with the
MediaSession state. This apparently conflicts with updating the
notification in some cases, resulting in the incorrect song being
shown. Fix this by not populating the notification from Android 11
onward and only posting it when the MediaSession state was set.
Resolves#179.
Remove WindowInsetsCompat from the project, as it is a pile of garbage.
WindowInsetsCompat refuses to actually update the correct fields when
you transform it back into a WindowInsets. This makes it impossible to
use in Auxio. The fact that Google decided to make such an
overengineered wrapper to some version-specific methods is an absolute
joke. All you needed were some extension functions, but no, let's make
an entire wrapper class filled with so many gotchas as to be unusable.
Rework the rounded covers option into a new "Round Mode" option.
This commit extends the rounded corners configuration to now the
widget, thus making the setting apply now to covers, the bar, and
the widget configuration. This makes a naming change useful.
Further rework the button layout to be alike to other modern widgets.
This changeset transforms the play button into a FAB-ish thing,
makes spacing coherent between the uses of each layout, and adds
spacers to make the buttons layout in a more appealing way.
Update the library update process to be on a co-routine, updating
callbacks on the main thread.
For some insane reason, the Main dispatcher used normally when
loading music just disappears sometimes. This leads to unpleasent
crashes as callbacks expect to be called on the app thread, not
any background threads.
Fix this by forcing the Main dispatcher during the update process.
This requires the music update process to also run on a background
thread, albeit that will be useful for automatic rescanning late ron.
Completely rework Auxio's iconography based on the new material symbols
icon set.
This does the following:
1. Switches the sharp + filled icon style to an outlined + round icon
style.
2. Removes 32dp icons from everywhere except the playback panel.
This does not:
1. Actually handle optical sizes right. This is going to take some more
work to make it harmonious with the current UI.
2. Update margins in some places to be harmonious with the new icons.
This is also going to take some more work to do properly.
Rework the bottom sheet layout process to accomodate the new rounded
corners and be far more efficient.
This removes the weird content layout code and moves it into the inset
code, which not only allows content to show in the corners, but also
allows us to minimize the amount of layouts that we normally perform.
Make BottomSheetLayout apply rounded corners when rounded covers is
enabled.
Draggable elements should be rounded, not straight, so this simply
makes this consistent. It does have one visual error as it stands,
stemming from the layout code allocating space for the bar and thus
clipping certain content. I hope to fix this soon.
Do not use collectImmediately when recreating tabs in the home menu.
This resulted in an issue where the ViewPager would not update to
reflect the new tab layout.
Fix several issues with the bottom sheet implementation, mostly
relating to what occurs when the bar is hidden.
Previously, if the bar was hidden and then re-shown, it would not be
visible, and you could override a hidden event with another tap. These
are now both resolved by removing a stray visibility assignment and
replacing the old drag checks with a new isDraggable attribute.
In the future, I hope to abuse BottomSheetBehavior into not being a
pile of garbage and replace this system once and for all.
Override isActive to control when the ReplayGain engine should
manipulate audio.
This makes the system much more efficient, as we can side-step a
useless copy when ReplayGain shouldn't be applied.
Remove the TitleMidLarge style, replacing it with more Material3
attributes.
This finally removes all of the non-standard text styles. There is
still the two TitleMedium styles, but this is considered okay.
In the playback view, TitleMidLarge has become TitleLarge, which
honestly works quite well, as the same text styles are used in the
detail views.
Force LTR on timeline controls, as per the Material Design guidelines.
The guidelines state that while "directional" UIs should be LTR/RTL
depending on locale, "timeline" UIs should always by LTR, as the
direction of time is universal. Auxio did not do this, and so the
timeline controls would be RTL on other elements. Fix this by forcing
LTR on the UI elements that correspond to timelines.
Now, this is not the best system. To ensure that the rest of the layout
remains sane, much of the directional views have to be wrapped in a
redundant layout, which is somewhat in-efficient. However, the impact
seems to be at least negligable.
Rework Sort again into a new class that leverages a better Mode design
and static comparator instances.
This somewhat improves efficiency, but is also far easier to work with
and has far less footguns with adding new sorts.
Add new shortcut utilities for collecting StateFlows in a safe manner.
The priamry addition here is collectImmediately. collectImmediately
just calls block with the existing value initially, which helps remove
a good amount of bugs regarding state initialization. Sure, it is a bit
inefficient given that it will also initialize on startup, but this is
okay.
The other utilities are the same, but simply remove the launch
boilerplate.
Add a new "Detail playback mode" option that allows one to configure
what selecting a song will do in an album/artist/genre.
This is mostly a clone of the prior setting, just in a new context.
Resolves#164.
Add a utility to easily work with lifecycle-dependent fragment objects.
This reduces the code duplication required to maintain objects that
would leak after the destruction of a fragment. We normally would not
do this as a delegate, as that usually entails some lifecycle wizardry
that can easily break and crash the app in esoteric situations.
However, this this just extends the normal lifecycle without watching
any state, it seems to be pretty safe to use.
Rework the preference classes to reduce the horrible bloat of the
recursivelyHandlePreference function.
This was mostly implementing new methods into IntListPreference and
adding a new preference to represent the weird, "generic" dialogs that
are used at points. While some preferences still need to be tweaked in
recursivelyHandlePreference, it is nowhere near as bad as it was prior.
Revamp the shared object SettingsManager into a standalone utility
called Settings.
This makes many things easier in Auxio. It completely unifies the key
format that we use (Android Strings instead of Java consts), eliminates
the pretty dumb initialization method that we use, and eliminates the
dubiousness of holding a Context-related utility in a global field.
The only cost was having to migrate even more ViewModels to Android
ViewModels. Whatever.
Add synchronized calls to all mutations in PlaybackStateManager.
I mean, it is global mutable state modified on several threads. This is
the safest option to remove a bunch of horrible bugs.
Add a controller role to PlaybackStateManager.
The controller manages the internal player, and acts as an intermediary
between other internal objects that want to modify the player, but
don't actually have access to it.
This makes a bunch of future changes far easier.
Add a shortcut to shuffle all songs.
This is likely the only static shortcut Auxio will have. Top tracks
and recently added are completely useless for me, so I will never
add them. I may add more dynamic shortcuts for recently played items,
however.
Note that we use a basic black shuffle icon here. I will not add icon
customization to these shortcuts.
Fix a visual issue with the queue animation where the playback view
will still slightly show.
This was caused by the lack of a background in the queue fragment UI.
Fix an issue where a seam might appear on some covers when rounded
covers was enabled.
This was caused by a poor usage of clipToOutline. Replace with simply
stacking existing image instances on top of eachother.
Use ServiceCompat.stopForeground instead of stopForeground.
This is preliminary preparation for Android 13. I can only change SDK
versions however when the Android Gradle Plugin makes a new release
though.
Remove the animated indicator, replacing it with a static one.
I wish I could have kept this, but once again android is a sh******ed
mess and makes it impossible to dynamically animate something depending
on the playing state. It will restart the animation, ignore calls to
stop, or just flat out now run the code path in the first place due to
race conditions.
Further separate backend implementations to remove redundant tasks.
Generally, if I have not encountered an issue where a field is not
present/sane, I don't want to put in a "well, maybe" fallback, as
that would tank performance. Only do it when we need to.
Introduce MenuFragment in order to replace ActionMenu.
ActionMenu was a terrible class filled with hacks. Introduce a new
fragment called MenuFragment that enables the same features, plus:
1. Requiring consumers the specify the menu, which prevents issues
from one-size-fits-all menus (unless absolutely necessary)
2. Fixing an issue where multiple menus appear at once
Switch the excluded directory system to StorageVolume.
This finally removes dependence on the in-house Volume constructs, and
also completely extends fully music folder selection support to all
versions that Auxio supports.
This is possibly one of the most invasive and risky reworks I have done
with Auxio's music loader, but it's also somewhat exciting, as no other
music player I know of handles Volumes like I do.
Leverage StorageVolume when working with file paths.
StorageVolume is android's navive API for handling external volumes.
Ideally, we would want to replace our built-in volume class with this
new API, however doing so is somewhat complicated as some methods only
exist on newer API levels. This is only the first step until we are
able to migrate the excluded directory system to this as well.
Make some ViewModel instances AndroidViewModels in order to make some
code less insane.
I don't like doing this, as I want to keep ViewModel instances clean of
android things, but this just makes a lot of functionality easier to
implement.
Only show codecs in the "format" field, if we are able to extract them.
This is primarily for consistency, as there is no way for us to
determine the container format outside of an extension (which could not
be sane).
Fix an API 21-specific bug that could result in covers not loading when
quality covers was enabled.
This stemmed from a use of `use` on MediaMetadataRetriever, which
relied on an interface not present on the class on API 21.
Completely rework the excluded directory system into a new
"Music Folders" system.
This is implemented alongside a new "Include" mode. This mode
allows the user to restrict music indexing to a parsicular folder.
I've been reluctant to add this feature, as having two separate
options seemed bad. This resolves it by effectively packing whether
to include/exclude directories into a single option.
Resolves#154.
Add a non-queue version of the song menu to the playback panel.
While the original UI flow of clicking on each TextView to navigate
to a particular item is still present, this simply makes the action
more clear to a first-time user.
Heavily improve the way Auxio handles shows formats in the song
properties view.
This is composed of the following:
- Using ExoPlayer to find a format-specific mime type before having to
fall back to other solutions
- Keeping around the format and extension mime types so that each is
picked in the best circumstances
- Using MediaFormat to also retrieve a format-specific mime type in the
case that ExoPlayer parsing is disabled
- Adding special names for the most common formats
Remove scientific notation from the auxio icon, allowing me to
reintroduce it to the notification icon.
API 21 does not support scientific notation in vector drawables, so we
need to remove them from the icon for it to not crash the system ui.
Use basic scroll indicators when a dialog shows a list.
Mostly for material guidelines. Excluded dialogs and int pref dialog
have not been modified, as I am still working on revamping those.
Remove malformed songs that have a size of 0 bytes, but are still
present in MediaStore.
This issue only seems to occur when files are transferred via MTP, but
are not loaded due to storage issues.
Add support for file size, format, and parent directory values to the
MediaStore backend.
I hope that this handles API boundaries properly, especially regarding
path parsing. As a side-note, I have learned of a way to extend
external volume support to even earlier versions. Maybe.
When ReplayGain is not needed, use ByteBuffer.put in favor of a for
loop when doing a simple copy.
ByteBuffer.put is highly optimized and far more efficient than a for
loop.
Redo the paths and remove scientific notation from the indicator to
make it work below API 22.
The fact that studio does not have a built-in vector editor of any
kind that properly handles the edge-cases is shameful.
Add a new view called ImageGroup that will handle all advanced image
hacks from now on.
This includes the indicator (which is now animated), any selection
indicators, and the weirdness of the album song image. All of that
is now handled by ImageGroup. This is the culmination of probably
a day and a half of wrangling with android insanity and having to
remove a lot of what I liked about the indicator in order to make
this work on a basic level.
The only major bug I am currently aware of with this is that the
indicator is bugged out on Lollipop devices due to bad vectors.
Again.
I never want to do this again. I cannot believe that adding a basic
indicator took this long and required so much stupid hacks and
inefficient code. And then google wonders why android apps are so
visually unappealing and janky and laggy. Hm. Must be that devs aren't
using the brand new FooBarBlasterFlow library!
Add a playing indicator to cover art.
This is simply to improve the general aestethics of this view. Of
course, the current way I implement this is incredibly stupid and I
plan to replace it.
Completely rework the way Auxio handles icons.
This is mostly two changes:
1. Removing ImageButton/StyledImageButton for MaterialButton. This is
done by abusing MaterialButton's theming options to make it only show
an icon.
2. Standardizing icon sizes into small, medium, and large categories.
Small is the default, Medium and Large are for edge-cases like the
playback icons which look horrible at 24dp.
3. Abusing the Toolbar to make it follow Material 3 guidelines. This
mostly involved removing the strange icon sizing and correctly padding
the view.
4. Reworking the playback bar to use more, smaller icons, making it
more like a Toolbar in the process (which I like).
Supply a view to WindowInsetsCompat.toWindowInsetsCompat through all
calls in the app.
This does nothing, but it would resolve future bugs if I were to use
the most exotic window inset types.
On the playback par, pick the larger inset out of the system bar
insets or system gesture insets.
This is to fix an issue where android just does not provide correct
gesture insets on Android 12L, and [maybe???] fix what is going on
in #149.