.
diff --git a/README.md b/README.md
index 0ebc5a1..874a718 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,126 @@
-# tempus
+
+
+
-Subsonic Audio Streaming Android
\ No newline at end of file
+---
+
+
+ Access your music library on all your android devices
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+**Tempus** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
+
+Tempus does not rely on magic algorithms to decide what you should listen to. Instead, the interface is built around your listening history, randomness, and optionally integrates with services like Last.fm to personalize your music experience.
+
+The project is a fork of [Tempo](#credits).
+
+**If you find Tempus useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.**
+
+**Use the Github version of the app for full Android Auto and Chromecast support.**
+
+sha256 signing key fingerprint
+`B7:85:01:B9:34:D0:4E:0A:CA:8D:94:AF:D6:72:6A:4D:1D:CE:65:79:7F:1D:41:71:0F:64:3C:29:00:EB:1D:1D`
+
+### Releases
+
+Please note the two variants in the release assets include release/debug and 32/64 bit flavors.
+
+`app-tempus` <- The github release with all the android auto/chromecast features
+
+`app-degoogled*` <- The izzyOnDroid release that goes without any of the google stuff. It is now available on izzyOnDroid (64bit) I am releasing the both 32/64bit apk's here on github for those who need a 32bit version.
+
+[CHANGELOG.md](CHANGELOG.md)
+
+## Usage
+
+[Documentation](USAGE.md) (work in progress)
+
+## Features
+- **Subsonic Integration**: Tempus seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go.
+- **Sleek and Intuitive UI**: Enjoy a clean and user-friendly interface designed to enhance your music listening experience, tailored to your preferences and listening history.
+- **Browse and Search**: Easily navigate through your music library using various browsing and searching options, including artists, albums, genres, playlists, decades and more.
+- **Streaming and Offline Mode**: Stream music directly from your Subsonic server. Offline mode is currently under active development and may have limitations when using multiple servers.
+- **Playlist Management**: Create, edit, and manage playlists to curate your perfect music collection.
+- **Gapless Playback**: Experience uninterrupted playback with gapless listening mode.
+- **Chromecast Support**: Stream your music to Chromecast devices. The support is currently in a rudimentary state.
+- **Scrobbling Integration**: Optionally integrate Tempus with Last.fm or Listenbrainz.org to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
+- **Podcasts and Radio**: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempus, expanding your audio entertainment options.
+- **Transcoding Support**: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server.
+- **Android Auto Support**: Enjoy your favorite music on the go with full Android Auto integration, allowing you to seamlessly control and listen to your tracks directly from your mobile device while driving.
+- **Multiple Libraries**: Tempus handles multi-library setups gracefully. They are displayed as Library folders.
+- **Equalizer**: Option to use in app equalizer.
+- **Widget**: New widget to keeping the basic controls on your screen at all times.
+- **Available in 11 languages**: Currently in Chinese, French, German, Italian, Korean, Polish, Portuguese, Russion, Spanish and Turkish
+
+## Screenshot
+
+
+ Light theme
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dark theme
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Contributing
+
+Please fork and open PR's against the development branch. Make sure your PR builds successfully.
+
+If there is an UI change, please include a before/after screenshot and a short video/gif if that helps elaborating the fix/feature in the PR.
+
+Currently there are no tests but I would love to start on some unit tests.
+
+Not a hard requirement but any new feature/change should ideally include an update to the nacent documention.
+
+## Support
+
+[**Buy me a coffee**](https://ko-fi.com/eddyizm)
+bitcoin: `3QVHSSCJvn6yXEcJ3A3cxYLMmbvFsrnUs5`
+
+## License
+
+Tempus is released under the [GNU General Public License v3.0](LICENSE). Feel free to modify, distribute, and use the app in accordance with the terms of the license. Contributions to the project are also welcome.
+
+## Credits
+Thanks to the original repo/creator [CappielloAntonio](https://github.com/CappielloAntonio) (forked from v3.9.0)
+
+[Opensvg.org](https://opensvg.org) for the new turntable logo.
\ No newline at end of file
diff --git a/USAGE.md b/USAGE.md
new file mode 100644
index 0000000..05abee9
--- /dev/null
+++ b/USAGE.md
@@ -0,0 +1,183 @@
+# Tempus Usage Guide
+[<- back home](README.md)
+
+## Table of Contents
+- [Prerequisites](#prerequisites)
+- [Getting Started](#getting-started)
+- [Server Configuration](#server-configuration)
+- [Main Features](#main-features)
+
+- [Navigation](#navigation)
+- [Playback Controls](#playback-controls)
+- [Favorites](#favorites)
+- [Playlist Management](#playlist-management)
+- [Android Auto](#android-auto)
+- [Settings](#settings)
+- [Troubleshooting](#troubleshooting)
+
+## Prerequisites
+
+**Important Notice**: This app is a Subsonic-compatible client and does not provide any music content itself. To use this application, you must have:
+
+- An active Subsonic API server (or compatible service) already set up
+- Valid login credentials for your Subsonic server
+- Music content uploaded and organized on your server
+
+### Verified backends
+This app works with any service that implements the Subsonic API, including:
+- [LMS - Lightweight Music Server](https://github.com/epoupon/lms) - *personal fave and my backend*
+- [Navidrome](https://www.navidrome.org/)
+- [Gonic](https://github.com/sentriz/gonic)
+- [Ampache](https://github.com/ampache/ampache)
+- [NextCloud Music](https://apps.nextcloud.com/apps/music)
+
+
+
+
+## Getting Started
+
+### Installation
+1. Download the APK from the [Releases](https://github.com/eddyizm/tempus/releases) section
+2. Enable "Install from unknown sources" in your Android settings
+3. Install the application
+
+### First Launch
+1. Open the application
+2. You will be prompted to configure your server connection
+3. Grant necessary permissions for media playback and background operation
+
+## Server Configuration
+
+### Initial Setup
+**IN PROGRESS**
+1. Enter your server URL (e.g., `https://your-subsonic-server.com`)
+2. Provide your username and password
+3. Test the connection to ensure proper configuration
+
+### Advanced Settings
+**TODO**
+
+## Main Features
+
+### Library View
+
+**Multi-library**
+
+Tempus handles multi-library setups gracefully. They are displayed as Library folders.
+
+However, if you want to limit or change libraries you could use a workaround, if your server supports it.
+
+You can create multiple users , one for each library, and save each of them in Tempus app.
+
+### Now Playing Screen
+
+On the main player control screen, tapping on the artwork will reveal a small collection of 4 buttons/icons.
+
+
+
+
+*marked the icons with numbers for clarity*
+
+1. Downloads the track (there is a notification if the android screen but not a pop toast currently )
+2. Adds track to playlist - pops up playlist dialog.
+3. Adds tracks to the queue via instant mix function
+ * TBD: what is the _instant mix function_?
+ * Uses [getSimilarSongs](https://opensubsonic.netlify.app/docs/endpoints/getsimilarsongs/) of OpenSubsonic API.
+ Which tracks to be mixed depends on the server implementation. For example, Navidrome gets 15 similar artists from LastFM, then 20 top songs from each.
+4. Saves play queue (if the feature is enabled in the settings)
+ * if the setting is not enabled, it toggles a view of the lyrics if available (slides to the right)
+
+### Podcasts
+If your server supports it - add a podcast rss feed
+
+
+
+
+### Radio Stations
+If your server supports it - add a internet radio station feed
+
+
+
+
+## Navigation
+
+### Bottom Navigation Bar
+**IN PROGRESS**
+- **Home**: Recently played and server recommendations
+- **Library**: Your server's complete music collection
+- **Download**: Locally downloaded files from server
+
+## Playback Controls
+
+### Streaming Controls
+**TODO**
+
+### Advanced Controls
+**TODO**
+
+## Favorites
+
+### Favorites (aka heart aka star) to albums and artists
+- Long pressing on an album gives you access to heart/unheart an album
+
+
+
+
+
+- Long pressing on an artist cover gets you the same access to to heart/unheart an album
+
+
+
+
+
+
+## Playlist Management
+
+### Server Playlists
+**TODO**
+
+### Creating Playlists
+**TODO**
+
+## Settings
+
+
+## Android Auto
+
+### Enabling on your head unit
+- You have to enable Android Auto developer options, which are different from actual Android dev options. Then you have to enable "Unknown sources" in Android Auto, otherwise the app won't appear as it isn't downloaded from Play Store. (screenshots needed)
+
+
+### Server Settings
+**IN PROGRESS**
+- Manage multiple server connections
+- Configure sync intervals
+- Set data usage limits for streaming
+
+### Audio Settings
+**IN PROGRESS**
+- Streaming quality settings
+- Offline caching preferences
+
+### Appearance
+**TODO**
+
+## Troubleshooting
+
+### Connection Issues
+
+**TODO**
+
+### Common Issues
+
+**TODO**
+
+### Support
+For additional help:
+- Question? Start a [Discussion](https://github.com/eddyizm/tempus/discussions)
+- Open an [issue](https://github.com/eddyizm/tempus/issues) if you don't find a discussion solving your issue.
+- Consult your Subsonic server's documentation
+
+---
+
+*Note: This app requires a pre-existing Subsonic-compatible server with music content.*
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..3f3f577
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,133 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
+
+android {
+ compileSdk 35
+ buildToolsVersion = '35.0.0'
+
+ defaultConfig {
+ minSdkVersion 24
+ targetSdk 35
+
+ versionCode 6
+ versionName '4.2.4'
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments += [
+ "room.schemaLocation": "$projectDir/schemas".toString(),
+ "room.incremental" : "true"
+ ]
+ }
+ }
+
+ }
+
+ splits {
+ abi {
+ enable true
+ reset()
+ //noinspection ChromeOsAbiSupport
+ include 'armeabi-v7a', 'arm64-v8a'
+ universalApk false
+ }
+ }
+
+ dependenciesInfo {
+ // Disables dependency metadata when building APKs (for IzzyOnDroid/F-Droid)
+ includeInApk = false
+ // Disables dependency metadata when building Android App Bundles (for Google Play)
+ includeInBundle = false
+ }
+
+ flavorDimensions += "default"
+
+ productFlavors {
+ tempus {
+ dimension = "default"
+ applicationId 'com.eddyizm.tempus'
+ }
+
+ degoogled {
+ dimension = "default"
+ applicationId "com.eddyizm.degoogled.tempus"
+ }
+
+ }
+
+ buildTypes {
+ release {
+ shrinkResources true
+ minifyEnabled true
+ debuggable false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+
+ debug {
+ applicationIdSuffix ".debug"
+ debuggable true
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+
+ buildFeatures {
+ viewBinding true
+ buildConfig true
+ }
+
+ namespace 'com.cappielloantonio.tempo'
+}
+
+dependencies {
+ implementation files('../libs/lib-decoder-ffmpeg-release.aar')
+
+ // AndroidX
+ implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
+ implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
+ implementation 'androidx.preference:preference-ktx:1.2.1'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.8.6'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.8.6'
+ implementation 'androidx.recyclerview:recyclerview:1.4.0'
+ implementation 'androidx.room:room-runtime:2.6.1'
+ implementation 'androidx.core:core-splashscreen:1.0.1'
+ implementation 'androidx.appcompat:appcompat:1.7.0'
+
+ // Android Material
+ implementation 'com.google.android.material:material:1.10.0'
+
+ // Glide
+ implementation 'com.github.bumptech.glide:glide:4.16.0'
+ implementation 'com.github.bumptech.glide:annotations:4.16.0'
+
+ // Media3
+ implementation 'androidx.media3:media3-session:1.8.0'
+ implementation 'androidx.media3:media3-common:1.8.0'
+ implementation 'androidx.media3:media3-exoplayer:1.8.0'
+ implementation 'androidx.media3:media3-ui:1.8.0'
+ implementation 'androidx.media3:media3-exoplayer-hls:1.8.0'
+ tempusImplementation 'androidx.media3:media3-cast:1.8.0'
+
+
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
+ annotationProcessor 'androidx.room:room-compiler:2.6.1'
+
+ // Retrofit
+ implementation 'com.squareup.retrofit2:retrofit:2.11.0'
+ implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14'
+ implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
+}
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(17)
+ }
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..48b1f06
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,28 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keepattributes SourceFile, LineNumberTable
+-keep public class * extends java.lang.Exception
+-keep class retrofit2.** { *; }
+
+-keep class **.reflect.TypeToken { *; }
+-keep class * extends **.reflect.TypeToken
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/1.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/1.json
new file mode 100644
index 0000000..0ff270b
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/1.json
@@ -0,0 +1,746 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "1f4e50f90f58fb9cb53c89747d142fd9",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1f4e50f90f58fb9cb53c89747d142fd9')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json
new file mode 100644
index 0000000..e039aed
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/10.json
@@ -0,0 +1,1065 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 10,
+ "identityHash": "58cb958cdb09f054c27673d1de7f26d0",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `local_address` TEXT, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localAddress",
+ "columnName": "local_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "playlist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `duration` INTEGER NOT NULL, `coverArt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "coverArt",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '58cb958cdb09f054c27673d1de7f26d0')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/11.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/11.json
new file mode 100644
index 0000000..9febeba
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/11.json
@@ -0,0 +1,1101 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 11,
+ "identityHash": "cceefd0896d9f0e949a30b53dd682bee",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `local_address` TEXT, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localAddress",
+ "columnName": "local_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "playlist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `duration` INTEGER NOT NULL, `coverArt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "coverArt",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cceefd0896d9f0e949a30b53dd682bee')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/12.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/12.json
new file mode 100644
index 0000000..7797459
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/12.json
@@ -0,0 +1,1151 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 12,
+ "identityHash": "2d26471ae15a1cdaf996261b72f81613",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `local_address` TEXT, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localAddress",
+ "columnName": "local_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `sampling_rate` INTEGER, `bit_depth` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "samplingRate",
+ "columnName": "sampling_rate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitDepth",
+ "columnName": "bit_depth",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "playlist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `duration` INTEGER NOT NULL, `coverArt` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "coverArt",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "lyrics_cache",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`song_id` TEXT NOT NULL, `artist` TEXT, `title` TEXT, `lyrics` TEXT, `structured_lyrics` TEXT, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`song_id`))",
+ "fields": [
+ {
+ "fieldPath": "songId",
+ "columnName": "song_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lyrics",
+ "columnName": "lyrics",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "structuredLyrics",
+ "columnName": "structured_lyrics",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "updated_at",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "song_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2d26471ae15a1cdaf996261b72f81613')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/2.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/2.json
new file mode 100644
index 0000000..773c693
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/2.json
@@ -0,0 +1,790 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "ff99e331b4c34a82c560588c4dd5735f",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ff99e331b4c34a82c560588c4dd5735f')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/3.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/3.json
new file mode 100644
index 0000000..ef76d57
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/3.json
@@ -0,0 +1,797 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 3,
+ "identityHash": "6ea111217793c58d54eabb1190dd92ec",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6ea111217793c58d54eabb1190dd92ec')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/4.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/4.json
new file mode 100644
index 0000000..5356ab9
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/4.json
@@ -0,0 +1,997 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 4,
+ "identityHash": "528d037bee0f0575f8e0670ae1b04e00",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '528d037bee0f0575f8e0670ae1b04e00')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
new file mode 100644
index 0000000..e8e7e86
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
@@ -0,0 +1,1004 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 5,
+ "identityHash": "0e65e1c3fb44d9dc04c9c6cf35b7ea58",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL DEFAULT 0, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0e65e1c3fb44d9dc04c9c6cf35b7ea58')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
new file mode 100644
index 0000000..c6ccf4b
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
@@ -0,0 +1,1016 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 6,
+ "identityHash": "dff788fb3b6ff922a1f566a9752c2029",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER NOT NULL DEFAULT 0, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, PRIMARY KEY(`index`))",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dff788fb3b6ff922a1f566a9752c2029')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
new file mode 100644
index 0000000..a3286de
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
@@ -0,0 +1,1021 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 7,
+ "identityHash": "cca7b016c047d8fdc86dd6373f2fb173",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cca7b016c047d8fdc86dd6373f2fb173')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
new file mode 100644
index 0000000..26ab81b
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
@@ -0,0 +1,1021 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 8,
+ "identityHash": "cca7b016c047d8fdc86dd6373f2fb173",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cca7b016c047d8fdc86dd6373f2fb173')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/9.json b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/9.json
new file mode 100644
index 0000000..0bdcae2
--- /dev/null
+++ b/app/schemas/com.cappielloantonio.tempo.database.AppDatabase/9.json
@@ -0,0 +1,1027 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 9,
+ "identityHash": "237a704eed556782438a6493deadaed7",
+ "entities": [
+ {
+ "tableName": "queue",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackOrder",
+ "columnName": "track_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastPlay",
+ "columnName": "last_play",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playingChanged",
+ "columnName": "playing_changed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "track_order"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "server",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `local_address` TEXT, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverName",
+ "columnName": "server_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localAddress",
+ "columnName": "local_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isLowSecurity",
+ "columnName": "low_security",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "recent_search",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
+ "fields": [
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "search"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "download",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playlistName",
+ "columnName": "playlist_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "downloadState",
+ "columnName": "download_state",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "downloadUri",
+ "columnName": "download_uri",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "chronology",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "server",
+ "columnName": "server",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
+ "fields": [
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "songId",
+ "columnName": "songId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "albumId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artistId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "toStar",
+ "columnName": "toStar",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "timestamp"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "session_media_item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, `stream_id` TEXT, `stream_url` TEXT, `timestamp` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "parent_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isDir",
+ "columnName": "is_dir",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "album",
+ "columnName": "album",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artist",
+ "columnName": "artist",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "track",
+ "columnName": "track",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "year",
+ "columnName": "year",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "genre",
+ "columnName": "genre",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coverArtId",
+ "columnName": "cover_art_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "size",
+ "columnName": "size",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "suffix",
+ "columnName": "suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedContentType",
+ "columnName": "transcoding_content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "transcodedSuffix",
+ "columnName": "transcoded_suffix",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bitrate",
+ "columnName": "bitrate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isVideo",
+ "columnName": "is_video",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userRating",
+ "columnName": "user_rating",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "averageRating",
+ "columnName": "average_rating",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "playCount",
+ "columnName": "play_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "discNumber",
+ "columnName": "disc_number",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "starred",
+ "columnName": "starred",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "albumId",
+ "columnName": "album_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "artistId",
+ "columnName": "artist_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "bookmarkPosition",
+ "columnName": "bookmark_position",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalWidth",
+ "columnName": "original_width",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "originalHeight",
+ "columnName": "original_height",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamUrl",
+ "columnName": "stream_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "index"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '237a704eed556782438a6493deadaed7')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/degoogled/ic_launcher-playstore.png b/app/src/degoogled/ic_launcher-playstore.png
new file mode 100644
index 0000000..8709829
Binary files /dev/null and b/app/src/degoogled/ic_launcher-playstore.png differ
diff --git a/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt
new file mode 100644
index 0000000..5c4e939
--- /dev/null
+++ b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt
@@ -0,0 +1,561 @@
+package com.cappielloantonio.tempo.service
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.app.TaskStackBuilder
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.os.Binder
+import android.os.Bundle
+import android.os.IBinder
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import androidx.media3.common.*
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.DefaultLoadControl
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.exoplayer.source.MediaSource
+import androidx.media3.session.*
+import androidx.media3.session.MediaSession.ControllerInfo
+import com.cappielloantonio.tempo.R
+import com.cappielloantonio.tempo.repository.QueueRepository
+import com.cappielloantonio.tempo.ui.activity.MainActivity
+import com.cappielloantonio.tempo.util.AssetLinkUtil
+import com.cappielloantonio.tempo.util.Constants
+import com.cappielloantonio.tempo.util.DownloadUtil
+import com.cappielloantonio.tempo.util.DynamicMediaSourceFactory
+import com.cappielloantonio.tempo.util.MappingUtil
+import com.cappielloantonio.tempo.util.Preferences
+import com.cappielloantonio.tempo.util.ReplayGainUtil
+import com.cappielloantonio.tempo.widget.WidgetUpdateManager
+import com.google.common.collect.ImmutableList
+import com.google.common.util.concurrent.Futures
+import com.google.common.util.concurrent.ListenableFuture
+
+
+@UnstableApi
+class MediaService : MediaLibraryService() {
+ private val librarySessionCallback = CustomMediaLibrarySessionCallback()
+
+ private lateinit var player: ExoPlayer
+ private lateinit var mediaLibrarySession: MediaLibrarySession
+ private lateinit var shuffleCommands: List
+ private lateinit var repeatCommands: List
+ private lateinit var networkCallback: CustomNetworkCallback
+ lateinit var equalizerManager: EqualizerManager
+
+ private var customLayout = ImmutableList.of()
+ private val widgetUpdateHandler = Handler(Looper.getMainLooper())
+ private var widgetUpdateScheduled = false
+ private val widgetUpdateRunnable = object : Runnable {
+ override fun run() {
+ if (!player.isPlaying) {
+ widgetUpdateScheduled = false
+ return
+ }
+ updateWidget()
+ widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
+ }
+ }
+
+ inner class LocalBinder : Binder() {
+ fun getEqualizerManager(): EqualizerManager {
+ return this@MediaService.equalizerManager
+ }
+ }
+
+ private val binder = LocalBinder()
+
+ companion object {
+ private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
+ "android.media3.session.demo.SHUFFLE_ON"
+ private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
+ "android.media3.session.demo.SHUFFLE_OFF"
+ private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF =
+ "android.media3.session.demo.REPEAT_OFF"
+ private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE =
+ "android.media3.session.demo.REPEAT_ONE"
+ private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL =
+ "android.media3.session.demo.REPEAT_ALL"
+ const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
+ const val ACTION_EQUALIZER_UPDATED = "com.cappielloantonio.tempo.service.EQUALIZER_UPDATED"
+ }
+
+ fun updateMediaItems() {
+ Log.d("MediaService", "update items");
+ val n = player.mediaItemCount
+ val k = player.currentMediaItemIndex
+ val current = player.currentPosition
+ val items = (0 .. n-1).map{i -> MappingUtil.mapMediaItem(player.getMediaItemAt(i))}
+ player.clearMediaItems()
+ player.setMediaItems(items, k, current)
+ }
+
+ inner class CustomNetworkCallback : ConnectivityManager.NetworkCallback() {
+ var wasWifi = false
+
+ init {
+ val manager = getSystemService(ConnectivityManager::class.java)
+ val network = manager.activeNetwork
+ val capabilities = manager.getNetworkCapabilities(network)
+ if (capabilities != null)
+ wasWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ }
+
+ override fun onCapabilitiesChanged(network : Network, networkCapabilities : NetworkCapabilities) {
+ val isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ if (isWifi != wasWifi) {
+ wasWifi = isWifi
+ widgetUpdateHandler.post(Runnable {
+ updateMediaItems()
+ })
+ }
+ }
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+
+ initializeCustomCommands()
+ initializePlayer()
+ initializeMediaLibrarySession()
+ restorePlayerFromQueue()
+ initializePlayerListener()
+ initializeEqualizerManager()
+ initializeNetworkListener()
+
+ setPlayer(player)
+ }
+
+ override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
+ return mediaLibrarySession
+ }
+
+ override fun onDestroy() {
+ releaseNetworkCallback()
+ equalizerManager.release()
+ stopWidgetUpdates()
+ releasePlayer()
+ super.onDestroy()
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ // Check if the intent is for our custom equalizer binder
+ if (intent?.action == ACTION_BIND_EQUALIZER) {
+ return binder
+ }
+ // Otherwise, handle it as a normal MediaLibraryService connection
+ return super.onBind(intent)
+ }
+
+ private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
+
+ override fun onConnect(
+ session: MediaSession,
+ controller: ControllerInfo
+ ): MediaSession.ConnectionResult {
+ val connectionResult = super.onConnect(session, controller)
+ val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
+
+ (shuffleCommands + repeatCommands).forEach { commandButton ->
+ commandButton.sessionCommand?.let { availableSessionCommands.add(it) }
+ }
+
+ customLayout = buildCustomLayout(session.player)
+
+ return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
+ .setAvailableSessionCommands(availableSessionCommands.build())
+ .setAvailablePlayerCommands(connectionResult.availablePlayerCommands)
+ .setCustomLayout(customLayout)
+ .build()
+ }
+
+ override fun onPostConnect(session: MediaSession, controller: ControllerInfo) {
+ if (!customLayout.isEmpty() && controller.controllerVersion != 0) {
+ ignoreFuture(mediaLibrarySession.setCustomLayout(controller, customLayout))
+ }
+ }
+
+ fun buildCustomLayout(player: Player): ImmutableList {
+ val shuffle = shuffleCommands[if (player.shuffleModeEnabled) 1 else 0]
+ val repeat = when (player.repeatMode) {
+ Player.REPEAT_MODE_ONE -> repeatCommands[1]
+ Player.REPEAT_MODE_ALL -> repeatCommands[2]
+ else -> repeatCommands[0]
+ }
+ return ImmutableList.of(shuffle, repeat)
+ }
+
+ override fun onCustomCommand(
+ session: MediaSession,
+ controller: ControllerInfo,
+ customCommand: SessionCommand,
+ args: Bundle
+ ): ListenableFuture {
+ when (customCommand.customAction) {
+ CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON -> player.shuffleModeEnabled = true
+ CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF -> player.shuffleModeEnabled = false
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF,
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL,
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> {
+ val nextMode = when (player.repeatMode) {
+ Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_ALL
+ Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ONE
+ else -> Player.REPEAT_MODE_OFF
+ }
+ player.repeatMode = nextMode
+ }
+ }
+
+ customLayout = librarySessionCallback.buildCustomLayout(player)
+ session.setCustomLayout(customLayout)
+
+ return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
+ }
+
+ override fun onAddMediaItems(
+ mediaSession: MediaSession,
+ controller: ControllerInfo,
+ mediaItems: List
+ ): ListenableFuture> {
+ val updatedMediaItems = mediaItems.map { mediaItem ->
+ val mediaMetadata = mediaItem.mediaMetadata
+
+ val newMetadata = mediaMetadata.buildUpon()
+ .setArtist(
+ if (mediaMetadata.artist != null) mediaMetadata.artist
+ else mediaMetadata.extras?.getString("uri") ?: ""
+ )
+ .build()
+
+ mediaItem.buildUpon()
+ .setUri(mediaItem.requestMetadata.mediaUri)
+ .setMediaMetadata(newMetadata)
+ .setMimeType(MimeTypes.BASE_TYPE_AUDIO)
+ .build()
+ }
+ return Futures.immediateFuture(updatedMediaItems)
+ }
+ }
+
+ private fun initializeCustomCommands() {
+ shuffleCommands = listOf(
+ getShuffleCommandButton(
+ SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY)
+ ),
+ getShuffleCommandButton(
+ SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY)
+ )
+ )
+
+ repeatCommands = listOf(
+ getRepeatCommandButton(
+ SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF, Bundle.EMPTY)
+ ),
+ getRepeatCommandButton(
+ SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE, Bundle.EMPTY)
+ ),
+ getRepeatCommandButton(
+ SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL, Bundle.EMPTY)
+ )
+ )
+
+ customLayout = ImmutableList.of(shuffleCommands[0], repeatCommands[0])
+ }
+
+ private fun initializePlayer() {
+ player = ExoPlayer.Builder(this)
+ .setRenderersFactory(getRenderersFactory())
+ .setMediaSourceFactory(getMediaSourceFactory())
+ .setAudioAttributes(AudioAttributes.DEFAULT, true)
+ .setHandleAudioBecomingNoisy(true)
+ .setWakeMode(C.WAKE_MODE_NETWORK)
+ .setLoadControl(initializeLoadControl())
+ .build()
+
+ player.shuffleModeEnabled = Preferences.isShuffleModeEnabled()
+ player.repeatMode = Preferences.getRepeatMode()
+ }
+
+ private fun initializeEqualizerManager() {
+ equalizerManager = EqualizerManager()
+ val audioSessionId = player.audioSessionId
+ attachEqualizerIfPossible(audioSessionId)
+ }
+
+ private fun initializeMediaLibrarySession() {
+ val sessionActivityPendingIntent =
+ TaskStackBuilder.create(this).run {
+ addNextIntent(Intent(this@MediaService, MainActivity::class.java))
+ getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
+ }
+
+ mediaLibrarySession =
+ MediaLibrarySession.Builder(this, player, librarySessionCallback)
+ .setSessionActivity(sessionActivityPendingIntent)
+ .build()
+
+ if (!customLayout.isEmpty()) {
+ mediaLibrarySession.setCustomLayout(customLayout)
+ }
+ }
+
+ private fun initializeNetworkListener() {
+ networkCallback = CustomNetworkCallback()
+ getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(networkCallback)
+ updateMediaItems()
+ }
+
+ private fun restorePlayerFromQueue() {
+ if (player.mediaItemCount > 0) return
+
+ val queueRepository = QueueRepository()
+ val storedQueue = queueRepository.media
+ if (storedQueue.isNullOrEmpty()) return
+
+ val mediaItems = MappingUtil.mapMediaItems(storedQueue)
+ if (mediaItems.isEmpty()) return
+
+ val lastIndex = try {
+ queueRepository.lastPlayedMediaIndex
+ } catch (_: Exception) {
+ 0
+ }.coerceIn(0, mediaItems.size - 1)
+
+ val lastPosition = try {
+ queueRepository.lastPlayedMediaTimestamp
+ } catch (_: Exception) {
+ 0L
+ }.let { if (it < 0L) 0L else it }
+
+ player.setMediaItems(mediaItems, lastIndex, lastPosition)
+ player.prepare()
+ updateWidget()
+ }
+
+ private fun initializePlayerListener() {
+ player.addListener(object : Player.Listener {
+ override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
+ if (mediaItem == null) return
+
+ if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
+ MediaManager.setLastPlayedTimestamp(mediaItem)
+ }
+ updateWidget()
+ }
+
+ override fun onTracksChanged(tracks: Tracks) {
+ ReplayGainUtil.setReplayGain(player, tracks)
+ val currentMediaItem = player.currentMediaItem
+ if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) {
+ MediaManager.scrobble(currentMediaItem, false)
+ }
+
+ if (player.currentMediaItemIndex + 1 == player.mediaItemCount)
+ MediaManager.continuousPlay(player.currentMediaItem)
+ }
+
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
+ if (!isPlaying) {
+ MediaManager.setPlayingPausedTimestamp(
+ player.currentMediaItem,
+ player.currentPosition
+ )
+ } else {
+ MediaManager.scrobble(player.currentMediaItem, false)
+ }
+ if (isPlaying) {
+ scheduleWidgetUpdates()
+ } else {
+ stopWidgetUpdates()
+ }
+ updateWidget()
+ }
+
+ override fun onPlaybackStateChanged(playbackState: Int) {
+ super.onPlaybackStateChanged(playbackState)
+ if (!player.hasNextMediaItem() &&
+ playbackState == Player.STATE_ENDED &&
+ player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
+ ) {
+ MediaManager.scrobble(player.currentMediaItem, true)
+ MediaManager.saveChronology(player.currentMediaItem)
+ }
+ updateWidget()
+ }
+
+ override fun onPositionDiscontinuity(
+ oldPosition: Player.PositionInfo,
+ newPosition: Player.PositionInfo,
+ reason: Int
+ ) {
+ super.onPositionDiscontinuity(oldPosition, newPosition, reason)
+
+ if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
+ if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
+ MediaManager.scrobble(oldPosition.mediaItem, true)
+ MediaManager.saveChronology(oldPosition.mediaItem)
+ }
+
+ if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
+ MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
+ }
+ }
+ }
+
+ override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
+ Preferences.setShuffleModeEnabled(shuffleModeEnabled)
+ customLayout = librarySessionCallback.buildCustomLayout(player)
+ mediaLibrarySession.setCustomLayout(customLayout)
+ }
+
+ override fun onRepeatModeChanged(repeatMode: Int) {
+ Preferences.setRepeatMode(repeatMode)
+ customLayout = librarySessionCallback.buildCustomLayout(player)
+ mediaLibrarySession.setCustomLayout(customLayout)
+ }
+
+ override fun onAudioSessionIdChanged(audioSessionId: Int) {
+ attachEqualizerIfPossible(audioSessionId)
+ }
+ })
+ if (player.isPlaying) {
+ scheduleWidgetUpdates()
+ }
+ }
+
+ private fun setPlayer(player: Player) {
+ mediaLibrarySession.player = player
+ }
+
+ private fun releasePlayer() {
+ player.release()
+ mediaLibrarySession.release()
+ }
+
+ private fun releaseNetworkCallback() {
+ getSystemService(ConnectivityManager::class.java).unregisterNetworkCallback(networkCallback)
+ }
+
+ @SuppressLint("PrivateResource")
+ private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton {
+ val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON
+ return CommandButton.Builder()
+ .setDisplayName(
+ getString(
+ if (isOn) R.string.exo_controls_shuffle_on_description
+ else R.string.exo_controls_shuffle_off_description
+ )
+ )
+ .setSessionCommand(sessionCommand)
+ .setIconResId(if (isOn) R.drawable.exo_icon_shuffle_off else R.drawable.exo_icon_shuffle_on)
+ .build()
+ }
+
+ @SuppressLint("PrivateResource")
+ private fun getRepeatCommandButton(sessionCommand: SessionCommand): CommandButton {
+ val icon = when (sessionCommand.customAction) {
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> R.drawable.exo_icon_repeat_one
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> R.drawable.exo_icon_repeat_all
+ else -> R.drawable.exo_icon_repeat_off
+ }
+ val description = when (sessionCommand.customAction) {
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> R.string.exo_controls_repeat_one_description
+ CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL -> R.string.exo_controls_repeat_all_description
+ else -> R.string.exo_controls_repeat_off_description
+ }
+ return CommandButton.Builder()
+ .setDisplayName(getString(description))
+ .setSessionCommand(sessionCommand)
+ .setIconResId(icon)
+ .build()
+ }
+
+ private fun ignoreFuture(@Suppress("UNUSED_PARAMETER") customLayout: ListenableFuture) {
+ /* Do nothing. */
+ }
+
+ private fun initializeLoadControl(): DefaultLoadControl {
+ return DefaultLoadControl.Builder()
+ .setBufferDurationsMs(
+ (DefaultLoadControl.DEFAULT_MIN_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
+ (DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
+ DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
+ DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
+ )
+ .build()
+ }
+
+ private fun updateWidget() {
+ val mi = player.currentMediaItem
+ val title = mi?.mediaMetadata?.title?.toString()
+ ?: mi?.mediaMetadata?.extras?.getString("title")
+ val artist = mi?.mediaMetadata?.artist?.toString()
+ ?: mi?.mediaMetadata?.extras?.getString("artist")
+ val album = mi?.mediaMetadata?.albumTitle?.toString()
+ ?: mi?.mediaMetadata?.extras?.getString("album")
+ val extras = mi?.mediaMetadata?.extras
+ val coverId = extras?.getString("coverArtId")
+ val songLink = extras?.getString("assetLinkSong")
+ ?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_SONG, extras?.getString("id"))
+ val albumLink = extras?.getString("assetLinkAlbum")
+ ?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ALBUM, extras?.getString("albumId"))
+ val artistLink = extras?.getString("assetLinkArtist")
+ ?: AssetLinkUtil.buildLink(AssetLinkUtil.TYPE_ARTIST, extras?.getString("artistId"))
+ val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
+ val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
+ WidgetUpdateManager.updateFromState(
+ this,
+ title ?: "",
+ artist ?: "",
+ album ?: "",
+ coverId,
+ player.isPlaying,
+ player.shuffleModeEnabled,
+ player.repeatMode,
+ position,
+ duration,
+ songLink,
+ albumLink,
+ artistLink
+ )
+ }
+
+ private fun scheduleWidgetUpdates() {
+ if (widgetUpdateScheduled) return
+ widgetUpdateHandler.postDelayed(widgetUpdateRunnable, WIDGET_UPDATE_INTERVAL_MS)
+ widgetUpdateScheduled = true
+ }
+
+ private fun stopWidgetUpdates() {
+ if (!widgetUpdateScheduled) return
+ widgetUpdateHandler.removeCallbacks(widgetUpdateRunnable)
+ widgetUpdateScheduled = false
+ }
+
+ private fun attachEqualizerIfPossible(audioSessionId: Int): Boolean {
+ if (audioSessionId == 0 || audioSessionId == -1) return false
+ val attached = equalizerManager.attachToSession(audioSessionId)
+ if (attached) {
+ val enabled = Preferences.isEqualizerEnabled()
+ equalizerManager.setEnabled(enabled)
+ val bands = equalizerManager.getNumberOfBands()
+ val savedLevels = Preferences.getEqualizerBandLevels(bands)
+ for (i in 0 until bands) {
+ equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
+ }
+ sendBroadcast(Intent(ACTION_EQUALIZER_UPDATED))
+ }
+ return attached
+ }
+
+ private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
+
+ private fun getMediaSourceFactory(): MediaSource.Factory = DynamicMediaSourceFactory(this)
+}
+
+private const val WIDGET_UPDATE_INTERVAL_MS = 1000L
diff --git a/app/src/degoogled/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java b/app/src/degoogled/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java
new file mode 100644
index 0000000..2ad74c8
--- /dev/null
+++ b/app/src/degoogled/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java
@@ -0,0 +1,65 @@
+package com.cappielloantonio.tempo.ui.fragment;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.media3.common.util.UnstableApi;
+
+import com.cappielloantonio.tempo.R;
+import com.cappielloantonio.tempo.databinding.FragmentToolbarBinding;
+import com.cappielloantonio.tempo.ui.activity.MainActivity;
+
+@UnstableApi
+public class ToolbarFragment extends Fragment {
+ private static final String TAG = "ToolbarFragment";
+
+ private FragmentToolbarBinding bind;
+ private MainActivity activity;
+
+ public ToolbarFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.main_page_menu, menu);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ activity = (MainActivity) getActivity();
+
+ bind = FragmentToolbarBinding.inflate(inflater, container, false);
+ View view = bind.getRoot();
+
+ return view;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == R.id.action_search) {
+ activity.navController.navigate(R.id.searchFragment);
+ return true;
+ } else if (item.getItemId() == R.id.action_settings) {
+ activity.navController.navigate(R.id.settingsFragment);
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/app/src/degoogled/java/com/cappielloantonio/tempo/util/Flavors.java b/app/src/degoogled/java/com/cappielloantonio/tempo/util/Flavors.java
new file mode 100644
index 0000000..68d2350
--- /dev/null
+++ b/app/src/degoogled/java/com/cappielloantonio/tempo/util/Flavors.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.util;
+
+import android.content.Context;
+
+public class Flavors {
+ public static void initializeCastContext(Context context) {
+
+ }
+}
diff --git a/app/src/degoogled/res/drawable/ic_launcher_foreground.xml b/app/src/degoogled/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2f629d4
--- /dev/null
+++ b/app/src/degoogled/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/degoogled/res/drawable/ic_splash_logo.xml b/app/src/degoogled/res/drawable/ic_splash_logo.xml
new file mode 100644
index 0000000..cc4ea53
--- /dev/null
+++ b/app/src/degoogled/res/drawable/ic_splash_logo.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/degoogled/res/menu/main_page_menu.xml b/app/src/degoogled/res/menu/main_page_menu.xml
new file mode 100644
index 0000000..4016fef
--- /dev/null
+++ b/app/src/degoogled/res/menu/main_page_menu.xml
@@ -0,0 +1,15 @@
+
+
\ No newline at end of file
diff --git a/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..7353dbd
--- /dev/null
+++ b/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..7353dbd
--- /dev/null
+++ b/app/src/degoogled/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/degoogled/res/mipmap-hdpi/ic_launcher.webp b/app/src/degoogled/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..411c361
Binary files /dev/null and b/app/src/degoogled/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/degoogled/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/degoogled/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..fbbe2e1
Binary files /dev/null and b/app/src/degoogled/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/degoogled/res/mipmap-mdpi/ic_launcher.webp b/app/src/degoogled/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..940d52f
Binary files /dev/null and b/app/src/degoogled/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/degoogled/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/degoogled/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..dd79b42
Binary files /dev/null and b/app/src/degoogled/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/degoogled/res/mipmap-xhdpi/ic_launcher.webp b/app/src/degoogled/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..f502e42
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/degoogled/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/degoogled/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..40a9c5a
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..7454bb0
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..0b81536
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..d8ed149
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1a6a93a
Binary files /dev/null and b/app/src/degoogled/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/degoogled/res/values/ic_launcher_background.xml b/app/src/degoogled/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..dacc1d2
--- /dev/null
+++ b/app/src/degoogled/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #626A75
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b8d72d8
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..b086779
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/cappielloantonio/tempo/App.java b/app/src/main/java/com/cappielloantonio/tempo/App.java
new file mode 100644
index 0000000..40105ee
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/App.java
@@ -0,0 +1,109 @@
+package com.cappielloantonio.tempo;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceManager;
+
+import com.cappielloantonio.tempo.github.Github;
+import com.cappielloantonio.tempo.helper.ThemeHelper;
+import com.cappielloantonio.tempo.subsonic.Subsonic;
+import com.cappielloantonio.tempo.subsonic.SubsonicPreferences;
+import com.cappielloantonio.tempo.util.Preferences;
+
+public class App extends Application {
+ private static App instance;
+ private static Context context;
+ private static Subsonic subsonic;
+ private static Github github;
+ private static SharedPreferences preferences;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ String themePref = sharedPreferences.getString(Preferences.THEME, ThemeHelper.DEFAULT_MODE);
+ ThemeHelper.applyTheme(themePref);
+
+ instance = new App();
+ context = getApplicationContext();
+ preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static App getInstance() {
+ if (instance == null) {
+ instance = new App();
+ }
+
+ return instance;
+ }
+
+ public static Context getContext() {
+ if (context == null) {
+ context = getInstance();
+ }
+
+ return context;
+ }
+
+ public static Subsonic getSubsonicClientInstance(boolean override) {
+ if (subsonic == null || override) {
+ subsonic = getSubsonicClient();
+ }
+ return subsonic;
+ }
+
+ public static Github getGithubClientInstance() {
+ if (github == null) {
+ github = new Github();
+ }
+ return github;
+ }
+
+ public SharedPreferences getPreferences() {
+ if (preferences == null) {
+ preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ return preferences;
+ }
+
+ public static void refreshSubsonicClient() {
+ subsonic = getSubsonicClient();
+ }
+
+ private static Subsonic getSubsonicClient() {
+ SubsonicPreferences preferences = getSubsonicPreferences();
+
+ if (preferences.getAuthentication() != null) {
+ if (preferences.getAuthentication().getPassword() != null)
+ Preferences.setPassword(preferences.getAuthentication().getPassword());
+ if (preferences.getAuthentication().getToken() != null)
+ Preferences.setToken(preferences.getAuthentication().getToken());
+ if (preferences.getAuthentication().getSalt() != null)
+ Preferences.setSalt(preferences.getAuthentication().getSalt());
+ }
+
+ return new Subsonic(preferences);
+ }
+
+ @NonNull
+ private static SubsonicPreferences getSubsonicPreferences() {
+ String server = Preferences.getInUseServerAddress();
+ String username = Preferences.getUser();
+ String password = Preferences.getPassword();
+ String token = Preferences.getToken();
+ String salt = Preferences.getSalt();
+ boolean isLowSecurity = Preferences.isLowScurity();
+
+ SubsonicPreferences preferences = new SubsonicPreferences();
+ preferences.setServerUrl(server);
+ preferences.setUsername(username);
+ preferences.setAuthentication(password, token, salt, isLowSecurity);
+
+ return preferences;
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/broadcast/receiver/ConnectivityStatusBroadcastReceiver.java b/app/src/main/java/com/cappielloantonio/tempo/broadcast/receiver/ConnectivityStatusBroadcastReceiver.java
new file mode 100644
index 0000000..21c34fd
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/broadcast/receiver/ConnectivityStatusBroadcastReceiver.java
@@ -0,0 +1,34 @@
+package com.cappielloantonio.tempo.broadcast.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.view.View;
+
+import androidx.annotation.OptIn;
+import androidx.media3.common.util.UnstableApi;
+
+import com.cappielloantonio.tempo.ui.activity.MainActivity;
+
+@OptIn(markerClass = UnstableApi.class)
+public class ConnectivityStatusBroadcastReceiver extends BroadcastReceiver {
+ private final MainActivity activity;
+
+ public ConnectivityStatusBroadcastReceiver(MainActivity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+
+ if (noConnectivity) {
+ activity.bind.offlineModeTextView.setVisibility(View.VISIBLE);
+ } else {
+ activity.bind.offlineModeTextView.setVisibility(View.GONE);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/AppDatabase.java b/app/src/main/java/com/cappielloantonio/tempo/database/AppDatabase.java
new file mode 100644
index 0000000..3a5e98e
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/AppDatabase.java
@@ -0,0 +1,69 @@
+package com.cappielloantonio.tempo.database;
+
+import androidx.media3.common.util.UnstableApi;
+import androidx.room.AutoMigration;
+import androidx.room.Database;
+import androidx.room.Room;
+import androidx.room.RoomDatabase;
+import androidx.room.TypeConverters;
+
+import com.cappielloantonio.tempo.App;
+import com.cappielloantonio.tempo.database.converter.DateConverters;
+import com.cappielloantonio.tempo.database.dao.ChronologyDao;
+import com.cappielloantonio.tempo.database.dao.DownloadDao;
+import com.cappielloantonio.tempo.database.dao.FavoriteDao;
+import com.cappielloantonio.tempo.database.dao.LyricsDao;
+import com.cappielloantonio.tempo.database.dao.PlaylistDao;
+import com.cappielloantonio.tempo.database.dao.QueueDao;
+import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
+import com.cappielloantonio.tempo.database.dao.ServerDao;
+import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
+import com.cappielloantonio.tempo.model.Chronology;
+import com.cappielloantonio.tempo.model.Download;
+import com.cappielloantonio.tempo.model.Favorite;
+import com.cappielloantonio.tempo.model.LyricsCache;
+import com.cappielloantonio.tempo.model.Queue;
+import com.cappielloantonio.tempo.model.RecentSearch;
+import com.cappielloantonio.tempo.model.Server;
+import com.cappielloantonio.tempo.model.SessionMediaItem;
+import com.cappielloantonio.tempo.subsonic.models.Playlist;
+
+@UnstableApi
+@Database(
+ version = 12,
+ entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class, Playlist.class, LyricsCache.class},
+ autoMigrations = {@AutoMigration(from = 10, to = 11), @AutoMigration(from = 11, to = 12)}
+)
+@TypeConverters({DateConverters.class})
+public abstract class AppDatabase extends RoomDatabase {
+ private final static String DB_NAME = "tempo_db";
+ private static AppDatabase instance;
+
+ public static synchronized AppDatabase getInstance() {
+ if (instance == null) {
+ instance = Room.databaseBuilder(App.getContext(), AppDatabase.class, DB_NAME)
+ .fallbackToDestructiveMigration()
+ .build();
+ }
+
+ return instance;
+ }
+
+ public abstract QueueDao queueDao();
+
+ public abstract ServerDao serverDao();
+
+ public abstract RecentSearchDao recentSearchDao();
+
+ public abstract DownloadDao downloadDao();
+
+ public abstract ChronologyDao chronologyDao();
+
+ public abstract FavoriteDao favoriteDao();
+
+ public abstract SessionMediaItemDao sessionMediaItemDao();
+
+ public abstract PlaylistDao playlistDao();
+
+ public abstract LyricsDao lyricsDao();
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/converter/DateConverters.kt b/app/src/main/java/com/cappielloantonio/tempo/database/converter/DateConverters.kt
new file mode 100644
index 0000000..132874b
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/converter/DateConverters.kt
@@ -0,0 +1,16 @@
+package com.cappielloantonio.tempo.database.converter
+
+import androidx.room.TypeConverter
+import java.util.*
+
+class DateConverters {
+ @TypeConverter
+ fun fromTimestamp(value: Long?): Date? {
+ return value?.let { Date(it) }
+ }
+
+ @TypeConverter
+ fun dateToTimestamp(date: Date?): Long? {
+ return date?.time
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/ChronologyDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/ChronologyDao.java
new file mode 100644
index 0000000..1bb02ea
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/ChronologyDao.java
@@ -0,0 +1,23 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Chronology;
+
+import java.util.List;
+
+@Dao
+public interface ChronologyDao {
+ @Query("SELECT * FROM chronology WHERE server == :server GROUP BY id ORDER BY timestamp DESC LIMIT :count")
+ LiveData> getLastPlayed(String server, int count);
+
+ @Query("SELECT * FROM chronology WHERE timestamp >= :endDate AND timestamp < :startDate AND server == :server GROUP BY id ORDER BY COUNT(id) DESC LIMIT 20")
+ LiveData> getAllFrom(long startDate, long endDate, String server);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Chronology chronologyObject);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/DownloadDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/DownloadDao.java
new file mode 100644
index 0000000..a2d49f6
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/DownloadDao.java
@@ -0,0 +1,41 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Download;
+
+import java.util.List;
+
+@Dao
+public interface DownloadDao {
+ @Query("SELECT * FROM download WHERE download_state = 1 ORDER BY artist, album, disc_number, track ASC")
+ LiveData> getAll();
+
+ @Query("SELECT * FROM download WHERE download_state = 1 ORDER BY artist, album, disc_number, track ASC")
+ List getAllSync();
+
+ @Query("SELECT * FROM download WHERE id = :id")
+ Download getOne(String id);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Download download);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertAll(List downloads);
+
+ @Query("UPDATE download SET download_state = 1 WHERE id = :id")
+ void update(String id);
+
+ @Query("DELETE FROM download WHERE id = :id")
+ void delete(String id);
+
+ @Query("DELETE FROM download WHERE id IN (:ids)")
+ void deleteByIds(List ids);
+
+ @Query("DELETE FROM download")
+ void deleteAll();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/FavoriteDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/FavoriteDao.java
new file mode 100644
index 0000000..ec6ae68
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/FavoriteDao.java
@@ -0,0 +1,26 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Favorite;
+
+import java.util.List;
+
+@Dao
+public interface FavoriteDao {
+ @Query("SELECT * FROM favorite")
+ List getAll();
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ void insert(Favorite favorite);
+
+ @Delete
+ void delete(Favorite favorite);
+
+ @Query("DELETE FROM favorite")
+ void deleteAll();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/LyricsDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/LyricsDao.java
new file mode 100644
index 0000000..89d0d58
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/LyricsDao.java
@@ -0,0 +1,24 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.LyricsCache;
+
+@Dao
+public interface LyricsDao {
+ @Query("SELECT * FROM lyrics_cache WHERE song_id = :songId")
+ LyricsCache getOne(String songId);
+
+ @Query("SELECT * FROM lyrics_cache WHERE song_id = :songId")
+ LiveData observeOne(String songId);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(LyricsCache lyricsCache);
+
+ @Query("DELETE FROM lyrics_cache WHERE song_id = :songId")
+ void delete(String songId);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/PlaylistDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/PlaylistDao.java
new file mode 100644
index 0000000..52e025d
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/PlaylistDao.java
@@ -0,0 +1,27 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.subsonic.models.Playlist;
+
+import java.util.List;
+
+@Dao
+public interface PlaylistDao {
+ // @Query("SELECT * FROM playlist WHERE server=:serverId")
+ // LiveData> getAll(String serverId);
+
+ @Query("SELECT * FROM playlist")
+ LiveData> getAll();
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Playlist playlist);
+
+ @Delete
+ void delete(Playlist playlist);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/QueueDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/QueueDao.java
new file mode 100644
index 0000000..4c507b2
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/QueueDao.java
@@ -0,0 +1,44 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Queue;
+
+import java.util.List;
+
+@Dao
+public interface QueueDao {
+ @Query("SELECT * FROM queue")
+ LiveData> getAll();
+
+ @Query("SELECT * FROM queue")
+ List getAllSimple();
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Queue songQueueObject);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertAll(List songQueueObjects);
+
+ @Query("DELETE FROM queue WHERE queue.track_order=:position")
+ void delete(int position);
+
+ @Query("DELETE FROM queue")
+ void deleteAll();
+
+ @Query("SELECT COUNT(*) FROM queue")
+ int count();
+
+ @Query("UPDATE queue SET last_play=:timestamp WHERE id=:id")
+ void setLastPlay(String id, long timestamp);
+
+ @Query("UPDATE queue SET playing_changed=:timestamp WHERE id=:id")
+ void setPlayingChanged(String id, long timestamp);
+
+ @Query("SELECT * FROM queue ORDER BY last_play DESC LIMIT 1")
+ Queue getLastPlayed();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/RecentSearchDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/RecentSearchDao.java
new file mode 100644
index 0000000..b1bd6c0
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/RecentSearchDao.java
@@ -0,0 +1,23 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.RecentSearch;
+
+import java.util.List;
+
+@Dao
+public interface RecentSearchDao {
+ @Query("SELECT * FROM recent_search ORDER BY search DESC")
+ List getRecent();
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(RecentSearch search);
+
+ @Delete
+ void delete(RecentSearch search);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/ServerDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/ServerDao.java
new file mode 100644
index 0000000..b4a6bbc
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/ServerDao.java
@@ -0,0 +1,24 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Server;
+
+import java.util.List;
+
+@Dao
+public interface ServerDao {
+ @Query("SELECT * FROM server")
+ LiveData> getAll();
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insert(Server server);
+
+ @Delete
+ void delete(Server server);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/database/dao/SessionMediaItemDao.java b/app/src/main/java/com/cappielloantonio/tempo/database/dao/SessionMediaItemDao.java
new file mode 100644
index 0000000..a3f415a
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/database/dao/SessionMediaItemDao.java
@@ -0,0 +1,29 @@
+package com.cappielloantonio.tempo.database.dao;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import com.cappielloantonio.tempo.model.Queue;
+import com.cappielloantonio.tempo.model.SessionMediaItem;
+
+import java.util.List;
+
+@Dao
+public interface SessionMediaItemDao {
+ @Query("SELECT * FROM session_media_item WHERE id = :id")
+ SessionMediaItem get(String id);
+
+ @Query("SELECT * FROM session_media_item WHERE timestamp = :timestamp")
+ List get(long timestamp);
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ void insert(SessionMediaItem sessionMediaItem);
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ void insertAll(List sessionMediaItems);
+
+ @Query("DELETE FROM session_media_item")
+ void deleteAll();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/Github.java b/app/src/main/java/com/cappielloantonio/tempo/github/Github.java
new file mode 100644
index 0000000..f0b0de4
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/Github.java
@@ -0,0 +1,29 @@
+package com.cappielloantonio.tempo.github;
+
+import com.cappielloantonio.tempo.github.api.release.ReleaseClient;
+
+public class Github {
+ private static final String OWNER = "eddyizm";
+ private static final String REPO = "Tempus";
+ private ReleaseClient releaseClient;
+
+ public ReleaseClient getReleaseClient() {
+ if (releaseClient == null) {
+ releaseClient = new ReleaseClient(this);
+ }
+
+ return releaseClient;
+ }
+
+ public String getUrl() {
+ return "https://api.github.com/";
+ }
+
+ public static String getOwner() {
+ return OWNER;
+ }
+
+ public static String getRepo() {
+ return REPO;
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/GithubRetrofitClient.kt b/app/src/main/java/com/cappielloantonio/tempo/github/GithubRetrofitClient.kt
new file mode 100644
index 0000000..b9d1035
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/GithubRetrofitClient.kt
@@ -0,0 +1,30 @@
+package com.cappielloantonio.tempo.github
+
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+class GithubRetrofitClient(github: Github) {
+ var retrofit: Retrofit
+
+ init {
+ retrofit = Retrofit.Builder()
+ .baseUrl(github.url)
+ .addConverterFactory(GsonConverterFactory.create())
+ .client(getOkHttpClient())
+ .build()
+ }
+
+ private fun getOkHttpClient(): OkHttpClient {
+ return OkHttpClient.Builder()
+ .addInterceptor(getHttpLoggingInterceptor())
+ .build()
+ }
+
+ private fun getHttpLoggingInterceptor(): HttpLoggingInterceptor {
+ val loggingInterceptor = HttpLoggingInterceptor()
+ loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
+ return loggingInterceptor
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseClient.java b/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseClient.java
new file mode 100644
index 0000000..9bcf410
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseClient.java
@@ -0,0 +1,24 @@
+package com.cappielloantonio.tempo.github.api.release;
+
+import android.util.Log;
+
+import com.cappielloantonio.tempo.github.Github;
+import com.cappielloantonio.tempo.github.GithubRetrofitClient;
+import com.cappielloantonio.tempo.github.models.LatestRelease;
+
+import retrofit2.Call;
+
+public class ReleaseClient {
+ private static final String TAG = "ReleaseClient";
+
+ private final ReleaseService releaseService;
+
+ public ReleaseClient(Github github) {
+ this.releaseService = new GithubRetrofitClient(github).getRetrofit().create(ReleaseService.class);
+ }
+
+ public Call getLatestRelease() {
+ Log.d(TAG, "getLatestRelease()");
+ return releaseService.getLatestRelease(Github.getOwner(), Github.getRepo());
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseService.java b/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseService.java
new file mode 100644
index 0000000..be6faf3
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/api/release/ReleaseService.java
@@ -0,0 +1,12 @@
+package com.cappielloantonio.tempo.github.api.release;
+
+import com.cappielloantonio.tempo.github.models.LatestRelease;
+
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Path;
+
+public interface ReleaseService {
+ @GET("repos/{owner}/{repo}/releases/latest")
+ Call getLatestRelease(@Path("owner") String owner, @Path("repo") String repo);
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/models/Assets.kt b/app/src/main/java/com/cappielloantonio/tempo/github/models/Assets.kt
new file mode 100644
index 0000000..6e815a7
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/models/Assets.kt
@@ -0,0 +1,34 @@
+package com.cappielloantonio.tempo.github.models
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Assets(
+ @SerializedName("url")
+ var url: String? = null,
+ @SerializedName("id")
+ var id: Int? = null,
+ @SerializedName("node_id")
+ var nodeId: String? = null,
+ @SerializedName("name")
+ var name: String? = null,
+ @SerializedName("label")
+ var label: String? = null,
+ @SerializedName("uploader")
+ var uploader: Uploader? = Uploader(),
+ @SerializedName("content_type")
+ var contentType: String? = null,
+ @SerializedName("state")
+ var state: String? = null,
+ @SerializedName("size")
+ var size: Int? = null,
+ @SerializedName("download_count")
+ var downloadCount: Int? = null,
+ @SerializedName("created_at")
+ var createdAt: String? = null,
+ @SerializedName("updated_at")
+ var updatedAt: String? = null,
+ @SerializedName("browser_download_url")
+ var browserDownloadUrl: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/models/Author.kt b/app/src/main/java/com/cappielloantonio/tempo/github/models/Author.kt
new file mode 100644
index 0000000..996da2d
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/models/Author.kt
@@ -0,0 +1,44 @@
+package com.cappielloantonio.tempo.github.models
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Author(
+ @SerializedName("login")
+ var login: String? = null,
+ @SerializedName("id")
+ var id: Int? = null,
+ @SerializedName("node_id")
+ var nodeId: String? = null,
+ @SerializedName("avatar_url")
+ var avatarUrl: String? = null,
+ @SerializedName("gravatar_id")
+ var gravatarId: String? = null,
+ @SerializedName("url")
+ var url: String? = null,
+ @SerializedName("html_url")
+ var htmlUrl: String? = null,
+ @SerializedName("followers_url")
+ var followersUrl: String? = null,
+ @SerializedName("following_url")
+ var followingUrl: String? = null,
+ @SerializedName("gists_url")
+ var gistsUrl: String? = null,
+ @SerializedName("starred_url")
+ var starredUrl: String? = null,
+ @SerializedName("subscriptions_url")
+ var subscriptionsUrl: String? = null,
+ @SerializedName("organizations_url")
+ var organizationsUrl: String? = null,
+ @SerializedName("repos_url")
+ var reposUrl: String? = null,
+ @SerializedName("events_url")
+ var eventsUrl: String? = null,
+ @SerializedName("received_events_url")
+ var receivedEventsUrl: String? = null,
+ @SerializedName("type")
+ var type: String? = null,
+ @SerializedName("site_admin")
+ var siteAdmin: Boolean? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/models/LatestRelease.kt b/app/src/main/java/com/cappielloantonio/tempo/github/models/LatestRelease.kt
new file mode 100644
index 0000000..ae4a8ec
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/models/LatestRelease.kt
@@ -0,0 +1,46 @@
+package com.cappielloantonio.tempo.github.models
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class LatestRelease(
+ @SerializedName("url")
+ var url: String? = null,
+ @SerializedName("assets_url")
+ var assetsUrl: String? = null,
+ @SerializedName("upload_url")
+ var uploadUrl: String? = null,
+ @SerializedName("html_url")
+ var htmlUrl: String? = null,
+ @SerializedName("id")
+ var id: Int? = null,
+ @SerializedName("author")
+ var author: Author? = Author(),
+ @SerializedName("node_id")
+ var nodeId: String? = null,
+ @SerializedName("tag_name")
+ var tagName: String? = null,
+ @SerializedName("target_commitish")
+ var targetCommitish: String? = null,
+ @SerializedName("name")
+ var name: String? = null,
+ @SerializedName("draft")
+ var draft: Boolean? = null,
+ @SerializedName("prerelease")
+ var prerelease: Boolean? = null,
+ @SerializedName("created_at")
+ var createdAt: String? = null,
+ @SerializedName("published_at")
+ var publishedAt: String? = null,
+ @SerializedName("assets")
+ var assets: ArrayList = arrayListOf(),
+ @SerializedName("tarball_url")
+ var tarballUrl: String? = null,
+ @SerializedName("zipball_url")
+ var zipballUrl: String? = null,
+ @SerializedName("body")
+ var body: String? = null,
+ @SerializedName("reactions")
+ var reactions: Reactions? = Reactions()
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/models/Reactions.kt b/app/src/main/java/com/cappielloantonio/tempo/github/models/Reactions.kt
new file mode 100644
index 0000000..21f3f90
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/models/Reactions.kt
@@ -0,0 +1,28 @@
+package com.cappielloantonio.tempo.github.models
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Reactions(
+ @SerializedName("url")
+ var url: String? = null,
+ @SerializedName("total_count")
+ var totalCount: Int? = null,
+ @SerializedName("+1")
+ var like: Int? = null,
+ @SerializedName("-1")
+ var dislike: Int? = null,
+ @SerializedName("laugh")
+ var laugh: Int? = null,
+ @SerializedName("hooray")
+ var hooray: Int? = null,
+ @SerializedName("confused")
+ var confused: Int? = null,
+ @SerializedName("heart")
+ var heart: Int? = null,
+ @SerializedName("rocket")
+ var rocket: Int? = null,
+ @SerializedName("eyes")
+ var eyes: Int? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/models/Uploader.kt b/app/src/main/java/com/cappielloantonio/tempo/github/models/Uploader.kt
new file mode 100644
index 0000000..e0c74ec
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/models/Uploader.kt
@@ -0,0 +1,44 @@
+package com.cappielloantonio.tempo.github.models
+
+import androidx.annotation.Keep
+import com.google.gson.annotations.SerializedName
+
+@Keep
+data class Uploader(
+ @SerializedName("login")
+ var login: String? = null,
+ @SerializedName("id")
+ var id: Int? = null,
+ @SerializedName("node_id")
+ var nodeId: String? = null,
+ @SerializedName("avatar_url")
+ var avatarUrl: String? = null,
+ @SerializedName("gravatar_id")
+ var gravatarId: String? = null,
+ @SerializedName("url")
+ var url: String? = null,
+ @SerializedName("html_url")
+ var htmlUrl: String? = null,
+ @SerializedName("followers_url")
+ var followersUrl: String? = null,
+ @SerializedName("following_url")
+ var followingUrl: String? = null,
+ @SerializedName("gists_url")
+ var gistsUrl: String? = null,
+ @SerializedName("starred_url")
+ var starredUrl: String? = null,
+ @SerializedName("subscriptions_url")
+ var subscriptionsUrl: String? = null,
+ @SerializedName("organizations_url")
+ var organizationsUrl: String? = null,
+ @SerializedName("repos_url")
+ var reposUrl: String? = null,
+ @SerializedName("events_url")
+ var eventsUrl: String? = null,
+ @SerializedName("received_events_url")
+ var receivedEventsUrl: String? = null,
+ @SerializedName("type")
+ var type: String? = null,
+ @SerializedName("site_admin")
+ var siteAdmin: Boolean? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/github/utils/UpdateUtil.java b/app/src/main/java/com/cappielloantonio/tempo/github/utils/UpdateUtil.java
new file mode 100644
index 0000000..a4e61fb
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/github/utils/UpdateUtil.java
@@ -0,0 +1,32 @@
+package com.cappielloantonio.tempo.github.utils;
+
+import com.cappielloantonio.tempo.BuildConfig;
+import com.cappielloantonio.tempo.github.models.LatestRelease;
+
+public class UpdateUtil {
+
+ public static boolean showUpdateDialog(LatestRelease release) {
+ if (release.getTagName() == null) return false;
+ String remoteTag = release.getTagName().replaceAll("^\\D+", "");
+
+ try {
+ String[] local = BuildConfig.VERSION_NAME.split("\\.");
+ String[] remote = remoteTag.split("\\.");
+
+ for (int i = 0; i < local.length; i++) {
+ int localPart = Integer.parseInt(local[i]);
+ int remotePart = Integer.parseInt(remote[i]);
+
+ if (localPart > remotePart) {
+ return false;
+ } else if (localPart < remotePart) {
+ return true;
+ }
+ }
+ } catch (Exception exception) {
+ return false;
+ }
+
+ return false;
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideModule.java b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideModule.java
new file mode 100644
index 0000000..ccbffb2
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideModule.java
@@ -0,0 +1,32 @@
+package com.cappielloantonio.tempo.glide;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.annotation.GlideModule;
+import com.bumptech.glide.load.DecodeFormat;
+import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
+import com.bumptech.glide.Registry;
+import com.bumptech.glide.module.AppGlideModule;
+import com.bumptech.glide.request.RequestOptions;
+import com.cappielloantonio.tempo.util.Preferences;
+
+import java.io.InputStream;
+
+@GlideModule
+public class CustomGlideModule extends AppGlideModule {
+ @Override
+ public void applyOptions(@NonNull Context context, GlideBuilder builder) {
+ int diskCacheSize = Preferences.getImageCacheSize() * 1024 * 1024;
+ builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "cache", diskCacheSize));
+ builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));
+ }
+
+ @Override
+ public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
+ registry.replace(String.class, InputStream.class, new IPv6StringLoader.Factory());
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java
new file mode 100644
index 0000000..a6e650e
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java
@@ -0,0 +1,150 @@
+package com.cappielloantonio.tempo.glide;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
+import com.bumptech.glide.RequestManager;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.CustomTarget;
+import com.bumptech.glide.signature.ObjectKey;
+import com.cappielloantonio.tempo.App;
+import com.cappielloantonio.tempo.R;
+import com.cappielloantonio.tempo.util.Preferences;
+import com.cappielloantonio.tempo.util.Util;
+import com.google.android.material.elevation.SurfaceColors;
+
+import java.util.Map;
+
+public class CustomGlideRequest {
+ private static final String TAG = "CustomGlideRequest";
+
+ public static final int CORNER_RADIUS = Preferences.isCornerRoundingEnabled() ? Preferences.getRoundedCornerSize() : 1;
+
+ public static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
+
+ public enum ResourceType {
+ Unknown,
+ Album,
+ Artist,
+ Folder,
+ Directory,
+ Playlist,
+ Podcast,
+ Radio,
+ Song,
+ }
+
+ public static RequestOptions createRequestOptions(Context context, String item, ResourceType type) {
+ return new RequestOptions()
+ .placeholder(new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context)))
+ .fallback(getPlaceholder(context, type))
+ .error(getPlaceholder(context, type))
+ .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
+ .signature(new ObjectKey(item != null ? item : 0))
+ .transform(new CenterCrop(), new RoundedCorners(CustomGlideRequest.CORNER_RADIUS));
+ }
+
+ @Nullable
+ private static Drawable getPlaceholder(Context context, ResourceType type) {
+ switch (type) {
+ case Album:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_album);
+ case Artist:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_artist);
+ case Folder:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_folder);
+ case Directory:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_directory);
+ case Playlist:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_playlist);
+ case Podcast:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_podcast);
+ case Radio:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_radio);
+ case Song:
+ return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_song);
+ default:
+ case Unknown:
+ return new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context));
+ }
+ }
+
+ public static String createUrl(String item, int size) {
+ Map params = App.getSubsonicClientInstance(false).getParams();
+
+ StringBuilder uri = new StringBuilder();
+
+ uri.append(App.getSubsonicClientInstance(false).getUrl());
+ uri.append("getCoverArt");
+
+ if (params.containsKey("u") && params.get("u") != null)
+ uri.append("?u=").append(Util.encode(params.get("u")));
+ if (params.containsKey("p") && params.get("p") != null)
+ uri.append("&p=").append(params.get("p"));
+ if (params.containsKey("s") && params.get("s") != null)
+ uri.append("&s=").append(params.get("s"));
+ if (params.containsKey("t") && params.get("t") != null)
+ uri.append("&t=").append(params.get("t"));
+ if (params.containsKey("v") && params.get("v") != null)
+ uri.append("&v=").append(params.get("v"));
+ if (params.containsKey("c") && params.get("c") != null)
+ uri.append("&c=").append(params.get("c"));
+ if (size != -1)
+ uri.append("&size=").append(size);
+
+ uri.append("&id=").append(item);
+
+ Log.d(TAG, "createUrl() " + uri);
+
+ return uri.toString();
+ }
+
+ public static void loadAlbumArtBitmap(Context context,
+ String coverId,
+ int size,
+ CustomTarget target) {
+ String url = createUrl(coverId, size);
+ Glide.with(context)
+ .asBitmap()
+ .load(url)
+ .apply(createRequestOptions(context, coverId, ResourceType.Album))
+ .into(target);
+ }
+
+ public static class Builder {
+ private final RequestManager requestManager;
+ private String item;
+
+ private Builder(Context context, String item, ResourceType type) {
+ this.requestManager = Glide.with(context);
+
+ if (item != null && !Preferences.isDataSavingMode()) {
+ this.item = createUrl(item, Preferences.getImageSize());
+ }
+
+ requestManager.applyDefaultRequestOptions(createRequestOptions(context, item, type));
+ }
+
+ public static Builder from(Context context, String item, ResourceType type) {
+ return new Builder(context, item, type);
+ }
+
+ public RequestBuilder build() {
+ return requestManager
+ .load(item)
+ .transition(DrawableTransitionOptions.withCrossFade());
+ }
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/glide/IPv6StringLoader.java b/app/src/main/java/com/cappielloantonio/tempo/glide/IPv6StringLoader.java
new file mode 100644
index 0000000..85307ac
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/glide/IPv6StringLoader.java
@@ -0,0 +1,110 @@
+package com.cappielloantonio.tempo.glide;
+
+import androidx.annotation.NonNull;
+
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.Options;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+import com.bumptech.glide.signature.ObjectKey;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class IPv6StringLoader implements ModelLoader {
+ private static final int DEFAULT_TIMEOUT_MS = 2500;
+
+ @Override
+ public boolean handles(@NonNull String model) {
+ return model.startsWith("http://") || model.startsWith("https://");
+ }
+
+ @Override
+ public LoadData buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
+ if (!handles(model)) {
+ return null;
+ }
+ return new LoadData<>(new ObjectKey(model), new IPv6StreamFetcher(model));
+ }
+
+ private static class IPv6StreamFetcher implements DataFetcher {
+ private final String model;
+ private InputStream stream;
+ private HttpURLConnection connection;
+
+ IPv6StreamFetcher(String model) {
+ this.model = model;
+ }
+
+ @Override
+ public void loadData(@NonNull Priority priority, @NonNull DataCallback super InputStream> callback) {
+ try {
+ URL url = new URL(model);
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setConnectTimeout(DEFAULT_TIMEOUT_MS);
+ connection.setReadTimeout(DEFAULT_TIMEOUT_MS);
+ connection.setUseCaches(true);
+ connection.setDoInput(true);
+ connection.connect();
+
+ if (connection.getResponseCode() / 100 != 2) {
+ callback.onLoadFailed(new IOException("Request failed with status code: " + connection.getResponseCode()));
+ return;
+ }
+
+ stream = connection.getInputStream();
+ callback.onDataReady(stream);
+ } catch (IOException e) {
+ callback.onLoadFailed(e);
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException ignored) {
+ }
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ // HttpURLConnection does not provide a direct cancel mechanism.
+ }
+
+ @NonNull
+ @Override
+ public Class getDataClass() {
+ return InputStream.class;
+ }
+
+ @NonNull
+ @Override
+ public DataSource getDataSource() {
+ return DataSource.REMOTE;
+ }
+ }
+
+ public static class Factory implements ModelLoaderFactory {
+ @NonNull
+ @Override
+ public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) {
+ return new IPv6StringLoader();
+ }
+
+ @Override
+ public void teardown() {
+ // No-op
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/ThemeHelper.java b/app/src/main/java/com/cappielloantonio/tempo/helper/ThemeHelper.java
new file mode 100644
index 0000000..d10392b
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/ThemeHelper.java
@@ -0,0 +1,35 @@
+package com.cappielloantonio.tempo.helper;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatDelegate;
+
+public class ThemeHelper {
+ private static final String TAG = "ThemeHelper";
+
+ public static final String LIGHT_MODE = "light";
+ public static final String DARK_MODE = "dark";
+ public static final String DEFAULT_MODE = "default";
+
+ public static void applyTheme(@NonNull String themePref) {
+ switch (themePref) {
+ case LIGHT_MODE: {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
+ break;
+ }
+ case DARK_MODE: {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
+ break;
+ }
+ default: {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+ } else {
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
+ }
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/CustomLinearSnapHelper.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/CustomLinearSnapHelper.java
new file mode 100644
index 0000000..4d045d0
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/CustomLinearSnapHelper.java
@@ -0,0 +1,24 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import android.view.View;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.LinearSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class CustomLinearSnapHelper extends LinearSnapHelper {
+ @Override
+ public View findSnapView(RecyclerView.LayoutManager layoutManager) {
+ if (layoutManager instanceof LinearLayoutManager) {
+ LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
+ if (!needToDoSnap(linearLayoutManager)) {
+ return null;
+ }
+ }
+ return super.findSnapView(layoutManager);
+ }
+
+ public boolean needToDoSnap(LinearLayoutManager linearLayoutManager) {
+ return linearLayoutManager.findFirstCompletelyVisibleItemPosition() != 0 && linearLayoutManager.findLastCompletelyVisibleItemPosition() != linearLayoutManager.getItemCount() - 1;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/DotsIndicatorDecoration.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/DotsIndicatorDecoration.java
new file mode 100644
index 0000000..0dcf9c9
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/DotsIndicatorDecoration.java
@@ -0,0 +1,116 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.annotation.ColorInt;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.jetbrains.annotations.NotNull;
+
+public class DotsIndicatorDecoration extends RecyclerView.ItemDecoration {
+ private static final String TAG = "DotsIndicatorDecoration";
+
+ private final int indicatorHeight;
+ private final int indicatorItemPadding;
+ private final int radius;
+
+ private final Paint inactivePaint = new Paint();
+ private final Paint activePaint = new Paint();
+
+ public DotsIndicatorDecoration(int radius, int padding, int indicatorHeight, @ColorInt int colorInactive, @ColorInt int colorActive) {
+ float strokeWidth = Resources.getSystem().getDisplayMetrics().density * 1;
+ this.radius = radius;
+
+ inactivePaint.setStrokeCap(Paint.Cap.ROUND);
+ inactivePaint.setStrokeWidth(strokeWidth);
+ inactivePaint.setStyle(Paint.Style.STROKE);
+ inactivePaint.setAntiAlias(true);
+ inactivePaint.setColor(colorInactive);
+
+ activePaint.setStrokeCap(Paint.Cap.ROUND);
+ activePaint.setStrokeWidth(strokeWidth);
+ activePaint.setStyle(Paint.Style.FILL);
+ activePaint.setAntiAlias(true);
+ activePaint.setColor(colorActive);
+
+ this.indicatorItemPadding = padding;
+ this.indicatorHeight = indicatorHeight;
+ }
+
+ @Override
+ public void onDrawOver(@NotNull Canvas c, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) {
+ super.onDrawOver(c, parent, state);
+
+ if (parent.getAdapter() == null) return;
+
+ int itemCount = (int) Math.ceil((double) parent.getAdapter().getItemCount() / 5);
+
+ if (itemCount <= 1) {
+ return;
+ }
+
+ // center horizontally, calculate width and subtract half from center
+ float totalLength = this.radius * 2 * itemCount;
+ float paddingBetweenItems = Math.max(0, itemCount - 1) * indicatorItemPadding;
+ float indicatorTotalWidth = totalLength + paddingBetweenItems;
+ float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2f;
+
+ // center vertically in the allotted space
+ float indicatorPosY = parent.getHeight() - indicatorHeight - (float) indicatorItemPadding / 4;
+
+ drawInactiveDots(c, indicatorStartX, indicatorPosY, itemCount);
+
+ final int activePosition;
+
+ if (parent.getLayoutManager() instanceof GridLayoutManager) {
+ activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
+ } else if (parent.getLayoutManager() instanceof LinearLayoutManager) {
+ activePosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
+ } else {
+ // not supported layout manager
+ return;
+ }
+
+ if (activePosition == RecyclerView.NO_POSITION) {
+ return;
+ }
+
+ // find offset of active page if the user is scrolling
+ final View activeChild = parent.getLayoutManager().findViewByPosition(activePosition);
+ if (activeChild == null) {
+ return;
+ }
+
+ drawActiveDot(c, indicatorStartX, indicatorPosY, activePosition);
+ }
+
+ private void drawInactiveDots(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) {
+ // width of item indicator including padding
+ final float itemWidth = this.radius * 2 + indicatorItemPadding;
+
+ float start = indicatorStartX + radius;
+ for (int i = 0; i < itemCount; i++) {
+ c.drawCircle(start, indicatorPosY, radius, inactivePaint);
+ start += itemWidth;
+ }
+ }
+
+ private void drawActiveDot(Canvas c, float indicatorStartX, float indicatorPosY, int highlightPosition) {
+ // width of item indicator including padding
+ final float itemWidth = this.radius * 2 + indicatorItemPadding;
+ float highlightStart = (float) Math.ceil(indicatorStartX + radius + itemWidth * highlightPosition / 5);
+ c.drawCircle(highlightStart, indicatorPosY, radius, activePaint);
+ }
+
+ @Override
+ public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ outRect.bottom = indicatorHeight;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/FastScrollbar.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/FastScrollbar.java
new file mode 100644
index 0000000..d479813
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/FastScrollbar.java
@@ -0,0 +1,197 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.core.view.ViewCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class FastScrollbar extends LinearLayout {
+ private static final int BUBBLE_ANIMATION_DURATION = 100;
+ private static final int TRACK_SNAP_RANGE = 5;
+
+ private TextView bubble;
+ private View handle;
+ private RecyclerView recyclerView;
+ private int height;
+ private boolean isInitialized = false;
+ private ObjectAnimator currentAnimator = null;
+
+ private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
+ updateBubbleAndHandlePosition();
+ }
+ };
+
+ public interface BubbleTextGetter {
+ String getTextToShowInBubble(int pos);
+ }
+
+ public FastScrollbar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ public FastScrollbar(final Context context) {
+ super(context);
+ init(context);
+ }
+
+ public FastScrollbar(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ protected void init(Context context) {
+ if (isInitialized) return;
+ isInitialized = true;
+ setOrientation(HORIZONTAL);
+ setClipChildren(false);
+ }
+
+ public void setViewsToUse(@LayoutRes int layoutResId, @IdRes int bubbleResId, @IdRes int handleResId) {
+ final LayoutInflater inflater = LayoutInflater.from(getContext());
+ inflater.inflate(layoutResId, this, true);
+ bubble = findViewById(bubbleResId);
+ if (bubble != null) bubble.setVisibility(INVISIBLE);
+ handle = findViewById(handleResId);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ height = h;
+ updateBubbleAndHandlePosition();
+ }
+
+ @Override
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
+ final int action = event.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (event.getX() < handle.getX() - ViewCompat.getPaddingStart(handle)) return false;
+ if (currentAnimator != null) currentAnimator.cancel();
+ if (bubble != null && bubble.getVisibility() == INVISIBLE) showBubble();
+ handle.setSelected(true);
+ case MotionEvent.ACTION_MOVE:
+ final float y = event.getY();
+ setBubbleAndHandlePosition(y);
+ setRecyclerViewPosition(y);
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ handle.setSelected(false);
+ hideBubble();
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ public void setRecyclerView(final RecyclerView recyclerView) {
+ if (this.recyclerView != recyclerView) {
+ if (this.recyclerView != null)
+ this.recyclerView.removeOnScrollListener(onScrollListener);
+ this.recyclerView = recyclerView;
+ if (this.recyclerView == null) return;
+ recyclerView.addOnScrollListener(onScrollListener);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (recyclerView != null) {
+ recyclerView.removeOnScrollListener(onScrollListener);
+ recyclerView = null;
+ }
+ }
+
+ private void setRecyclerViewPosition(float y) {
+ if (recyclerView != null) {
+ final int itemCount = recyclerView.getAdapter().getItemCount();
+ float proportion;
+ if (handle.getY() == 0) proportion = 0f;
+ else if (handle.getY() + handle.getHeight() >= height - TRACK_SNAP_RANGE)
+ proportion = 1f;
+ else proportion = y / (float) height;
+ final int targetPos = getValueInRange(0, itemCount - 1, (int) (proportion * (float) itemCount));
+ ((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(targetPos, 0);
+ final String bubbleText = ((BubbleTextGetter) recyclerView.getAdapter()).getTextToShowInBubble(targetPos);
+ if (bubble != null) {
+ bubble.setText(bubbleText);
+ if (TextUtils.isEmpty(bubbleText)) {
+ hideBubble();
+ } else if (bubble.getVisibility() == View.INVISIBLE) {
+ showBubble();
+ }
+ }
+ }
+ }
+
+ private int getValueInRange(int min, int max, int value) {
+ int minimum = Math.max(min, value);
+ return Math.min(minimum, max);
+ }
+
+ private void updateBubbleAndHandlePosition() {
+ if (bubble == null || handle.isSelected()) return;
+
+ final int verticalScrollOffset = recyclerView.computeVerticalScrollOffset();
+ final int verticalScrollRange = recyclerView.computeVerticalScrollRange();
+ float proportion = (float) verticalScrollOffset / ((float) verticalScrollRange - height);
+ setBubbleAndHandlePosition(height * proportion);
+ }
+
+ private void setBubbleAndHandlePosition(float y) {
+ final int handleHeight = handle.getHeight();
+ handle.setY(getValueInRange(0, height - handleHeight, (int) (y - handleHeight / 2)));
+ if (bubble != null) {
+ int bubbleHeight = bubble.getHeight();
+ bubble.setY(getValueInRange(0, height - bubbleHeight - handleHeight / 2, (int) (y - bubbleHeight)));
+ }
+ }
+
+ private void showBubble() {
+ if (bubble == null) return;
+ bubble.setVisibility(VISIBLE);
+ if (currentAnimator != null) currentAnimator.cancel();
+ currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 0f, 1f).setDuration(BUBBLE_ANIMATION_DURATION);
+ currentAnimator.start();
+ }
+
+ private void hideBubble() {
+ if (bubble == null) return;
+ if (currentAnimator != null) currentAnimator.cancel();
+ currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 1f, 0f).setDuration(BUBBLE_ANIMATION_DURATION);
+ currentAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ bubble.setVisibility(INVISIBLE);
+ currentAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ bubble.setVisibility(INVISIBLE);
+ currentAnimator = null;
+ }
+ });
+ currentAnimator.start();
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/GridItemDecoration.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/GridItemDecoration.java
new file mode 100644
index 0000000..71f5479
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/GridItemDecoration.java
@@ -0,0 +1,41 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class GridItemDecoration extends RecyclerView.ItemDecoration {
+ private final int spanCount;
+ private final int spacing;
+ private final boolean includeEdge;
+
+ public GridItemDecoration(int spanCount, int spacing, boolean includeEdge) {
+ this.spanCount = spanCount;
+ this.spacing = spacing;
+ this.includeEdge = includeEdge;
+ }
+
+ @Override
+ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, RecyclerView parent, @NonNull RecyclerView.State state) {
+ int position = parent.getChildAdapterPosition(view); // item position
+ int column = position % spanCount; // item column
+
+ if (includeEdge) {
+ outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
+ outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
+
+ if (position < spanCount) { // top edge
+ outRect.top = spacing;
+ }
+ outRect.bottom = spacing; // item bottom
+ } else {
+ outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
+ outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
+ if (position >= spanCount) {
+ outRect.top = spacing; // item top
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/NestedScrollableHost.kt b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/NestedScrollableHost.kt
new file mode 100644
index 0000000..b2fdba2
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/NestedScrollableHost.kt
@@ -0,0 +1,88 @@
+package com.cappielloantonio.tempo.helper.recyclerview
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import android.widget.FrameLayout
+import androidx.viewpager2.widget.ViewPager2
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import kotlin.math.absoluteValue
+import kotlin.math.sign
+
+class NestedScrollableHost : FrameLayout {
+ constructor(context: Context) : super(context)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ private var touchSlop = 0
+ private var initialX = 0f
+ private var initialY = 0f
+ private val parentViewPager: ViewPager2?
+ get() {
+ var v: View? = parent as? View
+ while (v != null && v !is ViewPager2) {
+ v = v.parent as? View
+ }
+ return v as? ViewPager2
+ }
+
+ private val child: View? get() = if (childCount > 0) getChildAt(0) else null
+
+ init {
+ touchSlop = ViewConfiguration.get(context).scaledTouchSlop
+ }
+
+ private fun canChildScroll(orientation: Int, delta: Float): Boolean {
+ val direction = -delta.sign.toInt()
+ return when (orientation) {
+ 0 -> child?.canScrollHorizontally(direction) ?: false
+ 1 -> child?.canScrollVertically(direction) ?: false
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
+ handleInterceptTouchEvent(e)
+ return super.onInterceptTouchEvent(e)
+ }
+
+ private fun handleInterceptTouchEvent(e: MotionEvent) {
+ val orientation = parentViewPager?.orientation ?: return
+
+ // Early return if child can't scroll in same direction as parent
+ if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
+ return
+ }
+
+ if (e.action == MotionEvent.ACTION_DOWN) {
+ initialX = e.x
+ initialY = e.y
+ parent.requestDisallowInterceptTouchEvent(true)
+ } else if (e.action == MotionEvent.ACTION_MOVE) {
+ val dx = e.x - initialX
+ val dy = e.y - initialY
+ val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
+
+ // assuming ViewPager2 touch-slop is 2x touch-slop of child
+ val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
+ val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
+
+ if (scaledDx > touchSlop || scaledDy > touchSlop) {
+ if (isVpHorizontal == (scaledDy > scaledDx)) {
+ // Gesture is perpendicular, allow all parents to intercept
+ parent.requestDisallowInterceptTouchEvent(false)
+ } else {
+ // Gesture is parallel, query child if movement in that direction is possible
+ if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
+ // Child can scroll, disallow all parents to intercept
+ parent.requestDisallowInterceptTouchEvent(true)
+ } else {
+ // Child cannot scroll, allow all parents to intercept
+ parent.requestDisallowInterceptTouchEvent(false)
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/PaginationScrollListener.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/PaginationScrollListener.java
new file mode 100644
index 0000000..2e1c8f4
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/PaginationScrollListener.java
@@ -0,0 +1,33 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
+ private final LinearLayoutManager layoutManager;
+
+ protected PaginationScrollListener(LinearLayoutManager layoutManager) {
+ this.layoutManager = layoutManager;
+ }
+
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+
+ int visibleItemCount = layoutManager.getChildCount();
+ int totalItemCount = layoutManager.getItemCount();
+ int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
+
+ if (!isLoading()) {
+ if (firstVisibleItemPosition >= 0 && (visibleItemCount + firstVisibleItemPosition) >= (totalItemCount / 4 * 3)) {
+ loadMoreItems();
+ }
+ }
+ }
+
+ protected abstract void loadMoreItems();
+
+ public abstract boolean isLoading();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/SquareLayout.java b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/SquareLayout.java
new file mode 100644
index 0000000..3437e49
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/helper/recyclerview/SquareLayout.java
@@ -0,0 +1,28 @@
+package com.cappielloantonio.tempo.helper.recyclerview;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+public class SquareLayout extends RelativeLayout {
+ public SquareLayout(Context context) {
+ super(context);
+ }
+
+ public SquareLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/ClickCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/ClickCallback.java
new file mode 100644
index 0000000..d65695c
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/ClickCallback.java
@@ -0,0 +1,35 @@
+package com.cappielloantonio.tempo.interfaces;
+
+
+import android.os.Bundle;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface ClickCallback {
+ default void onMediaClick(Bundle bundle) {}
+ default void onMediaLongClick(Bundle bundle) {}
+ default void onAlbumClick(Bundle bundle) {}
+ default void onAlbumLongClick(Bundle bundle) {}
+ default void onArtistClick(Bundle bundle) {}
+ default void onArtistLongClick(Bundle bundle) {}
+ default void onGenreClick(Bundle bundle) {}
+ default void onPlaylistClick(Bundle bundle) {}
+ default void onPlaylistLongClick(Bundle bundle) {}
+ default void onYearClick(Bundle bundle) {}
+ default void onServerClick(Bundle bundle) {}
+ default void onServerLongClick(Bundle bundle) {}
+ default void onPodcastEpisodeClick(Bundle bundle) {}
+ default void onPodcastEpisodeAltClick(Bundle bundle) {}
+ default void onPodcastEpisodeLongClick(Bundle bundle) {}
+ default void onPodcastChannelClick(Bundle bundle) {}
+ default void onPodcastChannelLongClick(Bundle bundle) {}
+ default void onInternetRadioStationClick(Bundle bundle) {}
+ default void onInternetRadioStationLongClick(Bundle bundle) {}
+ default void onMusicFolderClick(Bundle bundle) {}
+ default void onMusicDirectoryClick(Bundle bundle) {}
+ default void onMusicIndexClick(Bundle bundle) {}
+ default void onDownloadGroupLongClick(Bundle bundle) {}
+ default void onShareClick(Bundle bundle) {}
+ default void onShareLongClick(Bundle bundle) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/DecadesCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/DecadesCallback.java
new file mode 100644
index 0000000..531114e
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/DecadesCallback.java
@@ -0,0 +1,8 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface DecadesCallback {
+ default void onLoadYear(int year) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/DialogClickCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/DialogClickCallback.java
new file mode 100644
index 0000000..adc3937
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/DialogClickCallback.java
@@ -0,0 +1,13 @@
+package com.cappielloantonio.tempo.interfaces;
+
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface DialogClickCallback {
+ default void onPositiveClick() {}
+
+ default void onNegativeClick() {}
+
+ default void onNeutralClick() {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaCallback.java
new file mode 100644
index 0000000..23fb6c5
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaCallback.java
@@ -0,0 +1,11 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+import java.util.List;
+
+@Keep
+public interface MediaCallback {
+ default void onError(Exception exception) {}
+ default void onLoadMedia(List> media) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaIndexCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaIndexCallback.java
new file mode 100644
index 0000000..6a236e5
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/MediaIndexCallback.java
@@ -0,0 +1,8 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface MediaIndexCallback {
+ default void onRecovery(int index) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/PlaylistCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/PlaylistCallback.java
new file mode 100644
index 0000000..73caf6d
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/PlaylistCallback.java
@@ -0,0 +1,8 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface PlaylistCallback {
+ default void onDismiss() {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/PodcastCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/PodcastCallback.java
new file mode 100644
index 0000000..fde54e3
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/PodcastCallback.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+
+public interface PodcastCallback {
+ default void onDismiss() {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/RadioCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/RadioCallback.java
new file mode 100644
index 0000000..42bcea8
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/RadioCallback.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+
+public interface RadioCallback {
+ default void onDismiss() {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/ScanCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/ScanCallback.java
new file mode 100644
index 0000000..6260743
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/ScanCallback.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface ScanCallback {
+ default void onError(Exception exception) {}
+ default void onSuccess(boolean isScanning, long count) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/StarCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/StarCallback.java
new file mode 100644
index 0000000..a5bb177
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/StarCallback.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface StarCallback {
+ default void onError() {}
+ default void onSuccess() {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/interfaces/SystemCallback.java b/app/src/main/java/com/cappielloantonio/tempo/interfaces/SystemCallback.java
new file mode 100644
index 0000000..dc0b008
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/interfaces/SystemCallback.java
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.interfaces;
+
+import androidx.annotation.Keep;
+
+@Keep
+public interface SystemCallback {
+ default void onError(Exception exception) {}
+ default void onSuccess(String password, String token, String salt) {}
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Chronology.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Chronology.kt
new file mode 100644
index 0000000..a3b142e
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Chronology.kt
@@ -0,0 +1,59 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+import androidx.media3.common.MediaItem
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.cappielloantonio.tempo.subsonic.models.Child
+import com.cappielloantonio.tempo.util.Preferences
+import kotlinx.parcelize.Parcelize
+import java.util.Date
+
+@Keep
+@Parcelize
+@Entity(tableName = "chronology")
+class Chronology(
+ @PrimaryKey override val id: String,
+ @ColumnInfo(name = "timestamp")
+ var timestamp: Long = System.currentTimeMillis(),
+ @ColumnInfo(name = "server")
+ var server: String? = null,
+) : Child(id) {
+ constructor(mediaItem: MediaItem) : this(mediaItem.mediaMetadata.extras!!.getString("id")!!) {
+ parentId = mediaItem.mediaMetadata.extras!!.getString("parentId")
+ isDir = mediaItem.mediaMetadata.extras!!.getBoolean("isDir")
+ title = mediaItem.mediaMetadata.extras!!.getString("title")
+ album = mediaItem.mediaMetadata.extras!!.getString("album")
+ artist = mediaItem.mediaMetadata.extras!!.getString("artist")
+ track = mediaItem.mediaMetadata.extras!!.getInt("track")
+ year = mediaItem.mediaMetadata.extras!!.getInt("year")
+ genre = mediaItem.mediaMetadata.extras!!.getString("genre")
+ coverArtId = mediaItem.mediaMetadata.extras!!.getString("coverArtId")
+ size = mediaItem.mediaMetadata.extras!!.getLong("size")
+ contentType = mediaItem.mediaMetadata.extras!!.getString("contentType")
+ suffix = mediaItem.mediaMetadata.extras!!.getString("suffix")
+ transcodedContentType = mediaItem.mediaMetadata.extras!!.getString("transcodedContentType")
+ transcodedSuffix = mediaItem.mediaMetadata.extras!!.getString("transcodedSuffix")
+ duration = mediaItem.mediaMetadata.extras!!.getInt("duration")
+ bitrate = mediaItem.mediaMetadata.extras!!.getInt("bitrate")
+ samplingRate = mediaItem.mediaMetadata.extras!!.getInt("samplingRate")
+ bitDepth = mediaItem.mediaMetadata.extras!!.getInt("bitDepth")
+ path = mediaItem.mediaMetadata.extras!!.getString("path")
+ isVideo = mediaItem.mediaMetadata.extras!!.getBoolean("isVideo")
+ userRating = mediaItem.mediaMetadata.extras!!.getInt("userRating")
+ averageRating = mediaItem.mediaMetadata.extras!!.getDouble("averageRating")
+ playCount = mediaItem.mediaMetadata.extras!!.getLong("playCount")
+ discNumber = mediaItem.mediaMetadata.extras!!.getInt("discNumber")
+ created = Date(mediaItem.mediaMetadata.extras!!.getLong("created"))
+ starred = Date(mediaItem.mediaMetadata.extras!!.getLong("starred"))
+ albumId = mediaItem.mediaMetadata.extras!!.getString("albumId")
+ artistId = mediaItem.mediaMetadata.extras!!.getString("artistId")
+ type = mediaItem.mediaMetadata.extras!!.getString("type")
+ bookmarkPosition = mediaItem.mediaMetadata.extras!!.getLong("bookmarkPosition")
+ originalWidth = mediaItem.mediaMetadata.extras!!.getInt("originalWidth")
+ originalHeight = mediaItem.mediaMetadata.extras!!.getInt("originalHeight")
+ server = Preferences.getServerId()
+ timestamp = Date().time
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt
new file mode 100644
index 0000000..0c54e1c
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt
@@ -0,0 +1,64 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.cappielloantonio.tempo.subsonic.models.Child
+import kotlinx.parcelize.Parcelize
+
+@Keep
+@Parcelize
+@Entity(tableName = "download")
+class Download(
+ @PrimaryKey override val id: String,
+ @ColumnInfo(name = "playlist_id")
+ var playlistId: String? = null,
+ @ColumnInfo(name = "playlist_name")
+ var playlistName: String? = null,
+ @ColumnInfo(name = "download_state", defaultValue = "1")
+ var downloadState: Int = 0,
+ @ColumnInfo(name = "download_uri", defaultValue = "")
+ var downloadUri: String? = null,
+) : Child(id) {
+ constructor(child: Child) : this(child.id) {
+ parentId = child.parentId
+ isDir = child.isDir
+ title = child.title
+ album = child.album
+ artist = child.artist
+ track = child.track
+ year = child.year
+ genre = child.genre
+ coverArtId = child.coverArtId
+ size = child.size
+ contentType = child.contentType
+ suffix = child.suffix
+ transcodedContentType = child.transcodedContentType
+ transcodedSuffix = child.transcodedSuffix
+ duration = child.duration
+ bitrate = child.bitrate
+ samplingRate = child.samplingRate
+ bitDepth = child.bitDepth
+ path = child.path
+ isVideo = child.isVideo
+ userRating = child.userRating
+ averageRating = child.averageRating
+ playCount = child.playCount
+ discNumber = child.discNumber
+ created = child.created
+ starred = child.starred
+ albumId = child.albumId
+ artistId = child.artistId
+ type = child.type
+ bookmarkPosition = child.bookmarkPosition
+ originalWidth = child.originalWidth
+ originalHeight = child.originalHeight
+ }
+}
+
+@Keep
+data class DownloadStack(
+ var id: String,
+ var view: String?,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Favorite.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Favorite.kt
new file mode 100644
index 0000000..d5bbb8c
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Favorite.kt
@@ -0,0 +1,32 @@
+package com.cappielloantonio.tempo.model
+
+import android.os.Parcelable
+import androidx.annotation.Keep
+import androidx.annotation.Nullable
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Keep
+@Parcelize
+@Entity(tableName = "favorite")
+data class Favorite(
+ @PrimaryKey
+ @ColumnInfo(name = "timestamp")
+ var timestamp: Long,
+
+ @ColumnInfo(name = "songId")
+ val songId: String?,
+
+ @ColumnInfo(name = "albumId")
+ val albumId: String?,
+
+ @ColumnInfo(name = "artistId")
+ val artistId: String?,
+
+ @ColumnInfo(name = "toStar")
+ val toStar: Boolean,
+) : Parcelable {
+ override fun toString(): String = (songId ?: "null") + (albumId ?: "null") + (artistId ?: "null")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt b/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt
new file mode 100644
index 0000000..79ca435
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt
@@ -0,0 +1,11 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+
+@Keep
+data class HomeSector(
+ val id: String,
+ val sectorTitle: String,
+ var isVisible: Boolean,
+ val order: Int,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/LyricsCache.kt b/app/src/main/java/com/cappielloantonio/tempo/model/LyricsCache.kt
new file mode 100644
index 0000000..3c437e2
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/LyricsCache.kt
@@ -0,0 +1,25 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlin.jvm.JvmOverloads
+
+@Keep
+@Entity(tableName = "lyrics_cache")
+data class LyricsCache @JvmOverloads constructor(
+ @PrimaryKey
+ @ColumnInfo(name = "song_id")
+ var songId: String,
+ @ColumnInfo(name = "artist")
+ var artist: String? = null,
+ @ColumnInfo(name = "title")
+ var title: String? = null,
+ @ColumnInfo(name = "lyrics")
+ var lyrics: String? = null,
+ @ColumnInfo(name = "structured_lyrics")
+ var structuredLyrics: String? = null,
+ @ColumnInfo(name = "updated_at")
+ var updatedAt: Long = System.currentTimeMillis()
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Queue.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Queue.kt
new file mode 100644
index 0000000..8784017
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Queue.kt
@@ -0,0 +1,59 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.cappielloantonio.tempo.subsonic.models.Child
+import kotlinx.parcelize.Parcelize
+
+@Keep
+@Parcelize
+@Entity(tableName = "queue")
+class Queue(
+ override val id: String,
+ @PrimaryKey
+ @ColumnInfo(name = "track_order")
+ var trackOrder: Int = 0,
+ @ColumnInfo(name = "last_play")
+ var lastPlay: Long = 0,
+ @ColumnInfo(name = "playing_changed")
+ var playingChanged: Long = 0,
+ @ColumnInfo(name = "stream_id")
+ var streamId: String? = null,
+) : Child(id) {
+ constructor(child: Child) : this(child.id) {
+ parentId = child.parentId
+ isDir = child.isDir
+ title = child.title
+ album = child.album
+ artist = child.artist
+ track = child.track
+ year = child.year
+ genre = child.genre
+ coverArtId = child.coverArtId
+ size = child.size
+ contentType = child.contentType
+ suffix = child.suffix
+ transcodedContentType = child.transcodedContentType
+ transcodedSuffix = child.transcodedSuffix
+ duration = child.duration
+ bitrate = child.bitrate
+ samplingRate = child.samplingRate
+ bitDepth = child.bitDepth
+ path = child.path
+ isVideo = child.isVideo
+ userRating = child.userRating
+ averageRating = child.averageRating
+ playCount = child.playCount
+ discNumber = child.discNumber
+ created = child.created
+ starred = child.starred
+ albumId = child.albumId
+ artistId = child.artistId
+ type = child.type
+ bookmarkPosition = child.bookmarkPosition
+ originalWidth = child.originalWidth
+ originalHeight = child.originalHeight
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/RecentSearch.kt b/app/src/main/java/com/cappielloantonio/tempo/model/RecentSearch.kt
new file mode 100644
index 0000000..6004a31
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/RecentSearch.kt
@@ -0,0 +1,17 @@
+package com.cappielloantonio.tempo.model
+
+import android.os.Parcelable
+import androidx.annotation.Keep
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Keep
+@Parcelize
+@Entity(tableName = "recent_search")
+data class RecentSearch(
+ @PrimaryKey
+ @ColumnInfo(name = "search")
+ var search: String
+) : Parcelable
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt b/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt
new file mode 100644
index 0000000..0d46961
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt
@@ -0,0 +1,9 @@
+package com.cappielloantonio.tempo.model
+
+import androidx.annotation.Keep
+
+@Keep
+data class ReplayGain(
+ var trackGain: Float = 0f,
+ var albumGain: Float = 0f,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Server.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Server.kt
new file mode 100644
index 0000000..78bfa6e
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Server.kt
@@ -0,0 +1,39 @@
+package com.cappielloantonio.tempo.model
+
+import android.os.Parcelable
+import androidx.annotation.Keep
+import androidx.annotation.Nullable
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Keep
+@Parcelize
+@Entity(tableName = "server")
+data class Server(
+ @PrimaryKey
+ @ColumnInfo(name = "id")
+ val serverId: String,
+
+ @ColumnInfo(name = "server_name")
+ val serverName: String,
+
+ @ColumnInfo(name = "username")
+ val username: String,
+
+ @ColumnInfo(name = "password")
+ val password: String,
+
+ @ColumnInfo(name = "address")
+ val address: String,
+
+ @ColumnInfo(name = "local_address")
+ val localAddress: String?,
+
+ @ColumnInfo(name = "timestamp")
+ val timestamp: Long,
+
+ @ColumnInfo(name = "low_security", defaultValue = "false")
+ val isLowSecurity: Boolean
+) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/SessionMediaItem.kt b/app/src/main/java/com/cappielloantonio/tempo/model/SessionMediaItem.kt
new file mode 100644
index 0000000..60d641c
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/SessionMediaItem.kt
@@ -0,0 +1,289 @@
+package com.cappielloantonio.tempo.model
+
+import android.net.Uri
+import android.os.Bundle
+import androidx.annotation.Keep
+import androidx.media3.common.HeartRating
+import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaItem.RequestMetadata
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.MimeTypes
+import androidx.media3.common.util.UnstableApi
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.cappielloantonio.tempo.glide.CustomGlideRequest
+import com.cappielloantonio.tempo.subsonic.models.Child
+import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation
+import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode
+import com.cappielloantonio.tempo.util.Constants
+import com.cappielloantonio.tempo.util.MusicUtil
+import com.cappielloantonio.tempo.util.Preferences.getImageSize
+import java.util.Date
+
+@UnstableApi
+@Keep
+@Entity(tableName = "session_media_item")
+class SessionMediaItem() {
+ @PrimaryKey(autoGenerate = true)
+ @ColumnInfo(name = "index")
+ var index: Int = 0
+
+ @ColumnInfo(name = "id")
+ var id: String? = null
+
+ @ColumnInfo(name = "parent_id")
+ var parentId: String? = null
+
+ @ColumnInfo(name = "is_dir")
+ var isDir: Boolean = false
+
+ @ColumnInfo
+ var title: String? = null
+
+ @ColumnInfo
+ var album: String? = null
+
+ @ColumnInfo
+ var artist: String? = null
+
+ @ColumnInfo
+ var track: Int? = null
+
+ @ColumnInfo
+ var year: Int? = null
+
+ @ColumnInfo
+ var genre: String? = null
+
+ @ColumnInfo(name = "cover_art_id")
+ var coverArtId: String? = null
+
+ @ColumnInfo
+ var size: Long? = null
+
+ @ColumnInfo(name = "content_type")
+ var contentType: String? = null
+
+ @ColumnInfo
+ var suffix: String? = null
+
+ @ColumnInfo("transcoding_content_type")
+ var transcodedContentType: String? = null
+
+ @ColumnInfo(name = "transcoded_suffix")
+ var transcodedSuffix: String? = null
+
+ @ColumnInfo
+ var duration: Int? = null
+
+ @ColumnInfo("bitrate")
+ var bitrate: Int? = null
+
+ @ColumnInfo
+ var path: String? = null
+
+ @ColumnInfo(name = "is_video")
+ var isVideo: Boolean = false
+
+ @ColumnInfo(name = "user_rating")
+ var userRating: Int? = null
+
+ @ColumnInfo(name = "average_rating")
+ var averageRating: Double? = null
+
+ @ColumnInfo(name = "play_count")
+ var playCount: Long? = null
+
+ @ColumnInfo(name = "disc_number")
+ var discNumber: Int? = null
+
+ @ColumnInfo
+ var created: Date? = null
+
+ @ColumnInfo
+ var starred: Date? = null
+
+ @ColumnInfo(name = "album_id")
+ var albumId: String? = null
+
+ @ColumnInfo(name = "artist_id")
+ var artistId: String? = null
+
+ @ColumnInfo
+ var type: String? = null
+
+ @ColumnInfo(name = "bookmark_position")
+ var bookmarkPosition: Long? = null
+
+ @ColumnInfo(name = "original_width")
+ var originalWidth: Int? = null
+
+ @ColumnInfo(name = "original_height")
+ var originalHeight: Int? = null
+
+ @ColumnInfo(name = "stream_id")
+ var streamId: String? = null
+
+ @ColumnInfo(name = "stream_url")
+ var streamUrl: String? = null
+
+ @ColumnInfo(name = "timestamp")
+ var timestamp: Long? = null
+
+ constructor(child: Child) : this() {
+ id = child.id
+ parentId = child.parentId
+ isDir = child.isDir
+ title = child.title
+ album = child.album
+ artist = child.artist
+ track = child.track
+ year = child.year
+ genre = child.genre
+ coverArtId = child.coverArtId
+ size = child.size
+ contentType = child.contentType
+ suffix = child.suffix
+ transcodedContentType = child.transcodedContentType
+ transcodedSuffix = child.transcodedSuffix
+ duration = child.duration
+ bitrate = child.bitrate
+ path = child.path
+ isVideo = child.isVideo
+ userRating = child.userRating
+ averageRating = child.averageRating
+ playCount = child.playCount
+ discNumber = child.discNumber
+ created = child.created
+ starred = child.starred
+ albumId = child.albumId
+ artistId = child.artistId
+ type = Constants.MEDIA_TYPE_MUSIC
+ bookmarkPosition = child.bookmarkPosition
+ originalWidth = child.originalWidth
+ originalHeight = child.originalHeight
+ }
+
+ constructor(podcastEpisode: PodcastEpisode) : this() {
+ id = podcastEpisode.id
+ parentId = podcastEpisode.parentId
+ isDir = podcastEpisode.isDir
+ title = podcastEpisode.title
+ album = podcastEpisode.album
+ artist = podcastEpisode.artist
+ year = podcastEpisode.year
+ genre = podcastEpisode.genre
+ coverArtId = podcastEpisode.coverArtId
+ size = podcastEpisode.size
+ contentType = podcastEpisode.contentType
+ suffix = podcastEpisode.suffix
+ duration = podcastEpisode.duration
+ bitrate = podcastEpisode.bitrate
+ path = podcastEpisode.path
+ isVideo = podcastEpisode.isVideo
+ created = podcastEpisode.created
+ artistId = podcastEpisode.artistId
+ streamId = podcastEpisode.streamId
+ type = Constants.MEDIA_TYPE_PODCAST
+ }
+
+ constructor(internetRadioStation: InternetRadioStation) : this() {
+ id = internetRadioStation.id
+ title = internetRadioStation.name
+ streamUrl = internetRadioStation.streamUrl
+ type = Constants.MEDIA_TYPE_RADIO
+ }
+
+ fun getMediaItem(): MediaItem {
+ val uri: Uri = getStreamUri()
+ val artworkUri = Uri.parse(CustomGlideRequest.createUrl(coverArtId, getImageSize()))
+
+ val bundle = Bundle()
+ bundle.putString("id", id)
+ bundle.putString("parentId", parentId)
+ bundle.putBoolean("isDir", isDir)
+ bundle.putString("title", title)
+ bundle.putString("album", album)
+ bundle.putString("artist", artist)
+ bundle.putInt("track", track ?: 0)
+ bundle.putInt("year", year ?: 0)
+ bundle.putString("genre", genre)
+ bundle.putString("coverArtId", coverArtId)
+ bundle.putLong("size", size ?: 0)
+ bundle.putString("contentType", contentType)
+ bundle.putString("suffix", suffix)
+ bundle.putString("transcodedContentType", transcodedContentType)
+ bundle.putString("transcodedSuffix", transcodedSuffix)
+ bundle.putInt("duration", duration ?: 0)
+ bundle.putInt("bitrate", bitrate ?: 0)
+ bundle.putString("path", path)
+ bundle.putBoolean("isVideo", isVideo)
+ bundle.putInt("userRating", userRating ?: 0)
+ bundle.putDouble("averageRating", averageRating ?: .0)
+ bundle.putLong("playCount", playCount ?: 0)
+ bundle.putInt("discNumber", discNumber ?: 0)
+ bundle.putLong("created", created?.time ?: 0)
+ bundle.putLong("starred", starred?.time ?: 0)
+ bundle.putString("albumId", albumId)
+ bundle.putString("artistId", artistId)
+ bundle.putString("type", Constants.MEDIA_TYPE_MUSIC)
+ bundle.putLong("bookmarkPosition", bookmarkPosition ?: 0)
+ bundle.putInt("originalWidth", originalWidth ?: 0)
+ bundle.putInt("originalHeight", originalHeight ?: 0)
+ bundle.putString("uri", uri.toString())
+
+ return MediaItem.Builder()
+ .setMediaId(id!!)
+ .setMediaMetadata(
+ MediaMetadata.Builder()
+ .setTitle(title)
+ .setTrackNumber(track ?: 0)
+ .setDiscNumber(discNumber ?: 0)
+ .setReleaseYear(year ?: 0)
+ .setAlbumTitle(album)
+ .setArtist(artist)
+ .setArtworkUri(artworkUri)
+ .setUserRating(HeartRating(starred != null))
+ .setSupportedCommands(
+ listOf(
+ Constants.CUSTOM_COMMAND_TOGGLE_HEART_ON,
+ Constants.CUSTOM_COMMAND_TOGGLE_HEART_OFF
+ )
+ )
+ .setExtras(bundle)
+ .setIsBrowsable(false)
+ .setIsPlayable(true)
+ .build()
+ )
+ .setRequestMetadata(
+ RequestMetadata.Builder()
+ .setMediaUri(uri)
+ .setExtras(bundle)
+ .build()
+ )
+ .setMimeType(MimeTypes.BASE_TYPE_AUDIO)
+ .setUri(uri)
+ .build()
+ }
+
+ private fun getStreamUri(): Uri {
+ return when (type) {
+ Constants.MEDIA_TYPE_MUSIC -> {
+ MusicUtil.getStreamUri(id)
+ }
+
+ Constants.MEDIA_TYPE_PODCAST -> {
+ MusicUtil.getStreamUri(streamId)
+ }
+
+ Constants.MEDIA_TYPE_RADIO -> {
+ Uri.parse(streamUrl)
+ }
+
+ else -> {
+ MusicUtil.getStreamUri(id)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/AlbumRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/AlbumRepository.java
new file mode 100644
index 0000000..bcc358b
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/repository/AlbumRepository.java
@@ -0,0 +1,301 @@
+package com.cappielloantonio.tempo.repository;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.MutableLiveData;
+import android.util.Log;
+
+import com.cappielloantonio.tempo.App;
+import com.cappielloantonio.tempo.interfaces.DecadesCallback;
+import com.cappielloantonio.tempo.interfaces.MediaCallback;
+import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
+import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
+import com.cappielloantonio.tempo.subsonic.models.AlbumInfo;
+import com.cappielloantonio.tempo.subsonic.models.Child;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AlbumRepository {
+ public MutableLiveData> getAlbums(String type, int size, Integer fromYear, Integer toYear) {
+ MutableLiveData> listLiveAlbums = new MutableLiveData<>(new ArrayList<>());
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getAlbumList2(type, size, 0, fromYear, toYear)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful()
+ && response.body() != null
+ && response.body().getSubsonicResponse().getAlbumList2() != null
+ && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
+
+ listLiveAlbums.setValue(response.body().getSubsonicResponse().getAlbumList2().getAlbums());
+ } else {
+ Log.e("AlbumRepository", "API Error on getAlbums. HTTP Code: " + response.code());
+ listLiveAlbums.setValue(new ArrayList<>());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ Log.e("AlbumRepository", "Network Failure on getAlbums: " + t.getMessage());
+ listLiveAlbums.setValue(new ArrayList<>());
+ }
+ });
+
+ return listLiveAlbums;
+ }
+
+ public MutableLiveData> getStarredAlbums(boolean random, int size) {
+ MutableLiveData> starredAlbums = new MutableLiveData<>(new ArrayList<>());
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getStarred2()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null) {
+ List albums = response.body().getSubsonicResponse().getStarred2().getAlbums();
+
+ if (albums != null) {
+ if (random) {
+ Collections.shuffle(albums);
+ starredAlbums.setValue(albums.subList(0, Math.min(size, albums.size())));
+ } else {
+ starredAlbums.setValue(albums);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return starredAlbums;
+ }
+
+ public void setRating(String id, int rating) {
+ App.getSubsonicClientInstance(false)
+ .getMediaAnnotationClient()
+ .setRating(id, rating)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+ }
+
+ public MutableLiveData> getAlbumTracks(String id) {
+ MutableLiveData> albumTracks = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getAlbum(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ List tracks = new ArrayList<>();
+
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
+ if (response.body().getSubsonicResponse().getAlbum().getSongs() != null) {
+ tracks.addAll(response.body().getSubsonicResponse().getAlbum().getSongs());
+ }
+ }
+
+ albumTracks.setValue(tracks);
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return albumTracks;
+ }
+
+ public MutableLiveData> getArtistAlbums(String id) {
+ MutableLiveData> artistsAlbum = new MutableLiveData<>(new ArrayList<>());
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null && response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
+ List albums = response.body().getSubsonicResponse().getArtist().getAlbums();
+ albums.sort(Comparator.comparing(AlbumID3::getYear));
+ Collections.reverse(albums);
+ artistsAlbum.setValue(albums);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return artistsAlbum;
+ }
+
+ public MutableLiveData getAlbum(String id) {
+ MutableLiveData album = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getAlbum(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
+ album.setValue(response.body().getSubsonicResponse().getAlbum());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return album;
+ }
+
+ public MutableLiveData getAlbumInfo(String id) {
+ MutableLiveData albumInfo = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getAlbumInfo2(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumInfo() != null) {
+ albumInfo.setValue(response.body().getSubsonicResponse().getAlbumInfo());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return albumInfo;
+ }
+
+ public void getInstantMix(AlbumID3 album, int count, MediaCallback callback) {
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getSimilarSongs2(album.getId(), count)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ List songs = new ArrayList<>();
+
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSimilarSongs2() != null) {
+ songs.addAll(response.body().getSubsonicResponse().getSimilarSongs2().getSongs());
+ }
+
+ callback.onLoadMedia(songs);
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ callback.onLoadMedia(new ArrayList<>());
+ }
+ });
+ }
+
+ public MutableLiveData> getDecades() {
+ MutableLiveData> decades = new MutableLiveData<>();
+
+ getFirstAlbum(new DecadesCallback() {
+ @Override
+ public void onLoadYear(int first) {
+ getLastAlbum(new DecadesCallback() {
+ @Override
+ public void onLoadYear(int last) {
+ if (first != -1 && last != -1) {
+ List decadeList = new ArrayList();
+
+ int startDecade = first - (first % 10);
+ int lastDecade = last - (last % 10);
+
+ while (startDecade <= lastDecade) {
+ decadeList.add(startDecade);
+ startDecade = startDecade + 10;
+ }
+
+ decades.setValue(decadeList);
+ }
+ }
+ });
+ }
+ });
+
+ return decades;
+ }
+
+ private void getFirstAlbum(DecadesCallback callback) {
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getAlbumList2("byYear", 1, 0, 1900, Calendar.getInstance().get(Calendar.YEAR))
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null && !response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty()) {
+ callback.onLoadYear(response.body().getSubsonicResponse().getAlbumList2().getAlbums().get(0).getYear());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ callback.onLoadYear(-1);
+ }
+ });
+ }
+
+ private void getLastAlbum(DecadesCallback callback) {
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getAlbumList2("byYear", 1, 0, Calendar.getInstance().get(Calendar.YEAR), 1900)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
+ if (!response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty() && !response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty()) {
+ callback.onLoadYear(response.body().getSubsonicResponse().getAlbumList2().getAlbums().get(0).getYear());
+ } else {
+ callback.onLoadYear(-1);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ callback.onLoadYear(-1);
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java
new file mode 100644
index 0000000..5bea391
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java
@@ -0,0 +1,387 @@
+package com.cappielloantonio.tempo.repository;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.MutableLiveData;
+import android.util.Log;
+
+import com.cappielloantonio.tempo.App;
+import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
+import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
+import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
+import com.cappielloantonio.tempo.subsonic.models.ArtistInfo2;
+import com.cappielloantonio.tempo.subsonic.models.Child;
+import com.cappielloantonio.tempo.subsonic.models.IndexID3;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class ArtistRepository {
+ private final AlbumRepository albumRepository;
+
+ public ArtistRepository() {
+ this.albumRepository = new AlbumRepository();
+ }
+
+ public void getArtistAllSongs(String artistId, ArtistSongsCallback callback) {
+ Log.d("ArtistSync", "Getting albums for artist: " + artistId);
+
+ // Get the artist info first, which contains the albums
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(artistId)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null &&
+ response.body().getSubsonicResponse().getArtist() != null &&
+ response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
+
+ List albums = response.body().getSubsonicResponse().getArtist().getAlbums();
+ Log.d("ArtistSync", "Got albums directly: " + albums.size());
+
+ if (!albums.isEmpty()) {
+ fetchAllAlbumSongsWithCallback(albums, callback);
+ } else {
+ Log.d("ArtistSync", "No albums found in artist response");
+ callback.onSongsCollected(new ArrayList<>());
+ }
+ } else {
+ Log.d("ArtistSync", "Failed to get artist info");
+ callback.onSongsCollected(new ArrayList<>());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ Log.d("ArtistSync", "Error getting artist info: " + t.getMessage());
+ callback.onSongsCollected(new ArrayList<>());
+ }
+ });
+ }
+
+ private void fetchAllAlbumSongsWithCallback(List albums, ArtistSongsCallback callback) {
+ if (albums == null || albums.isEmpty()) {
+ Log.d("ArtistSync", "No albums to process");
+ callback.onSongsCollected(new ArrayList<>());
+ return;
+ }
+
+ List allSongs = new ArrayList<>();
+ AtomicInteger remainingAlbums = new AtomicInteger(albums.size());
+ Log.d("ArtistSync", "Processing " + albums.size() + " albums");
+
+ for (AlbumID3 album : albums) {
+ Log.d("ArtistSync", "Getting tracks for album: " + album.getName());
+ MutableLiveData> albumTracks = albumRepository.getAlbumTracks(album.getId());
+ albumTracks.observeForever(songs -> {
+ Log.d("ArtistSync", "Got " + (songs != null ? songs.size() : 0) + " songs from album");
+ if (songs != null) {
+ allSongs.addAll(songs);
+ }
+ albumTracks.removeObservers(null);
+
+ int remaining = remainingAlbums.decrementAndGet();
+ Log.d("ArtistSync", "Remaining albums: " + remaining);
+
+ if (remaining == 0) {
+ Log.d("ArtistSync", "All albums processed. Total songs: " + allSongs.size());
+ callback.onSongsCollected(allSongs);
+ }
+ });
+ }
+ }
+
+ public interface ArtistSongsCallback {
+ void onSongsCollected(List songs);
+ }
+
+ public MutableLiveData> getStarredArtists(boolean random, int size) {
+ MutableLiveData> starredArtists = new MutableLiveData<>(new ArrayList<>());
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getStarred2()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null) {
+ List artists = response.body().getSubsonicResponse().getStarred2().getArtists();
+
+ if (artists != null) {
+ if (!random) {
+ getArtistInfo(artists, starredArtists);
+ } else {
+ Collections.shuffle(artists);
+ getArtistInfo(artists.subList(0, Math.min(size, artists.size())), starredArtists);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return starredArtists;
+ }
+
+ public MutableLiveData> getArtists(boolean random, int size) {
+ MutableLiveData> listLiveArtists = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtists()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ List artists = new ArrayList<>();
+
+ if(response.body().getSubsonicResponse().getArtists() != null && response.body().getSubsonicResponse().getArtists().getIndices() != null) {
+ for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
+ if(index != null && index.getArtists() != null) {
+ artists.addAll(index.getArtists());
+ }
+ }
+ }
+
+ if (random) {
+ Collections.shuffle(artists);
+ getArtistInfo(artists.subList(0, artists.size() / size > 0 ? size : artists.size()), listLiveArtists);
+ } else {
+ listLiveArtists.setValue(artists);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ }
+ });
+
+ return listLiveArtists;
+ }
+
+ /*
+ * Method that returns essential artist information (cover, album number, etc.)
+ */
+ public void getArtistInfo(List artists, MutableLiveData> list) {
+ List liveArtists = list.getValue();
+ if (liveArtists == null) liveArtists = new ArrayList<>();
+ list.setValue(liveArtists);
+
+ for (ArtistID3 artist : artists) {
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(artist.getId())
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null) {
+ addToMutableLiveData(list, response.body().getSubsonicResponse().getArtist());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+ }
+ }
+
+ public MutableLiveData getArtistInfo(String id) {
+ MutableLiveData artist = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null) {
+ artist.setValue(response.body().getSubsonicResponse().getArtist());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return artist;
+ }
+
+ public MutableLiveData getArtistFullInfo(String id) {
+ MutableLiveData artistFullInfo = new MutableLiveData<>(null);
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtistInfo2(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtistInfo2() != null) {
+ artistFullInfo.setValue(response.body().getSubsonicResponse().getArtistInfo2());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return artistFullInfo;
+ }
+
+ public void setRating(String id, int rating) {
+ App.getSubsonicClientInstance(false)
+ .getMediaAnnotationClient()
+ .setRating(id, rating)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+ }
+
+ public MutableLiveData getArtist(String id) {
+ MutableLiveData artist = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null) {
+ artist.setValue(response.body().getSubsonicResponse().getArtist());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return artist;
+ }
+
+ public MutableLiveData> getInstantMix(ArtistID3 artist, int count) {
+ MutableLiveData> instantMix = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getSimilarSongs2(artist.getId(), count)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSimilarSongs2() != null) {
+ instantMix.setValue(response.body().getSubsonicResponse().getSimilarSongs2().getSongs());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return instantMix;
+ }
+
+ public MutableLiveData> getRandomSong(ArtistID3 artist, int count) {
+ MutableLiveData> randomSongs = new MutableLiveData<>();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getArtist(artist.getId())
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null &&
+ response.body().getSubsonicResponse().getArtist() != null &&
+ response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
+
+ List albums = response.body().getSubsonicResponse().getArtist().getAlbums();
+ Log.d("ArtistRepository", "Got albums directly: " + albums.size());
+ if (albums.isEmpty()) {
+ Log.d("ArtistRepository", "No albums found in artist response");
+ return;
+ }
+
+ Collections.shuffle(albums);
+ int[] counts = albums.stream().mapToInt(AlbumID3::getSongCount).toArray();
+ Arrays.parallelPrefix(counts, Integer::sum);
+ int albumLimit = 0;
+ int multiplier = 4; // get more than the limit so we can shuffle them
+ while (albumLimit < albums.size() && counts[albumLimit] < count * multiplier)
+ albumLimit++;
+ Log.d("ArtistRepository", String.format("Retaining %d/%d albums", albumLimit, albums.size()));
+
+ fetchAllAlbumSongsWithCallback(albums.stream().limit(albumLimit).collect(Collectors.toList()), songs -> {
+ Collections.shuffle(songs);
+ randomSongs.setValue(songs.stream().limit(count).collect(Collectors.toList()));
+ });
+ } else {
+ Log.d("ArtistRepository", "Failed to get artist info");
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ Log.d("ArtistRepository", "Error getting artist info: " + t.getMessage());
+ }
+ });
+
+ return randomSongs;
+ }
+
+ public MutableLiveData> getTopSongs(String artistName, int count) {
+ MutableLiveData> topSongs = new MutableLiveData<>(new ArrayList<>());
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getTopSongs(artistName, count)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getTopSongs() != null && response.body().getSubsonicResponse().getTopSongs().getSongs() != null) {
+ topSongs.setValue(response.body().getSubsonicResponse().getTopSongs().getSongs());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return topSongs;
+ }
+
+ private void addToMutableLiveData(MutableLiveData> liveData, ArtistID3 artist) {
+ List liveArtists = liveData.getValue();
+ if (liveArtists != null) liveArtists.add(artist);
+ liveData.setValue(liveArtists);
+ }
+}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java
new file mode 100644
index 0000000..fe24d81
--- /dev/null
+++ b/app/src/main/java/com/cappielloantonio/tempo/repository/AutomotiveRepository.java
@@ -0,0 +1,1027 @@
+package com.cappielloantonio.tempo.repository;
+
+
+import android.net.Uri;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+import androidx.media3.common.MediaItem;
+import androidx.media3.common.MediaMetadata;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.session.LibraryResult;
+
+import com.cappielloantonio.tempo.App;
+import com.cappielloantonio.tempo.database.AppDatabase;
+import com.cappielloantonio.tempo.database.dao.ChronologyDao;
+import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
+import com.cappielloantonio.tempo.glide.CustomGlideRequest;
+import com.cappielloantonio.tempo.model.Chronology;
+import com.cappielloantonio.tempo.model.Download;
+import com.cappielloantonio.tempo.model.SessionMediaItem;
+import com.cappielloantonio.tempo.service.DownloaderManager;
+import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
+import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
+import com.cappielloantonio.tempo.subsonic.models.Artist;
+import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
+import com.cappielloantonio.tempo.subsonic.models.Child;
+import com.cappielloantonio.tempo.subsonic.models.Directory;
+import com.cappielloantonio.tempo.subsonic.models.Index;
+import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
+import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
+import com.cappielloantonio.tempo.subsonic.models.Playlist;
+import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
+import com.cappielloantonio.tempo.util.DownloadUtil;
+import com.cappielloantonio.tempo.util.MappingUtil;
+import com.cappielloantonio.tempo.util.MusicUtil;
+import com.cappielloantonio.tempo.util.Preferences;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AutomotiveRepository {
+ private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
+ private final ChronologyDao chronologyDao = AppDatabase.getInstance().chronologyDao();
+
+ public ListenableFuture>> getAlbums(String prefix, String type, int size) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getAlbumList2(type, size, 0, null, null)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
+ List albums = response.body().getSubsonicResponse().getAlbumList2().getAlbums();
+
+ List mediaItems = new ArrayList<>();
+
+ for (AlbumID3 album : albums) {
+ Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
+
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(album.getName())
+ .setAlbumTitle(album.getName())
+ .setArtist(album.getArtist())
+ .setGenre(album.getGenre())
+ .setIsBrowsable(true)
+ .setIsPlayable(false)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
+ .setArtworkUri(artworkUri)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + album.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri("")
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getStarredSongs() {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getStarred2()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) {
+ List songs = response.body().getSubsonicResponse().getStarred2().getSongs();
+
+ setChildrenMetadata(songs);
+
+ List mediaItems = MappingUtil.mapMediaItems(songs);
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getRandomSongs(int count) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getRandomSongs(100, null, null)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) {
+ List songs = response.body().getSubsonicResponse().getRandomSongs().getSongs();
+
+ setChildrenMetadata(songs);
+
+ List mediaItems = MappingUtil.mapMediaItems(songs);
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getRecentlyPlayedSongs(String server, int count) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ chronologyDao.getLastPlayed(server, count).observeForever(new Observer>() {
+ @Override
+ public void onChanged(List chronology) {
+ if (chronology != null && !chronology.isEmpty()) {
+ List songs = new ArrayList<>(chronology);
+
+ setChildrenMetadata(songs);
+
+ List mediaItems = MappingUtil.mapMediaItems(songs);
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+
+ chronologyDao.getLastPlayed(server, count).removeObserver(this);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getStarredAlbums(String prefix) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getStarred2()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getAlbums() != null) {
+ List albums = response.body().getSubsonicResponse().getStarred2().getAlbums();
+
+ List mediaItems = new ArrayList<>();
+
+ for (AlbumID3 album : albums) {
+ Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
+
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(album.getName())
+ .setArtist(album.getArtist())
+ .setGenre(album.getGenre())
+ .setIsBrowsable(true)
+ .setIsPlayable(false)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
+ .setArtworkUri(artworkUri)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + album.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri("")
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getStarredArtists(String prefix) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getAlbumSongListClient()
+ .getStarred2()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getArtists() != null) {
+ List artists = response.body().getSubsonicResponse().getStarred2().getArtists();
+
+ Collections.shuffle(artists);
+
+ List mediaItems = new ArrayList<>();
+
+ for (ArtistID3 artist : artists) {
+ Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize()));
+
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(artist.getName())
+ .setIsBrowsable(true)
+ .setIsPlayable(false)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
+ .setArtworkUri(artworkUri)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + artist.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri("")
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getMusicFolders(String prefix) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getMusicFolders()
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null && response.body().getSubsonicResponse().getMusicFolders().getMusicFolders() != null) {
+ List musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders();
+
+ List mediaItems = new ArrayList<>();
+
+ for (MusicFolder musicFolder : musicFolders) {
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(musicFolder.getName())
+ .setIsBrowsable(true)
+ .setIsPlayable(false)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + musicFolder.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri("")
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ } else {
+ listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getIndexes(String prefix, String id) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getIndexes(id, null)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getIndexes() != null) {
+ List mediaItems = new ArrayList<>();
+
+ if (response.body().getSubsonicResponse().getIndexes().getIndices() != null) {
+ List indices = response.body().getSubsonicResponse().getIndexes().getIndices();
+
+ for (Index index : indices) {
+ if (index.getArtists() != null) {
+ for (Artist artist : index.getArtists()) {
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(artist.getName())
+ .setIsBrowsable(true)
+ .setIsPlayable(false)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_ARTIST)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + artist.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri("")
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+ }
+ }
+ }
+
+ if (response.body().getSubsonicResponse().getIndexes().getChildren() != null) {
+ List children = response.body().getSubsonicResponse().getIndexes().getChildren();
+
+ for (Child song : children) {
+ Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(song.getCoverArtId(), Preferences.getImageSize()));
+
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(song.getTitle())
+ .setAlbumTitle(song.getAlbum())
+ .setArtist(song.getArtist())
+ .setIsBrowsable(false)
+ .setIsPlayable(true)
+ .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
+ .setArtworkUri(artworkUri)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(prefix + song.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri(MusicUtil.getStreamUri(song.getId()))
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ setChildrenMetadata(children);
+ }
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ listenableFuture.setException(t);
+ }
+ });
+
+ return listenableFuture;
+ }
+
+ public ListenableFuture>> getDirectories(String prefix, String id) {
+ final SettableFuture>> listenableFuture = SettableFuture.create();
+
+ App.getSubsonicClientInstance(false)
+ .getBrowsingClient()
+ .getMusicDirectory(id)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getDirectory() != null && response.body().getSubsonicResponse().getDirectory().getChildren() != null) {
+ Directory directory = response.body().getSubsonicResponse().getDirectory();
+
+ List mediaItems = new ArrayList<>();
+
+ for (Child child : directory.getChildren()) {
+ Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(child.getCoverArtId(), Preferences.getImageSize()));
+
+ MediaMetadata mediaMetadata = new MediaMetadata.Builder()
+ .setTitle(child.getTitle())
+ .setIsBrowsable(child.isDir())
+ .setIsPlayable(!child.isDir())
+ .setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
+ .setArtworkUri(artworkUri)
+ .build();
+
+ MediaItem mediaItem = new MediaItem.Builder()
+ .setMediaId(child.isDir() ? prefix + child.getId() : child.getId())
+ .setMediaMetadata(mediaMetadata)
+ .setUri(!child.isDir() ? MusicUtil.getStreamUri(child.getId()) : Uri.parse(""))
+ .build();
+
+ mediaItems.add(mediaItem);
+ }
+
+ setChildrenMetadata(directory.getChildren().stream().filter(child -> !child.isDir()).collect(Collectors.toList()));
+
+ LibraryResult> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
+
+ listenableFuture.set(libraryResult);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call