Rewrite ARCHITECTURE.md

Redo the structure of ARCHITECTURE.md to make more sense with its intended purpose.
This commit is contained in:
OxygenCobalt 2021-02-07 19:57:52 -07:00
parent 26dd1036ec
commit ed10bd1898
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 70 additions and 46 deletions

View file

@ -21,7 +21,8 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
* Extension method for creating and showing a new [ActionMenu].
* @param anchor [View] This should be centered around
* @param data [BaseModel] this menu corresponds to
* @param flag (Optional, defaults to [ActionMenu.FLAG_NONE]) Any extra flags to accompany the data. See [ActionMenu] for more details
* @param flag (Optional, defaults to [ActionMenu.FLAG_NONE]) Any extra flags to accompany the data.
* @see ActionMenu
*/
fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_NONE) {
ActionMenu(requireCompatActivity(), anchor, data, flag).show()

View file

@ -2,6 +2,33 @@
This document is designed to provide a simple overview of Auxio's architecture and some guides on how to add to the codebase in an elegant manner. It will be updated as aspects about Auxio change.
#### Code structure
Auxio's codebase is mostly centered around 4 different types of code.
- UIs: Fragments, RecyclerView items, and Activities are part of this class. All of them should have little data logic in them and should primarily focus on displaying information in their UIs.
- ViewModels: These usually contain data and values that a UI can display, along with doing data processing. The data often takes the form of `MutableLiveData` or `LiveData`, which can be observed.
- Shared Objects: These are the fundamental building blocks of Auxio, and exist at the process level. These are usually retrieved using `getInstance` or a similar function. Shared Objects should be avoided in UIs, as their volatility can cause problems. Its better to use a ViewModel and their exposed data instead.
- Utilities: These are largely found in the `.ui`, `.music`, and `.coil` packages, taking the form of standalone or extension functions that can be used anywhere.
Ideally, UIs should only be talking to ViewModels, ViewModels should only be talking to the Shared Objects, and Shared Objects should only be talking to other shared objects. All objects can use the utility functions.
#### UI Structure
Auxio only has one activity, that being `MainActivity`. When adding a new UI, it should be added as a `Fragment` or a `RecyclerView` item depending on the situation.
Databinding should *always* be used instead of `findViewById`. Use `by memberBinding` if the binding needs to be a member variable in order to avoid memory leaks.
Usually, fragment creation is done in `onCreateView`, and organized into three parts:
- Create variables [Bindings, Adapters, etc]
- Set up the UI
- Set up LiveData observers
When creating a ViewHolder for a `RecyclerView`, one should use `BaseViewHolder` to standardize the binding process and automate some code shared across all ViewHolders.
Data is often bound using Binding Adapters, which are XML attributes assigned in layout files that can automatically display data, usually written as `app:bindingAdapterName="@{data}"`. Its recommended to use these instead of duplicating code manually.
#### Package structure overview
Auxio's package structure is mostly based around the features, and then any sub-features or components involved with that. There are some shared packages however. A diagram of the package structure is shown below:
@ -28,36 +55,47 @@ org.oxycblt.auxio # Main UI's and logging utilities
├──.ui # Shared user interface utilities
```
#### Primary code structure
#### `.coil`
Auxio's codebase is mostly centered around 4 different types of code.
[Coil](https://github.com/coil-kt/coil) is the image loader used by Auxio. All image loading is done through these four functions/binding adapters:
- UIs: Fragments, RecyclerView items, and Activities are part of this class. All of them should have little data logic in them and should primarily focus on displaying information in their UIs.
- ViewModels: These usually contain data and values that a UI can display, along with doing data processing. The data often takes the form of `MutableLiveData` or `LiveData`, which can be observed.
- Shared Objects: These are the fundamental building blocks of Auxio, and exist at the process level. These are usually retrieved using `getInstance` or a similar function. Shared Objects should be avoided in UIs, as their volatility can cause problems. Its better to use a ViewModel and their exposed data instead.
- Utilities: These are largely found in the `.ui`, `.music`, and `.coil` packages, taking the form of standalone or extension functions that can be used anywhere.
- `app:coverArt`: Binding Adapter that will load the cover art for a song or album
- `app:artistImage`: Binding Adapter that will load the artist image
- `app:genreImage`: Binding Adapter that will load the genre image
- `getBitmap`: Function that will take a song and return a bitmap, this should not be used in anything UI related, that is what the binding adapters above are for.
Ideally, UIs should only be talking to ViewModels, ViewModels should only be talking to the Shared Objects, and Shared Objects should only be talking to other shared objects. All objects can use the utility functions.
This should be enough to cover most use cases in Auxio. There are also fetchers for artist/genre images and higher quality covers, but these are not used outside of the module.
#### UI Structure
#### `.database`
Auxio only has one activity, that being `MainActivity`. When adding a new UI, it should be added as a `Fragment` or a `RecyclerView` item depending on the situation.
This is the general repository for databases in Auxio, along with their entities. All databases use `SQLiteOpenHelper`, with all database entities having their keys in a `companion object`.
Databinding should *always* be used instead of `findViewById`. Use `by memberBinding` if the binding needs to be a member variable in order to avoid memory leaks.
#### `.detail`
Usually, fragment creation is done in `onCreateView`, and organized into three parts:
Contains all the detail UIs for some data types in Auxio. All detail user interfaces share the same base layout (A Single RecyclerView) and only change the adapter/data being used. The adapters display both the header with information and the child items of the item itself, usually with a data list similar to this:
- Create variables [Bindings, Adapters, etc]
- Set up the UI
- Set up LiveData observers
`Item being displayed | Child Item | Child Item | Child Item...`
When creating a ViewHolder for a `RecyclerView`, one should use `BaseViewHolder` to standardize the binding process and automate some code shared across all ViewHolders.
`DetailViewModel` acts as the holder for the currently displaying items, along with having the `navToItem` LiveData that coordinates menu/playback navigation [Such as when a user presses "Go to artist"]
#### Binding Adapters
#### `.library`
Data is often bound using Binding Adapters, which are XML attributes assigned in layout files that can automatically display data, usually written as `app:bindingAdapterName="@{data}"`. Its recommended to use these instead of duplicating code manually. These can be found in `.coil` and `.music`.
The UI and adapters for the library view in Auxio, `LibraryViewModel` handles the sorting and which data to display in the fragment, while `LibraryFragment` and `LibraryAdapter` display the data.
#### Playback system
#### `.music`
The music loading system is based off of `MediaStore`, and loads the entire library into a variety of data objects.
All music objects inherit `BaseModel`, which guarantees that all music has both an ID and a name.
- Songs are the most basic element, with them having a reference to their album and genre.
- Albums contain a list of their songs and their parent artist.
- Artists contain a list of songs, a list of albums, and their most prominent genre.
- Genres contain a list of songs, its preferred to use `displayName` with genres as that will convert the any numbered names into non-numbered names.
`BaseModel` can be used as an argument type to specify that any music type, while `Parent` can be used as an argument type to only specify music objects that have child items, such as albums or artists.
#### `.playback`
Auxio's playback system is somewhat unorthodox, as it avoids a lot of the built-in android code in favor of a more understandable and controllable system. Its structured around a couple of objects, the connections being highlighted in this diagram.
@ -73,38 +111,23 @@ PlaybackStateManager───────────────────┘
`PlaybackStateManager` is the shared object that contains the master copy of the playback state, doing all operations on it. This object should ***NEVER*** be used in a UI, as it does not sanitize input and can cause major problems if a Volatile UI interacts with it. It's callback system is also prone to memory leaks if not cleared when done. `PlaybackViewModel` can be used instead, as it exposes stable data and abstracted functions that UI's can use to interact with the playback state.
`PlaybackService`'s job is to use the playback state to manage the ExoPlayer instance and also modify the state depending on system external events, such as when a button is pressed on a headset. It should **never** be bound to, mostly because there is no need given that `PlaybackViewModel` exposes the same data in a much safer fashion.
`PlaybackService`'s job is to use the playback state to manage the ExoPlayer instance and also modify the state depending on system events, such as when a button is pressed on a headset. It should **never** be bound to, mostly because there is no need given that `PlaybackViewModel` exposes the same data in a much safer fashion.
#### Using Music Data
#### `.recycler`
All music objects inherit `BaseModel`, which guarantees that all music has both an ID and a name.
Shared RecyclerView utilities. Important ones to note are `DiffCallback`, which acts as a reusable differ callback for all `BaseModel` instances, and the shared ViewHolders for each data type, such as `SongViewHolder` or `HeaderViewHolder`.
- Songs are the most basic element, with them having a reference to their album and genre.
- Albums contain a list of their songs and their parent artist.
- Artists contain a list of songs, a list of albums, and their most prominent genre.
- Genres contain a list of songs, its preferred to use `displayName` with genres as that will convert the any numbered names into non-numbered names.
#### `.search`
`BaseModel` can be used as an argument type to specify that any music type, while `Parent` can be used as an argument type to only specify music objects that have child items, such as albums or artists.
Package for Auxio's search functionality, `SearchViewHolder` handles the data results and filtering while `SearchFragment`/`SearchAdapter` handles the display of the results and user input.
### Using menus
#### `.songs`
Instead of directly instantiating a menu for an item yourself, you should instead use `ActionMenu` as it will automate the menu creation and click listeners immediately.
Package for the songs UI, there is no data management here, only a user interface.
#### Using Settings
#### `.ui`
Access to settings should preferably be done with `SettingsManager` as it can be accessed everywhere without a context.
#### Using Coil
[Coil](https://github.com/coil-kt/coil) is the image loader used by Auxio. All image loading is done through these four functions/binding adapters:
- `app:coverArt`: Binding Adapter that will load the cover art for a song or album
- `app:artistImage`: Binding Adapter that will load the artist image
- `app:genreImage`: Binding Adapter that will load the genre image
- `getBitmap`: Function that will take a song and return a bitmap, this should not be used in anything UI related, that is what the binding adapters above are for.
This should be enough to cover most use cases in Auxio.
#### Logging
Its recommended to use `logD` and `logE` for logging debug messages and errors. Both will automatically use the names of the objects that call it, and logging messages done with `logD` wont show in release builds.
Shared User Interface utilities. This is primarily made up of convenience/extension functions. It also contains some dedicated utilities, such as:
- The Accent Management system
- `newMenu` and `ActionMenu`, which automates menu creation for most datatypes
- `memberBinding` and `MemberBinder`, which allows for viewbindings to be used as a member variable without memory leaks or nullability issues.