Compare commits

..

No commits in common. "master" and "v3.8.1" have entirely different histories.

992 changed files with 37833 additions and 130748 deletions

View File

@ -1,12 +1,14 @@
# The format:
# SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2
SPOTIFY_SECRETS=
# 0 or 1 # 0 or 1
# 0 = disable # 0 = disable
# 1 = enable # 1 = enable
ENABLE_UPDATE_CHECK=$ENABLE_UPDATE_CHECK ENABLE_UPDATE_CHECK=
LASTFM_API_KEY=$LASTFM_API_KEY LASTFM_API_KEY=
LASTFM_API_SECRET=$LASTFM_API_SECRET LASTFM_API_SECRET=
# Release channel. Can be: nightly, stable # Release channel. Can be: nightly, stable
RELEASE_CHANNEL=$RELEASE_CHANNEL RELEASE_CHANNEL=
HIDE_DONATIONS=$HIDE_DONATIONS

View File

@ -1,3 +1,4 @@
{ {
"flutterSdkVersion": "3.35.2" "flutterSdkVersion": "3.22.3",
"flavors": {}
} }

4
.fvmrc
View File

@ -1,4 +0,0 @@
{
"flutter": "3.35.2",
"flavors": {}
}

View File

@ -7,13 +7,8 @@ labels:
body: body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? (Please read the description) label: Is there an existing issue for this?
description: | description: Make sure to check if this issue is a duplicate.
🚨 PLEASE! Make sure to check if this issue is a duplicate. 🚨
Don't waste our time, we are working hard to make Spotube better for you.
Try with multiple similar keywords, and check the closed issues too.
options: options:
- label: I have searched the existing issues - label: I have searched the existing issues
required: true required: true
@ -21,47 +16,29 @@ body:
attributes: attributes:
label: Current Behavior label: Current Behavior
description: Write what you are experiencing currently. description: Write what you are experiencing currently.
placeholder: |
The app isn't working as expected. It crashes when I do this...
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: Expected Behavior label: Expected Behavior
description: Write what you expected to happen. description: Write what you expected to happen.
placeholder: |
The app should do this when I do that...
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: Steps to reproduce label: Steps to reproduce
description: Steps to reproduce the issue. A not well written description might lead to the delay in fixing the issue. description: Steps to reproduce the issue. A not well written description might delay the resolve of it.
placeholder: | placeholder: |
1. I opened the app 1. I opened the app
2. I did this 2. I did this
3. And that 3. And that
4. Then this happened 4. Then this happened
- type: textarea
attributes:
label: Logs
description: |
If you have any logs, paste them here. Make sure to remove any sensitive information.
You can find the logs in the app's Settings > Developers > Logs page.
value: |
<details>
<summary>Logs</summary>
```
<Replace this line by pasting your logs here>
```
</details>
validations: validations:
required: true required: true
- type: input - type: input
attributes: attributes:
label: Operating System label: Operating System
description: The OS in which you used Spotube to face the issue. Use comma to separate multiple OS. description: The OS in which you used Spotube to face the issue.
placeholder: Android, Linux, macOS or Windows? Make sure to include the version too. placeholder: Android, Linux, macOS or Windows? Make sure to include the version too.
validations: validations:
required: true required: true
@ -97,10 +74,7 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Self grab label: Self grab
description: | description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions!
If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. Any contributions are welcome!
This project is maintained by one person. So PRs are always welcome. This is the best way to get your issue fixed faster.
options: options:
- label: I'm ready to work on this issue! - label: I'm ready to work on this issue!
required: false required: false

View File

@ -4,7 +4,7 @@ on:
pull_request: pull_request:
env: env:
FLUTTER_VERSION: 3.35.2 FLUTTER_VERSION: 3.22.2
jobs: jobs:
lint: lint:
@ -17,21 +17,18 @@ jobs:
with: with:
flutter-version: ${{ env.FLUTTER_VERSION }} flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Dummy Envs
run: |
envsubst < .env.example > .env
env:
ENABLE_UPDATE_CHECK: true
LASTFM_API_KEY: xxx
LASTFM_API_SECRET: xxx
RELEASE_CHANNEL: nightly
HIDE_DONATIONS: 0
- name: Configure repo - name: Configure repo
run: | run: |
flutter pub get flutter pub get
echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
dart run build_runner build --delete-conflicting-outputs dart run build_runner build --delete-conflicting-outputs
- name: Lint Dart files - name: Lint Dart files
run: | run: |
dart analyze --no-fatal-warnings dart analyze --no-fatal-warnings
- name: Lint translations & config files
run: |
npm install -g @prantlf/jsonlint
jsonlint -q -D --enforce-double-quotes ./lib/l10n/*.arb
jsonlint -q -D --enforce-double-quotes -T .vscode/*.json

View File

@ -4,7 +4,7 @@ on:
inputs: inputs:
version: version:
description: Version to publish (x.x.x) description: Version to publish (x.x.x)
default: 4.0.0 default: 3.8.1
required: true required: true
dry_run: dry_run:
description: Dry run description: Dry run

View File

@ -20,8 +20,7 @@ on:
description: Dry run without uploading to release description: Dry run without uploading to release
env: env:
FLUTTER_VERSION: 3.35.2 FLUTTER_VERSION: 3.22.3
FLUTTER_CHANNEL: master
permissions: permissions:
contents: write contents: write
@ -31,72 +30,64 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: ubuntu-22.04 - os: ubuntu-latest
platform: linux platform: linux
arch: x86
files: | files: |
dist/Spotube-linux-x86_64.deb dist/Spotube-linux-x86_64.deb
dist/Spotube-linux-x86_64.rpm dist/Spotube-linux-x86_64.rpm
dist/spotube-linux-*-x86_64.tar.xz dist/spotube-linux-*-x86_64.tar.xz
- os: ubuntu-22.04-arm - os: ubuntu-latest
platform: linux platform: linux_arm
arch: arm64
files: | files: |
dist/Spotube-linux-aarch64.deb dist/Spotube-linux-aarch64.deb
dist/spotube-linux-*-aarch64.tar.xz dist/spotube-linux-*-aarch64.tar.xz
- os: ubuntu-22.04 - os: ubuntu-latest
platform: android platform: android
arch: all
files: | files: |
build/Spotube-android-all-arch.apk build/Spotube-android-all-arch.apk
build/Spotube-playstore-all-arch.aab build/Spotube-playstore-all-arch.aab
- os: windows-latest - os: windows-latest
platform: windows platform: windows
arch: x86
files: | files: |
dist/Spotube-windows-x86_64.nupkg dist/Spotube-windows-x86_64.nupkg
dist/Spotube-windows-x86_64-setup.exe dist/Spotube-windows-x86_64-setup.exe
- os: macos-14 - os: macos-latest
platform: ios platform: ios
arch: all
files: | files: |
Spotube-iOS.ipa Spotube-iOS.ipa
- os: macos-14 - os: macos-14
platform: macos platform: macos
arch: all
files: | files: |
build/Spotube-macos-universal.dmg build/Spotube-macos-universal.dmg
build/Spotube-macos-universal.pkg build/Spotube-macos-universal.pkg
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: subosito/flutter-action@v2.18.0 - uses: subosito/flutter-action@v2.12.0
with: with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: ${{ env.FLUTTER_CHANNEL }}
cache: true cache: true
git-source: https://github.com/flutter/flutter.git cache-key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.yaml') }}
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Setup Java - name: Setup Java
if: ${{matrix.platform == 'android'}} if: ${{matrix.platform == 'android'}}
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: "zulu" distribution: 'zulu'
java-version: "17" java-version: '17'
cache: "gradle" cache: 'gradle'
check-latest: true check-latest: true
- name: Set up QEMU
if: ${{matrix.platform == 'linux_arm'}}
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
if: ${{matrix.platform == 'linux_arm'}}
uses: docker/setup-buildx-action@v3
- name: Setup Rust toolchain - name: Setup Rust toolchain
if: ${{matrix.platform != 'linux_arm'}}
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
with: with:
toolchain: stable toolchain: stable
- name: Install Xcode
if: ${{matrix.platform == 'ios'}}
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
- name: Install ${{matrix.platform}} dependencies - name: Install ${{matrix.platform}} dependencies
run: | run: |
flutter pub get flutter pub get
@ -108,16 +99,28 @@ jobs:
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
echo '${{ secrets.KEY_PROPERTIES }}' > android/key.properties echo '${{ secrets.KEY_PROPERTIES }}' > android/key.properties
- name: Unessary hosted tools
if: ${{matrix.platform == 'linux_arm'}}
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
swap-storage: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
- name: Build ${{matrix.platform}} binaries - name: Build ${{matrix.platform}} binaries
run: dart cli/cli.dart build --arch=${{matrix.arch}} ${{matrix.platform}} run: dart cli/cli.dart build ${{matrix.platform}}
env: env:
CHANNEL: ${{inputs.channel}} CHANNEL: ${{inputs.channel}}
DOTENV: ${{secrets.DOTENV_RELEASE}} DOTENV: ${{secrets.DOTENV_RELEASE}}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
with: with:
if-no-files-found: error if-no-files-found: error
name: ${{matrix.platform}}-${{matrix.arch}} name: Spotube-Release-Binaries
path: ${{matrix.files}} path: ${{matrix.files}}
- name: Debug With SSH When fails - name: Debug With SSH When fails
@ -127,13 +130,14 @@ jobs:
limit-access-to-actor: true limit-access-to-actor: true
upload: upload:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
needs: needs:
- build_platform - build_platform
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
name: Spotube-Release-Binaries
path: ./Spotube-Release-Binaries path: ./Spotube-Release-Binaries
- name: Install dependencies - name: Install dependencies
@ -142,19 +146,18 @@ jobs:
- name: Generate Checksums - name: Generate Checksums
run: | run: |
tree . tree .
find Spotube-Release-Binaries -type f -exec md5sum {} \; >> RELEASE.md5sum md5sum Spotube-Release-Binaries/* >> RELEASE.md5sum
find Spotube-Release-Binaries -type f -exec sha256sum {} \; >> RELEASE.sha256sum sha256sum Spotube-Release-Binaries/* >> RELEASE.sha256sum
sed -i 's|Spotube-Release-Binaries/.*/\([^/]*\)$|\1|' RELEASE.sha256sum RELEASE.md5sum
sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum
- name: Extract pubspec version - name: Extract pubspec version
run: | run: |
echo "PUBSPEC_VERSION=$(grep -oP 'version:\s*\K[^+]+(?=\+)' pubspec.yaml)" >> $GITHUB_ENV echo "PUBSPEC_VERSION=$(grep -oP 'version:\s*\K[^+]+(?=\+)' pubspec.yaml)" >> $GITHUB_ENV
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
with: with:
if-no-files-found: error if-no-files-found: error
name: sums name: Spotube-Release-Binaries
path: | path: |
RELEASE.md5sum RELEASE.md5sum
RELEASE.sha256sum RELEASE.sha256sum
@ -169,7 +172,7 @@ jobs:
omitNameDuringUpdate: true omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true omitPrereleaseDuringUpdate: true
allowUpdates: true allowUpdates: true
artifacts: Spotube-Release-Binaries/**/*,RELEASE.sha256sum,RELEASE.md5sum artifacts: Spotube-Release-Binaries/*,RELEASE.sha256sum,RELEASE.md5sum
- name: Upload Release Binaries (nightly) - name: Upload Release Binaries (nightly)
if: ${{ !inputs.dry_run && inputs.channel == 'nightly' }} if: ${{ !inputs.dry_run && inputs.channel == 'nightly' }}
@ -181,15 +184,9 @@ jobs:
omitNameDuringUpdate: true omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true omitPrereleaseDuringUpdate: true
allowUpdates: true allowUpdates: true
artifacts: Spotube-Release-Binaries/**/*,RELEASE.sha256sum,RELEASE.md5sum artifacts: Spotube-Release-Binaries/*,RELEASE.sha256sum,RELEASE.md5sum
body: | body: |
Build Number: ${{github.run_number}} Build Number: ${{github.run_number}}
Nightly release includes newest features but may contain bugs Nightly release includes newest features but may contain bugs
It is preferred to use the stable version unless you know what you're doing It is preferred to use the stable version unless you know what you're doing
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true

7
.gitignore vendored
View File

@ -73,13 +73,8 @@ dist
appimage-build appimage-build
android/key.properties android/key.properties
.fvm/flutter_sdk
**/pb_data **/pb_data
tm.json tm.json
# FVM Version Cache
.fvm/
android/build
android/app/.cxx

View File

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited. # This file should be version controlled and should not be manually edited.
version: version:
revision: "d7b523b356d15fb81e7d340bbe52b47f93937323" revision: "300451adae589accbece3490f4396f10bdf15e6e"
channel: "stable" channel: "stable"
project_type: app project_type: app
@ -13,11 +13,11 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 base_revision: 300451adae589accbece3490f4396f10bdf15e6e
- platform: windows - platform: windows
create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 base_revision: 300451adae589accbece3490f4396f10bdf15e6e
# User provided section # User provided section

22
.vscode/launch.json vendored
View File

@ -17,17 +17,6 @@
"dev" "dev"
] ]
}, },
{
"name": "spotube (mobile-skia)",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"args": [
"--flavor",
"dev",
"--no-enable-impeller"
]
},
{ {
"name": "spotube (profile)", "name": "spotube (profile)",
"type": "dart", "type": "dart",
@ -41,17 +30,6 @@
"request": "launch", "request": "launch",
"program": "lib/main.dart", "program": "lib/main.dart",
"flutterMode": "release" "flutterMode": "release"
},
{
"name": "spotube (mobile) (release)",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"flutterMode": "release",
"args": [
"--flavor",
"dev"
]
} }
], ],
"compounds": [] "compounds": []

View File

@ -5,17 +5,14 @@
"ambiguate", "ambiguate",
"Amoled", "Amoled",
"Buildless", "Buildless",
"configurators",
"danceability", "danceability",
"fuzzywuzzy", "fuzzywuzzy",
"gapless", "gapless",
"instrumentalness", "instrumentalness",
"isrc",
"Mpris", "Mpris",
"RGBO", "RGBO",
"riverpod", "riverpod",
"Scrobblenaut", "Scrobblenaut",
"shadcn",
"skeletonizer", "skeletonizer",
"songlink", "songlink",
"speechiness", "speechiness",
@ -28,7 +25,6 @@
"explorer.fileNesting.patterns": { "explorer.fileNesting.patterns": {
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies,flutter_launcher_icons*.yaml,flutter_native_splash*.yaml", "pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies,flutter_launcher_icons*.yaml,flutter_native_splash*.yaml",
"README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md", "README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md",
"*.dart": "${capture}.g.dart,${capture}.freezed.dart" "*.dart": "${capture}.g.dart,${capture}.freezed.dart",
}, }
"dart.flutterSdkPath": ".fvm/versions/3.35.2"
} }

File diff suppressed because it is too large Load Diff

View File

@ -119,7 +119,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/KRTirt
Do the following: Do the following:
- Install [Dart](https://dart.dev/get-dart) and [fvm](https://fvm.app/documentation/getting-started/installation) - Download the latest Flutter SDK (>=3.16.0) & enable desktop support
- Install Development dependencies in linux - Install Development dependencies in linux
- Debian (>=12/Bookworm)/Ubuntu - Debian (>=12/Bookworm)/Ubuntu
```bash ```bash
@ -138,11 +138,11 @@ Do the following:
- Create a `.env` in root of the project following the `.env.example` template - Create a `.env` in root of the project following the `.env.example` template
- Now run the following to bootstrap the project - Now run the following to bootstrap the project
```bash ```bash
fvm flutter pub get && fvm dart run build_runner build --delete-conflicting-outputs flutter pub get && dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
``` ```
- Finally run these following commands in the root of the project to start the Spotube Locally - Finally run these following commands in the root of the project to start the Spotube Locally
```bash ```bash
fvm flutter run -d <window|macos|linux|(<android-device-id>)> flutter run -d <window|macos|linux|(<android-device-id>)>
``` ```
Do debugging/testing/build etc then submit to us with PR against the development branch (dev) & we'll review your code Do debugging/testing/build etc then submit to us with PR against the development branch (dev) & we'll review your code

View File

@ -1,12 +1,12 @@
BSD-4-Clause License BSD-4-Clause License
Copyright (c) 2025 Kingkor Roy Tirtho. All rights reserved. Copyright (c) 2023 Kingkor Roy Tirtho. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must display the following acknowledgement: 3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
This product includes software developed by Kingkor Roy Tirtho. This product includes software developed by Kingkor Roy Tirtho.
4. Neither the name of the Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 4. Neither the name of the Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY KINGKOR ROY TIRTHO AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KINGKOR ROY TIRTHO AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE IS PROVIDED BY KINGKOR ROY TIRTHO AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KINGKOR ROY TIRTHO AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -8,7 +8,7 @@ tar:
mkdir -p $(TEMP_DIR)\ mkdir -p $(TEMP_DIR)\
&& cp -r $(BUNDLE_DIR)/* $(TEMP_DIR)\ && cp -r $(BUNDLE_DIR)/* $(TEMP_DIR)\
&& cp linux/spotube.desktop $(TEMP_DIR)\ && cp linux/spotube.desktop $(TEMP_DIR)\
&& cp assets/branding/spotube-logo.png $(TEMP_DIR)\ && cp assets/spotube-logo.png $(TEMP_DIR)\
&& cp linux/com.github.KRTirtho.Spotube.appdata.xml $(TEMP_DIR)\ && cp linux/com.github.KRTirtho.Spotube.appdata.xml $(TEMP_DIR)\
&& tar -cJf build/spotube-linux-${VERSION}-${PKG_ARCH}.tar.xz -C $(TEMP_DIR) .\ && tar -cJf build/spotube-linux-${VERSION}-${PKG_ARCH}.tar.xz -C $(TEMP_DIR) .\
&& rm -rf $(TEMP_DIR) && rm -rf $(TEMP_DIR)
@ -43,16 +43,3 @@ apk:
gensums: gensums:
sh -c scripts/gensums.sh sh -c scripts/gensums.sh
migrate:
dart run drift_dev make-migrations
dmg:
flutter build macos &&\
if [ -f dist/Spotube-macos-universal.dmg ];\
then rm dist/Spotube-macos-universal.dmg;\
fi &&\
appdmg appdmg.json dist/Spotube-macos-universal.dmg
changelog:
git-cliff --unreleased

176
README.md
View File

@ -1,8 +1,9 @@
<div align="center"> <div align="center">
<img width="600" src="assets/branding/spotube_banner.png" alt="Spotube Logo"> <img width="600" src="assets/spotube_banner.png" alt="Spotube Logo">
A cross-platform extensible open-source music streaming platform.<br> An open source, cross-platform Spotify client compatible across multiple platforms<br />
Bring your own music metadata/playlist with plugins created by community or by yourself. A small step towards the decentralized music streaming era! utilizing Spotify's data API and YouTube, Piped.video or JioSaavn as an audio source,<br />
eliminating the need for Spotify Premium
Btw it's not just another Electron app 😉 Btw it's not just another Electron app 😉
@ -18,24 +19,31 @@ Btw it's not just another Electron app 😉
--- ---
![Spotube Desktop](assets/branding/spotube-screenshot.png) ![Spotube Desktop](assets/spotube-screenshot.png)
![Spotube Mobile](assets/branding/mobile-screenshots/combined.jpg) ![Spotube Mobile](assets/mobile-screenshots/combined.png)
</div> </div>
## 🌃 Features ## 🌃 Features
- 🧩 Plugin powered, supports any platform or custom music service through plugins. - 🚫 No ads, thanks to the use of public & free Spotify and YT Music APIs¹
- 🗺️ Community driven plugins for popular platforms or create your own. - ⬇️ Freely downloadable tracks
- ⬇️ Freely downloadable tracks with tagged metadata. - 🖥️ 📱 Cross-platform support
- 🖥️ 📱 Cross-platform support. - 🪶 Small size & less data usage
- 🪶 Small size & less data usage. - 🕵️ Anonymous/guest login
- 🕒 Time synced lyrics regardless of the plugin support. - 🕒 Time synced lyrics
- ✋ No telemetry, diagnostics or user data collection. - ✋ No telemetry, diagnostics or user data collection
- 🚀 Native performance. - 🚀 Native performance
- 📖 Open source/libre software. - 📖 Open source/libre software
- 🔉 Playback control is done locally, not on the server. - 🔉 Playback control is done locally, not on the server
**¹** It is still **recommended** to support creators by engaging with their YouTube channels/Spotify tracks (or preferably by buying their merch/concert tickets/physical media).
### ❌ Unsupported features
- 🗣️ **Spotify Shows & Podcasts:** Shows and Podcasts will <ins>**never be supported**</ins> because the audio tracks are <ins>_only_</ins> available on Spotify and accessing them would require Spotify Premium.
- 🎧 **Spotify Listen Along:** [Coming soon!](https://github.com/KRTirtho/spotube/issues/8)
## 📜 ⬇️ Installation guide ## 📜 ⬇️ Installation guide
@ -58,13 +66,17 @@ This handy table lists all the methods you can use to install Spotube:
<td>MacOS</td> <td>MacOS</td>
<td> <td>
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-macos-universal.dmg"> <a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-macos-universal.dmg">
<img width="220" alt="MacOS Download" src="https://memory-map.com/wp-content/uploads/download-mac-OS-01.svg"> <img width="220" alt="MacOS Download" src="https://reachify.io/wp-content/uploads/2018/09/mac-download-button-1.png">
</a> </a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Android</td> <td>Android</td>
<td> <td>
<a href="https://play.google.com/store/apps/details?id=oss.krtirtho.spotube">
<img width="220" alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png">
</a>
<br>
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-all-arch.apk"> <a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-all-arch.apk">
<img width="220" alt="APK download" src="https://user-images.githubusercontent.com/114044633/223920025-83687de0-e463-4c5d-8122-e06e4bb7d40c.png"> <img width="220" alt="APK download" src="https://user-images.githubusercontent.com/114044633/223920025-83687de0-e463-4c5d-8122-e06e4bb7d40c.png">
</a> </a>
@ -74,19 +86,6 @@ This handy table lists all the methods you can use to install Spotube:
</a> </a>
</td> </td>
</tr> </tr>
<tr>
<tr>
<td>iOS</td>
<td>
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-iOS.ipa">
<img width="220" alt="Download iOS IPA" src="https://github.com/user-attachments/assets/3e50d93d-fb39-435c-be6b-337745f7c423">
</a>
<br/>
<blockquote style="color:red">
*iPA file only. Requires sideloading with <a href="https://altstore.io/">AltStore</a> or similar tools.
</blockquote>
</td>
</tr>
<tr> <tr>
<td>Flatpak</td> <td>Flatpak</td>
<td> <td>
@ -98,7 +97,7 @@ This handy table lists all the methods you can use to install Spotube:
</tr> </tr>
<tr> <tr>
<td>AppImage</td> <td>AppImage</td>
<td>AppImage's lacking stability led to it's temporary removal. More information at https://github.com/KRTirtho/spotube/issues/1082</td> <td>AppImage's lacking stability led to it's temporal removal. More information at https://github.com/KRTirtho/spotube/issues/1082</td>
</tr> </tr>
<tr> <tr>
<td>Debian/Ubuntu</td> <td>Debian/Ubuntu</td>
@ -177,7 +176,9 @@ You can compile Spotube's source code by [following these instructions](CONTRIBU
## 👥 The Spotube team ## 👥 The Spotube team
- [Kingkor Roy Tirtho](https://github.com/KRTirtho) - The Founder, Maintainer and Lead Developer - [Kingkor Roy Tirtho](https://github.com/KRTirtho) - The Founder, Maintainer and Lead Developer
- [RaptaG](https://github.com/RaptaG) - The GitHub Moderator and Community Manager
- [Owen Connor](https://github.com/owencz1998) - The Cool Discord Moderator - [Owen Connor](https://github.com/owencz1998) - The Cool Discord Moderator
- [Meenbeese](https://github.com/meenbeese) - The Android Developer
- [Piotr Rogowski](https://github.com/karniv00l) - The MacOS Developer - [Piotr Rogowski](https://github.com/karniv00l) - The MacOS Developer
- [Rusty Apple](https://github.com/RustyApple) - The Mysterious Unknown Guy - [Rusty Apple](https://github.com/RustyApple) - The Mysterious Unknown Guy
@ -185,7 +186,7 @@ You can compile Spotube's source code by [following these instructions](CONTRIBU
Spotube is open source and licensed under the [BSD-4-Clause](/LICENSE) License. Spotube is open source and licensed under the [BSD-4-Clause](/LICENSE) License.
If you are curious, you can [read the reason of choosing this license](https://dev.to/krtirtho/choosing-open-source-license-wisely-1m3p). If you are concerned, you can [read the reason of choosing this license](https://dev.to/krtirtho/choosing-open-source-license-wisely-1m3p).
<details> <details>
<summary> <summary>
@ -193,17 +194,13 @@ If you are curious, you can [read the reason of choosing this license](https://d
</summary> </summary>
### Services ### Services
1. [Flutter](https://flutter.dev) - Flutter transforms the app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase 1. [Flutter](https://flutter.dev) - Flutter transforms the app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase
1. [MPV](https://mpv.io) - mpv is a free (as in freedom) media player for the command line. It supports a wide variety of media file formats, audio and video codecs, and subtitle types. 1. [Spotify API](https://developer.spotify.com/documentation/web-api) - The Spotify Web API is a RESTful API that provides access to Spotify data
1. [Musicbrainz](https://musicbrainz.org) - MusicBrainz is a MetaBrainz project that aims to create a collaborative music database that is similar to the freedb project.
1. [Listenbrainz](https://listenbrainz.org) - ListenBrainz is a open-source project by the MetaBrainz Foundation that allows users to crowdsource and publicly store their digital music listening data.
1. [Piped](https://piped-docs.kavin.rocks/) - Piped is a privacy friendly alternative YouTube frontend, which is efficient and scalable by design. 1. [Piped](https://piped-docs.kavin.rocks/) - Piped is a privacy friendly alternative YouTube frontend, which is efficient and scalable by design.
1. [Invidious](https://invidious.io/) - Invidious is an open source alternative front-end to YouTube. 1. [YouTube](https://youtube.com/) - YouTube is an American online video-sharing platform headquartered in San Bruno, California. Three former PayPal employees—Chad Hurley, Steve Chen, and Jawed Karim—created the service in February 2005
1. [yt-dlp](https://github.com/yt-dlp/yt-dlp) - A feature-rich command-line audio/video downloader. 1. [JioSaavn](https://www.jiosaavn.com) - JioSaavn is an Indian online music streaming service and a digital distributor of Bollywood, English and other regional Indian music across the world. Since it was founded in 2007 as Saavn, the company has acquired rights to over 5 crore (50 million) music tracks in 15 languages
1. [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) - NewPipe's core library for extracting data from streaming sites.
1. [SongLink](https://song.link) - SongLink is a free smart link service that helps you share music with your audience. It's a one-stop-shop for creating smart links for music, podcasts, and other audio content 1. [SongLink](https://song.link) - SongLink is a free smart link service that helps you share music with your audience. It's a one-stop-shop for creating smart links for music, podcasts, and other audio content
1. [LRCLib](https://lrclib.net/) - A public synced lyric API. 1. [LRCLib](https://lrclib.net/) - A public synced lyric API
1. [Linux](https://www.linux.org) - Linux is a family of open-source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged in a Linux distribution 1. [Linux](https://www.linux.org) - Linux is a family of open-source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged in a Linux distribution
1. [AUR](https://aur.archlinux.org) - AUR stands for Arch User Repository. It is a community-driven repository for Arch-based Linux distributions users 1. [AUR](https://aur.archlinux.org) - AUR stands for Arch User Repository. It is a community-driven repository for Arch-based Linux distributions users
1. [Flatpak](https://flatpak.org) - Flatpak is a utility for software deployment and package management for Linux 1. [Flatpak](https://flatpak.org) - Flatpak is a utility for software deployment and package management for Linux
@ -213,87 +210,102 @@ If you are curious, you can [read the reason of choosing this license](https://d
1. [LastFM](https://last.fm) - Last.fm is a music streaming and discovery platform that helps users discover and share new music. It tracks users' music listening habits across many devices and platforms. 1. [LastFM](https://last.fm) - Last.fm is a music streaming and discovery platform that helps users discover and share new music. It tracks users' music listening habits across many devices and platforms.
### Dependencies ### Dependencies
1. [app_links](https://github.com/llfbandit/app_links) - Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter (desktop included). 1. [app_links](https://github.com/llfbandit/app_links) - Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter (desktop included).
1. [args](https://pub.dev/packages/args) - Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. 1. [args](https://pub.dev/packages/args) - Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.
1. [async](https://pub.dev/packages/async) - Utility functions and classes related to the 'dart:async' library. 1. [async](https://pub.dev/packages/async) - Utility functions and classes related to the 'dart:async' library.
1. [audio_service](https://pub.dev/packages/audio_service) - Flutter plugin to play audio in the background while the screen is off.
1. [audio_service_mpris](https://github.com/bdrazhzhov/audio-service-mpris) - audio_service platform interface supporting Media Player Remote Interfacing Specification. 1. [audio_service_mpris](https://github.com/bdrazhzhov/audio-service-mpris) - audio_service platform interface supporting Media Player Remote Interfacing Specification.
1. [audio_service](https://pub.dev/packages/audio_service) - Flutter plugin to play audio in the background while the screen is off.
1. [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour. 1. [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.
1. [auto_route](https://github.com/Milad-Akarie/auto_route_library) - AutoRoute is a declarative routing solution, where everything needed for navigation is automatically generated for you.
1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds. 1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds.
1. [bonsoir](https://bonsoir.skyost.eu) - A Zeroconf library that allows you to discover network services and to broadcast your own. Based on Apple Bonjour and Android NSD. 1. [bonsoir](https://bonsoir.skyost.eu) - A Zeroconf library that allows you to discover network services and to broadcast your own. Based on Apple Bonjour and Android NSD.
1. [build_runner](https://pub.dev/packages/build_runner) - A build system for Dart code generation and modular compilation.
1. [buttons_tabbar](https://afonsoraposo.com) - A Flutter package that implements a TabBar where each label is a toggle button.
1. [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) - Flutter library to load and cache network images. Can also be used with placeholder and error widgets. 1. [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) - Flutter library to load and cache network images. Can also be used with placeholder and error widgets.
1. [connectivity_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin for discovering the state of the network (WiFi & mobile/cellular) connectivity on Android and iOS. 1. [catcher_2](https://github.com/ThexXTURBOXx/catcher_2) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer.
1. [device_info_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin providing detailed information about the device (make, model, etc.), and Android or iOS version the app is running on. 1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
1. [crypto](https://pub.dev/packages/crypto) - Implementations of SHA, MD5, and HMAC cryptographic functions.
1. [curved_navigation_bar](https://github.com/rafalbednarczuk/curved_navigation_bar) - Stunning Animating Curved Shape Navigation Bar. Adjustable color, background color, animation curve, animation duration.
1. [custom_lint](https://pub.dev/packages/custom_lint) - Lint rules are a powerful way to improve the maintainability of a project. Custom Lint allows package authors and developers to easily write custom lint rules.
1. [dart_discord_rpc](https://github.com/alexmercerind/dart_discord_rpc) - Discord Rich Presence for Flutter & Dart apps & games.
1. [dbus](https://github.com/canonical/dbus.dart) - A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.
1. [device_info_plus](https://plus.fluttercommunity.dev/) - Flutter plugin providing detailed information about the device (make, model, etc.), and Android or iOS version the app is running on.
1. [dio](https://github.com/cfug/dio) - A powerful HTTP networking package,supports Interceptors,Aborting and canceling a request,Custom adapters, Transformers, etc. 1. [dio](https://github.com/cfug/dio) - A powerful HTTP networking package,supports Interceptors,Aborting and canceling a request,Custom adapters, Transformers, etc.
1. [drift](https://drift.simonbinder.eu/) - Drift is a reactive library to store relational data in Dart and Flutter applications. 1. [disable_battery_optimization](https://github.com/pvsvamsi/Disable-Battery-Optimizations) - Flutter plugin to check and disable battery optimizations. Also shows custom steps to disable the optimizations in devices like mi, xiaomi, samsung, oppo, huawei, oneplus etc
1. [draggable_scrollbar](https://github.com/fluttercommunity/flutter-draggable-scrollbar) - A scrollbar that can be dragged for quickly navigation through a vertical list. Additional option is showing label next to scrollthumb with information about current item.
1. [duration](https://github.com/desktop-dart/duration) - Utilities to make working with 'Duration's easier. Formats duration in human readable form and also parses duration in human readable form to Dart's Duration. 1. [duration](https://github.com/desktop-dart/duration) - Utilities to make working with 'Duration's easier. Formats duration in human readable form and also parses duration in human readable form to Dart's Duration.
1. [encrypt](https://pub.dev/packages/encrypt) - A set of high-level APIs over PointyCastle for two-way cryptography. 1. [envied_generator](https://github.com/petercinibulk/envied) - Generator for the Envied package. See https://pub.dev/packages/envied.
1. [envied](https://github.com/petercinibulk/envied) - Explicitly reads environment variables into a dart file from a .env file for more security and faster start up times. 1. [envied](https://github.com/petercinibulk/envied) - Explicitly reads environment variables into a dart file from a .env file for more security and faster start up times.
1. [file_picker](https://github.com/miguelpruivo/plugins_flutter_file_picker) - A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support. 1. [file_picker](https://github.com/miguelpruivo/plugins_flutter_file_picker) - A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
1. [file_selector](https://pub.dev/packages/file_selector) - Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. 1. [file_selector](https://pub.dev/packages/file_selector) - Flutter plugin for opening and saving files, or selecting directories, using native file selection UI.
1. [fluentui_system_icons](https://github.com/microsoft/fluentui-system-icons/tree/main) - Fluent UI System Icons are a collection of familiar, friendly and modern icons from Microsoft. 1. [fluentui_system_icons](https://github.com/microsoft/fluentui-system-icons/tree/main) - Fluent UI System Icons are a collection of familiar, friendly and modern icons from Microsoft.
1. [flutter_broadcasts](https://pub.dev/packages/flutter_broadcasts) - A plugin for sending and receiving broadcasts with Android intents and iOS notifications.
1. [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager/tree/develop/flutter_cache_manager) - Generic cache manager for flutter. Saves web files on the storages of the device and saves the cache info using sqflite. 1. [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager/tree/develop/flutter_cache_manager) - Generic cache manager for flutter. Saves web files on the storages of the device and saves the cache info using sqflite.
1. [flutter_discord_rpc](https://pub.dev/packages/flutter_discord_rpc) - Discord RPC support for Flutter desktop platforms
1. [flutter_displaymode](https://github.com/ajinasokan/flutter_displaymode) - A Flutter plugin to set display mode (resolution, refresh rate) on Android platform. Allows to enable high refresh rate on supported devices. 1. [flutter_displaymode](https://github.com/ajinasokan/flutter_displaymode) - A Flutter plugin to set display mode (resolution, refresh rate) on Android platform. Allows to enable high refresh rate on supported devices.
1. [flutter_feather_icons](https://github.com/muj-programmer/flutter_feather_icons) - Feather is a collection of simply beautiful open source icons. Each icon is designed on a 24x24 grid with an emphasis on simplicity, consistency and usability. 1. [flutter_feather_icons](https://github.com/muj-programmer/flutter_feather_icons) - Feather is a collection of simply beautiful open source icons. Each icon is designed on a 24x24 grid with an emphasis on simplicity, consistency and usability.
1. [flutter_form_builder](https://github.com/flutter-form-builder-ecosystem) - This package helps in creation of forms in Flutter by removing the boilerplate code, reusing validation, react to changes, and collect final user input. 1. [flutter_gen_runner](https://github.com/FlutterGen/flutter_gen) - The Flutter code generator for your assets, fonts, colors, … — Get rid of all String-based APIs.
1. [flutter_hooks](https://github.com/rrousselGit/flutter_hooks) - A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse. 1. [flutter_hooks](https://github.com/rrousselGit/flutter_hooks) - A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse.
1. [flutter_inappwebview](https://inappwebview.dev/) - A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window. 1. [flutter_inappwebview](https://inappwebview.dev/) - A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
1. [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
1. [flutter_lints](https://pub.dev/packages/flutter_lints) - Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.
1. [flutter_native_splash](https://pub.dev/packages/flutter_native_splash) - Customize Flutter's default white native splash screen with background color and splash image. Supports dark mode, full screen, and more. 1. [flutter_native_splash](https://pub.dev/packages/flutter_native_splash) - Customize Flutter's default white native splash screen with background color and splash image. Supports dark mode, full screen, and more.
1. [flutter_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. 1. [flutter_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
1. [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) - Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android. 1. [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) - Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android.
1. [flutter_sharing_intent](https://github.com/bhagat-techind/flutter_sharing_intent.git) - A flutter plugin that allow flutter apps to receive photos, videos, text, urls or any other file types from another app. 1. [flutter_sharing_intent](https://github.com/bhagat-techind/flutter_sharing_intent.git) - A flutter plugin that allow flutter apps to receive photos, videos, text, urls or any other file types from another app.
1. [flutter_undraw](https://github.com/KRTirtho/flutter_undraw) - Undraw.co Illustrations for Flutter with customization options 1. [flutter_svg](https://pub.dev/packages/flutter_svg) - An SVG rendering and widget library for Flutter, which allows painting and displaying Scalable Vector Graphics 1.1 files.
1. [form_builder_validators](https://github.com/flutter-form-builder-ecosystem) - Form Builder Validators set of validators for FlutterFormBuilder. Provides common validators and a way to make your own.
1. [form_validator](https://github.com/TheMisir/form-validator) - Simplest form validation library for flutter's form field widgets 1. [form_validator](https://github.com/TheMisir/form-validator) - Simplest form validation library for flutter's form field widgets
1. [freezed_annotation](https://pub.dev/packages/freezed_annotation) - Annotations for the freezed code-generator. This package does nothing without freezed too. 1. [freezed_annotation](https://pub.dev/packages/freezed_annotation) - Annotations for the freezed code-generator. This package does nothing without freezed too.
1. [freezed](https://pub.dev/packages/freezed) - Code generation for immutable classes that has a simple syntax/API without compromising on the features.
1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs! 1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs!
1. [gap](https://github.com/letsar/gap) - Flutter widgets for easily adding gaps inside Flex widgets such as Columns and Rows or scrolling views. 1. [gap](https://github.com/letsar/gap) - Flutter widgets for easily adding gaps inside Flex widgets such as Columns and Rows or scrolling views.
1. [go_router](https://pub.dev/packages/go_router) - A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more
1. [google_fonts](https://pub.dev/packages/google_fonts) - A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling. 1. [google_fonts](https://pub.dev/packages/google_fonts) - A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling.
1. [home_widget](https://pub.dev/packages/home_widget) - A plugin to provide a common interface for creating HomeScreen Widgets for Android and iOS. 1. [hive_flutter](https://github.com/hivedb/hive/tree/master/hive_flutter) - Extension for Hive. Makes it easier to use Hive in Flutter apps.
1. [hive_generator](https://github.com/hivedb/hive/tree/master/hive_generator) - Extension for Hive. Automatically generates TypeAdapters to store any class.
1. [hive](https://github.com/hivedb/hive/tree/master/hive) - Lightweight and blazing fast key-value database written in pure Dart. Strongly encrypted using AES-256.
1. [hooks_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. 1. [hooks_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
1. [html](https://pub.dev/packages/html) - APIs for parsing and manipulating HTML content outside the browser.
1. [html_unescape](https://github.com/filiph/html_unescape) - A small library for un-escaping HTML. Supports all Named Character References, Decimal Character References and Hexadecimal Character References. 1. [html_unescape](https://github.com/filiph/html_unescape) - A small library for un-escaping HTML. Supports all Named Character References, Decimal Character References and Hexadecimal Character References.
1. [html](https://pub.dev/packages/html) - APIs for parsing and manipulating HTML content outside the browser.
1. [http](https://pub.dev/packages/http) - A composable, multi-platform, Future-based API for HTTP requests. 1. [http](https://pub.dev/packages/http) - A composable, multi-platform, Future-based API for HTTP requests.
1. [image_picker](https://pub.dev/packages/image_picker) - Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. 1. [image_picker](https://pub.dev/packages/image_picker) - Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera.
1. [intl](https://pub.dev/packages/intl) - Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues. 1. [intl](https://pub.dev/packages/intl) - Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
1. [invidious](https://pub.dev/packages/invidious) - Invidious API client for Dart and Flutter. 1. [introduction_screen](https://pub.dev/packages/introduction_screen) - Introduction/Onboarding package for flutter app with some customizations possibilities
1. [io](https://pub.dev/packages/io) - Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values.
1. [jiosaavn](https://github.com/KRTirtho/jiosaavn) - Unofficial API client for jiosaavn.com 1. [jiosaavn](https://github.com/KRTirtho/jiosaavn) - Unofficial API client for jiosaavn.com
1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package. 1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package.
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
1. [local_notifier](https://github.com/leanflutter/local_notifier) - This plugin allows Flutter desktop apps to displaying local notifications. 1. [local_notifier](https://github.com/leanflutter/local_notifier) - This plugin allows Flutter desktop apps to displaying local notifications.
1. [logger](https://pub.dev/packages/logger) - Small, easy to use and extensible logger which prints beautiful logs. 1. [logger](https://pub.dev/packages/logger) - Small, easy to use and extensible logger which prints beautiful logs.
1. [logging](https://pub.dev/packages/logging) - Provides APIs for debugging and error logging, similar to loggers in other languages, such as the Closure JS Logger and java.util.logging.Logger.
1. [lrc](https://pub.dev/packages/lrc) - A Dart-only package that creates, parses, and handles LRC, which is a format that stores song lyrics. 1. [lrc](https://pub.dev/packages/lrc) - A Dart-only package that creates, parses, and handles LRC, which is a format that stores song lyrics.
1. [media_kit](https://github.com/media-kit/media-kit) - A cross-platform video player & audio player for Flutter & Dart. Performant, stable, feature-proof & modular.
1. [media_kit_libs_audio](https://github.com/media-kit/media-kit.git) - package:media_kit audio (only) playback native libraries for all platforms. 1. [media_kit_libs_audio](https://github.com/media-kit/media-kit.git) - package:media_kit audio (only) playback native libraries for all platforms.
1. [metadata_god](https://pub.dev/packages/metadata_god) - Plugin for retrieving and writing audio tags/metadata from audio files 1. [media_kit](https://github.com/media-kit/media-kit) - A cross-platform video player & audio player for Flutter & Dart. Performant, stable, feature-proof & modular.
1. [metadata_god](https://github.com/KRTirtho/metadata_god) - Plugin for retrieving and writing audio tags/metadata from audio files
1. [mime](https://pub.dev/packages/mime) - Utilities for handling media (MIME) types, including determining a type from a file extension and file contents. 1. [mime](https://pub.dev/packages/mime) - Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.
1. [open_file](https://pub.dev/packages/open_file) - A plug-in that can call native APP to open files with string result in flutter, support iOS(UTI) / android(intent) / PC(ffi) / web(dart:html) 1. [package_info_plus](https://plus.fluttercommunity.dev/) - Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.
1. [package_info_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.
1. [palette_generator](https://pub.dev/packages/palette_generator) - Flutter package for generating palette colors from a source image. 1. [palette_generator](https://pub.dev/packages/palette_generator) - Flutter package for generating palette colors from a source image.
1. [path](https://pub.dev/packages/path) - A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.
1. [path_provider](https://pub.dev/packages/path_provider) - Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. 1. [path_provider](https://pub.dev/packages/path_provider) - Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.
1. [path](https://pub.dev/packages/path) - A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.
1. [permission_handler](https://pub.dev/packages/permission_handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions. 1. [permission_handler](https://pub.dev/packages/permission_handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video 1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video
1. [riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. 1. [popover](https://github.com/minikin/popover) - A popover is a transient view that appears above other content onscreen when you tap a control or in an area.
1. [process_run](https://github.com/tekartik/process_run.dart/blob/master/packages/process_run) - Process run helpers for Linux/Win/Mac and which like feature for finding executables.
1. [pub_api_client](https://github.com/leoafarias/pub_api_client) - An API Client for Pub to interact with public package information.
1. [pubspec_parse](https://pub.dev/packages/pubspec_parse) - Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting.
1. [riverpod_lint](https://riverpod.dev) - Riverpod_lint is a developer tool for users of Riverpod, designed to help stop common issues and simplify repetitive tasks.
1. [scrobblenaut](https://github.com/Nebulino/Scrobblenaut) - A deadly simple LastFM API Wrapper for Dart. So deadly simple that it's gonna hit the mark.
1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter 1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter
1. [shadcn_flutter](https://github.com/sunarya-thito/shadcn_flutter) - Beautifully designed components from Shadcn/UI is now available for Flutter
1. [shared_preferences](https://pub.dev/packages/shared_preferences) - Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. 1. [shared_preferences](https://pub.dev/packages/shared_preferences) - Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.
1. [shelf](https://pub.dev/packages/shelf) - A model for web server middleware that encourages composition and easy reuse.
1. [shelf_router](https://pub.dev/packages/shelf_router) - A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations. 1. [shelf_router](https://pub.dev/packages/shelf_router) - A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations.
1. [shelf_web_socket](https://pub.dev/packages/shelf_web_socket) - A shelf handler that wires up a listener for every connection. 1. [shelf_web_socket](https://pub.dev/packages/shelf_web_socket) - A shelf handler that wires up a listener for every connection.
1. [shelf](https://pub.dev/packages/shelf) - A model for web server middleware that encourages composition and easy reuse.
1. [sidebarx](https://github.com/Frezyx/sidebarx) - flutter multiplatform navigation sidebar / side navigationbar / drawer widget
1. [simple_icons](https://teavelopment.com/) - The Simple Icon pack available as Flutter Icons. Provides over 1500 Free SVG icons for popular brands. 1. [simple_icons](https://teavelopment.com/) - The Simple Icon pack available as Flutter Icons. Provides over 1500 Free SVG icons for popular brands.
1. [skeleton_text](https://github.com/101Loop/Skeleton-Text) - A package that provides an easy way to add skeleton text loading animation in Flutter project. This project is a part of 101Loop community.
1. [skeletonizer](https://github.com/Milad-Akarie/skeletonizer) - Converts already built widgets into skeleton loaders with no extra effort. 1. [skeletonizer](https://github.com/Milad-Akarie/skeletonizer) - Converts already built widgets into skeleton loaders with no extra effort.
1. [sliding_up_panel](https://github.com/akshathjain/sliding_up_panel) - A draggable Flutter widget that makes implementing a SlidingUpPanel much easier!
1. [sliver_tools](https://github.com/Kavantix) - A set of useful sliver tools that are missing from the flutter framework 1. [sliver_tools](https://github.com/Kavantix) - A set of useful sliver tools that are missing from the flutter framework
1. [smtc_windows](https://pub.dev/packages/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet. 1. [smtc_windows](https://github.com/KRTirtho/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet.
1. [sqlite3](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3) - Provides lightweight yet convenient bindings to SQLite by using dart:ffi 1. [spotify](https://github.com/rinukkusu/spotify-dart) - An incomplete dart library for interfacing with the Spotify Web API.
1. [sqlite3_flutter_libs](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3_flutter_libs) - Flutter plugin to include native sqlite3 libraries with your app
1. [stroke_text](https://github.com/MohamedAbd0/stroke_text) - A Simple Flutter plugin for applying stroke (border) style to a text widget 1. [stroke_text](https://github.com/MohamedAbd0/stroke_text) - A Simple Flutter plugin for applying stroke (border) style to a text widget
1. [system_theme](https://github.com/bdlukaa/system_theme/tree/master/system_theme) - A plugin to get the current system theme info. Supports Android, Web, Windows, Linux and macOS 1. [system_theme](https://pub.dev/packages/system_theme) - A plugin to get the current system theme info. Supports Android, Web, Windows, Linux and macOS
1. [test](https://pub.dev/packages/test) - A full featured library for writing and running Dart tests across platforms.
1. [timezone](https://pub.dev/packages/timezone) - Time zone database and time zone aware DateTime. 1. [timezone](https://pub.dev/packages/timezone) - Time zone database and time zone aware DateTime.
1. [titlebar_buttons](https://github.com/gtk-flutter/titlebar_buttons) - A package which provides most of the titlebar buttons from windows, linux and macos. 1. [titlebar_buttons](https://github.com/gtk-flutter/titlebar_buttons) - A package which provides most of the titlebar buttons from windows, linux and macos.
1. [tray_manager](https://github.com/leanflutter/tray_manager) - This plugin allows Flutter desktop apps to defines system tray. 1. [tray_manager](https://github.com/leanflutter/tray_manager) - This plugin allows Flutter desktop apps to defines system tray.
@ -306,32 +318,8 @@ If you are curious, you can [read the reason of choosing this license](https://d
1. [wikipedia_api](https://github.com/KRTirtho/wikipedia_api) - Wikipedia API for dart and flutter 1. [wikipedia_api](https://github.com/KRTirtho/wikipedia_api) - Wikipedia API for dart and flutter
1. [win32_registry](https://pub.dev/packages/win32_registry) - A package that provides a friendly Dart API for accessing the Windows Registry. 1. [win32_registry](https://pub.dev/packages/win32_registry) - A package that provides a friendly Dart API for accessing the Windows Registry.
1. [window_manager](https://github.com/leanflutter/window_manager) - This plugin allows Flutter desktop apps to resizing and repositioning the window. 1. [window_manager](https://github.com/leanflutter/window_manager) - This plugin allows Flutter desktop apps to resizing and repositioning the window.
1. [youtube_explode_dart](https://github.com/Hexer10/youtube_explode_dart) - A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
1. [http_parser](https://pub.dev/packages/http_parser) - A platform-independent package for parsing and serializing HTTP formats.
1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
1. [otp_util](https://github.com/dushiling) - otp_util is a dart package to generate and verify one-time passwords,it It provides two methods TOPT and HOTP.They are Time-based OTPs and Counter-based OTPs.
1. [dio_http2_adapter](https://github.com/cfug/dio) - An adapter that combines HTTP/2 and dio. Supports reusing connections, header compression, etc.
1. [build_runner](https://pub.dev/packages/build_runner) - A build system for Dart code generation and modular compilation.
1. [envied_generator](https://github.com/petercinibulk/envied) - Generator for the Envied package. See https://pub.dev/packages/envied.
1. [flutter_gen_runner](https://github.com/FlutterGen/flutter_gen) - The Flutter code generator for your assets, fonts, colors, … — Get rid of all String-based APIs.
1. [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
1. [flutter_lints](https://pub.dev/packages/flutter_lints) - Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
1. [freezed](https://pub.dev/packages/freezed) - Code generation for immutable classes that has a simple syntax/API without compromising on the features.
1. [process_run](https://github.com/tekartik/process_run.dart/blob/master/packages/process_run) - Process run helpers for Linux/Win/Mac and which like feature for finding executables.
1. [pubspec_parse](https://pub.dev/packages/pubspec_parse) - Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting.
1. [pub_api_client](https://github.com/leoafarias/pub_api_client) - An API Client for Pub to interact with public package information.
1. [xml](https://github.com/renggli/dart-xml) - A lightweight library for parsing, traversing, querying, transforming and building XML documents. 1. [xml](https://github.com/renggli/dart-xml) - A lightweight library for parsing, traversing, querying, transforming and building XML documents.
1. [io](https://pub.dev/packages/io) - Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values. 1. [youtube_explode_dart](https://github.com/Hexer10/youtube_explode_dart) - A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
1. [drift_dev](https://drift.simonbinder.eu/) - Dev-dependency for users of drift. Contains the generator and development tools.
1. [auto_route_generator](https://github.com/Milad-Akarie/auto_route_library) - AutoRoute is a declarative routing solution, where everything needed for navigation is automatically generated for you.
1. [desktop_webview_window](https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_webview_window) - Show a webview window on your flutter desktop application.
1. [disable_battery_optimization](https://github.com/pvsvamsi/Disable-Battery-Optimizations) - Flutter plugin to check and disable battery optimizations. Also shows custom steps to disable the optimizations in devices like mi, xiaomi, samsung, oppo, huawei, oneplus etc
1. [draggable_scrollbar](https://github.com/fluttercommunity/flutter-draggable-scrollbar) - A scrollbar that can be dragged for quickly navigation through a vertical list. Additional option is showing label next to scrollthumb with information about current item.
1. [flutter_broadcasts](https://github.com/KRTirtho/flutter_broadcasts.git) - A plugin for sending and receiving broadcasts with Android intents and iOS notifications.
1. [scrobblenaut](https://github.com/Nebulino/Scrobblenaut) - A deadly simple LastFM API Wrapper for Dart. So deadly simple that it's gonna hit the mark.
1. [yt_dlp_dart](https://github.com/KRTirtho/yt_dlp_dart.git) - yt-dlp binding in Dart
1. [flutter_new_pipe_extractor](https://github.com/KRTirtho/flutter_new_pipe_extractor) - NewPipeExtractor binding for Flutter (Android only)
</details> </details>
<div align="center"><h4>© Copyright Spotube 2025</h4></div> <div align="center"><h4>© Copyright Spotube 2024</h4></div>

View File

@ -32,9 +32,10 @@ linter:
analyzer: analyzer:
errors: errors:
invalid_annotation_target: ignore invalid_annotation_target: ignore
plugins:
- custom_lint
exclude: exclude:
- "**.freezed.dart" - "**.freezed.dart"
- "**.g.dart" - "**.g.dart"
- "**.gr.dart" - "**.gr.dart"
- "**/generated_plugin_registrant.dart" - "**/generated_plugin_registrant.dart"
- test/**/*.dart

View File

@ -28,17 +28,12 @@ if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} }
def composeVersion = "1.4.8"
android { android {
namespace "oss.krtirtho.spotube" compileSdkVersion 34
compileSdkVersion 36 ndkVersion "25.1.8937393"
ndkVersion = "27.0.12077973"
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
@ -51,18 +46,10 @@ android {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion "$composeVersion" // Correlates with org.jetbrains.kotlin.android plugin in settings.gradle
}
defaultConfig { defaultConfig {
applicationId "oss.krtirtho.spotube" applicationId "oss.krtirtho.spotube"
minSdkVersion 24 minSdkVersion 24
targetSdkVersion 35 targetSdkVersion 34
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true multiDexEnabled true
@ -76,7 +63,6 @@ android {
storePassword keystoreProperties['storePassword'] storePassword keystoreProperties['storePassword']
} }
} }
buildTypes { buildTypes {
release { release {
signingConfig signingConfigs.release signingConfig signingConfigs.release
@ -110,30 +96,15 @@ android {
} }
} }
packagingOptions {
resources.excludes += "DebugProbesKt.bin"
}
} }
flutter { flutter {
source '../..' source '../..'
} }
def glanceVersion = "1.1.1"
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
// other deps so just ignore // other deps so just ignore
implementation 'com.android.support:multidex:2.0.1' implementation 'com.android.support:multidex:2.0.1'
implementation "androidx.glance:glance-appwidget:$glanceVersion"
implementation "androidx.glance:glance-appwidget-preview:$glanceVersion"
implementation "androidx.glance:glance-preview:$glanceVersion"
implementation "androidx.glance:glance-material3:$glanceVersion"
implementation "androidx.glance:glance-material:$glanceVersion"
implementation "androidx.work:work-runtime-ktx:2.8.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
implementation 'com.google.code.gson:gson:2.11.0'
} }

View File

@ -1,59 +1 @@
#Flutter Wrapper
# -keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
# -keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-keep class de.prosiebensat1digital.** { *; }
-keep class androidx.lifecycle.DefaultLifecycleObserver -keep class androidx.lifecycle.DefaultLifecycleObserver
-keepnames class kotlinx.serialization.** { *; }
-keepnames class oss.krtirtho.spotube.glance.models.** { *; }
-keep @kotlinx.serialization.Serializable class *
-keepclassmembers class ** {
@kotlinx.serialization.* <fields>;
}
## We don't need beans
-dontwarn java.beans.BeanDescriptor
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.Introspector
-dontwarn java.beans.PropertyDescriptor
## Rules for NewPipeExtractor
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter
-dontwarn org.mozilla.javascript.tools.**
-dontwarn javax.script.AbstractScriptEngine
-dontwarn javax.script.Bindings
-dontwarn javax.script.Compilable
-dontwarn javax.script.CompiledScript
-dontwarn javax.script.Invocable
-dontwarn javax.script.ScriptContext
-dontwarn javax.script.ScriptEngine
-dontwarn javax.script.ScriptEngineFactory
-dontwarn javax.script.ScriptException
-dontwarn javax.script.SimpleBindings
-dontwarn jdk.dynalink.CallSiteDescriptor
-dontwarn jdk.dynalink.DynamicLinker
-dontwarn jdk.dynalink.DynamicLinkerFactory
-dontwarn jdk.dynalink.NamedOperation
-dontwarn jdk.dynalink.Namespace
-dontwarn jdk.dynalink.NamespaceOperation
-dontwarn jdk.dynalink.Operation
-dontwarn jdk.dynalink.RelinkableCallSite
-dontwarn jdk.dynalink.StandardNamespace
-dontwarn jdk.dynalink.StandardOperation
-dontwarn jdk.dynalink.linker.GuardedInvocation
-dontwarn jdk.dynalink.linker.GuardingDynamicLinker
-dontwarn jdk.dynalink.linker.LinkRequest
-dontwarn jdk.dynalink.linker.LinkerServices
-dontwarn jdk.dynalink.linker.TypeBasedGuardingDynamicLinker
-dontwarn jdk.dynalink.linker.support.CompositeTypeBasedGuardingDynamicLinker
-dontwarn jdk.dynalink.linker.support.Guards
-dontwarn jdk.dynalink.support.ChainedCallSite

View File

@ -1,19 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="oss.krtirtho.spotube">
<!-- Flutter needs it to communicate with the running application <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET"/>
<application
android:name="${applicationName}"
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_en"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<!-- Disable Impeller -->
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" />
</application>
</manifest> </manifest>

View File

@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="oss.krtirtho.spotube">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
@ -17,26 +17,28 @@
</queries> </queries>
<application <application
android:name="${applicationName}"
android:allowBackup="false" android:allowBackup="false"
android:fullBackupContent="false" android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_en" android:label="@string/app_name_en"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"> >
<!-- Enable Impeller --> <!-- Enable Impeller -->
<!-- <meta-data <!-- <meta-data
android:name="io.flutter.embedding.android.EnableImpeller" android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" /> --> android:value="true" /> -->
<activity <activity
android:name="com.ryanheise.audioservice.AudioServiceActivity" android:name="com.ryanheise.audioservice.AudioServiceActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true" android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"> android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
>
<!-- <!--
Specifies an Android theme to apply to this Activity as soon as Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
@ -45,8 +47,8 @@
--> -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" /> android:resource="@style/NormalTheme"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -54,9 +56,12 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="open.spotify.com"
/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
@ -67,28 +72,23 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="spotube" /> <!-- Accepts URIs that begin with "spotify:// -->
<data android:scheme="spotify" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="es.antonborri.home_widget.action.LAUNCH" />
</intent-filter>
</activity> </activity>
<!-- AudioService Config --> <!-- AudioService Config -->
<service <service android:name="com.ryanheise.audioservice.AudioService"
android:name="com.ryanheise.audioservice.AudioService" android:foregroundServiceType="mediaPlayback"
android:exported="true" android:exported="true">
android:foregroundServiceType="mediaPlayback">
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService" /> <action android:name="android.media.browse.MediaBrowserService" />
</intent-filter> </intent-filter>
</service> </service>
<receiver <receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" /> <action android:name="android.intent.action.MEDIA_BUTTON" />
@ -96,40 +96,11 @@
</receiver> </receiver>
<!-- =================== --> <!-- =================== -->
<meta-data <meta-data android:name="com.google.android.gms.car.application"
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" /> android:resource="@xml/automotive_app_desc" />
<!-- Home Widget config -->
<receiver
android:name=".glance.HomePlayerWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/home_player_widget_config" />
</receiver>
<receiver
android:name="es.antonborri.home_widget.HomeWidgetBackgroundReceiver"
android:exported="true">
<intent-filter>
<action android:name="es.antonborri.home_widget.action.BACKGROUND" />
</intent-filter>
</receiver>
<service
android:name="es.antonborri.home_widget.HomeWidgetBackgroundService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<!-- =================== -->
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data android:name="flutterEmbedding" android:value="2" />
android:name="flutterEmbedding"
android:value="2" />
</application> </application>
</manifest> </manifest>

View File

@ -1,207 +0,0 @@
package oss.krtirtho.spotube.glance
import HomeWidgetGlanceState
import HomeWidgetGlanceStateDefinition
import android.R
import android.content.Context
import android.graphics.drawable.Icon
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalSize
import androidx.glance.action.ActionParameters
import androidx.glance.action.actionParametersOf
import androidx.glance.action.clickable
import androidx.glance.background
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.background
import androidx.glance.appwidget.components.CircleIconButton
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.state.GlanceStateDefinition
import com.google.gson.Gson
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.actionStartActivity
import oss.krtirtho.spotube.MainActivity
import oss.krtirtho.spotube.glance.models.Track
import oss.krtirtho.spotube.glance.widgets.FlutterAssetImageProvider
import oss.krtirtho.spotube.glance.widgets.TrackDetailsView
import oss.krtirtho.spotube.glance.widgets.TrackProgress
val gson = Gson()
val serverAddressKey = ActionParameters.Key<String>("serverAddress")
class Breakpoints {
companion object {
val SMALL_SQUARE = DpSize(100.dp, 100.dp)
val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp)
val BIG_SQUARE = DpSize(250.dp, 250.dp)
}
}
class HomePlayerWidget : GlanceAppWidget() {
override val sizeMode = SizeMode.Responsive(
setOf(
Breakpoints.SMALL_SQUARE,
Breakpoints.HORIZONTAL_RECTANGLE,
Breakpoints.BIG_SQUARE
)
)
override val stateDefinition: GlanceStateDefinition<*>?
get() = HomeWidgetGlanceStateDefinition()
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceContent(context, currentState())
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(widthDp = 100, heightDp = 100)
@Composable
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
val prefs = currentState.preferences
val size = LocalSize.current
val activeTrackStr = prefs.getString("activeTrack", null)
val isPlaying = prefs.getBoolean("isPlaying", false)
val playbackServerAddress = prefs.getString("playbackServerAddress", null) ?: ""
var activeTrack: Track? = null
if (activeTrackStr != null) {
activeTrack = gson.fromJson(activeTrackStr, Track::class.java)
}
val playIcon = Icon.createWithResource(context, R.drawable.ic_media_play);
val pauseIcon = Icon.createWithResource(context, R.drawable.ic_media_pause);
val previousIcon = Icon.createWithResource(context, R.drawable.ic_media_previous);
val nextIcon = Icon.createWithResource(context, R.drawable.ic_media_next);
GlanceTheme {
Box(
modifier = GlanceModifier
.fillMaxSize()
.cornerRadius(8.dp)
.background(
color = GlanceTheme.colors.surface.getColor(context)
)
.clickable {
actionStartActivity<MainActivity>(context)
}
,
) {
Box(
modifier = GlanceModifier
.background(
color =
GlanceTheme.colors.surface.getColor(context)
.copy(alpha = 0.5f),
)
.fillMaxSize(),
) {}
Column(
modifier = GlanceModifier.padding(top = 10.dp, start = 10.dp, end = 10.dp)
) {
Row(verticalAlignment = Alignment.Vertical.CenterVertically) {
TrackDetailsView(activeTrack)
}
Spacer(modifier = GlanceModifier.size(6.dp))
if (size != Breakpoints.SMALL_SQUARE) {
TrackProgress(prefs)
}
Spacer(modifier = GlanceModifier.size(6.dp))
Row(
modifier = GlanceModifier.fillMaxWidth(),
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
) {
CircleIconButton(
imageProvider = ImageProvider(previousIcon),
contentDescription = "Previous",
onClick = actionRunCallback<PreviousAction>(
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
)
)
Spacer(modifier = GlanceModifier.size(6.dp))
CircleIconButton(
imageProvider =
if (isPlaying) ImageProvider(pauseIcon)
else ImageProvider(playIcon),
contentDescription = "Play/Pause",
onClick = actionRunCallback<PlayPauseAction>(
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
)
)
Spacer(modifier = GlanceModifier.size(6.dp))
CircleIconButton(
imageProvider = ImageProvider(nextIcon),
contentDescription = "Previous",
onClick = actionRunCallback<NextAction>(
parameters = actionParametersOf(
serverAddressKey to playbackServerAddress
)
)
)
}
}
}
}
}
}
class PlayPauseAction : InteractiveAction("toggle-playback")
class NextAction : InteractiveAction("next")
class PreviousAction : InteractiveAction("previous")
abstract class InteractiveAction(val command: String) : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val serverAddress = parameters[serverAddressKey] ?: ""
Log.d("HomePlayerWidget", "Sending command $command to $serverAddress")
if (serverAddress == null || serverAddress.isEmpty()) {
return
}
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(
context,
Uri.parse("spotube://playback/$command?serverAddress=$serverAddress")
)
backgroundIntent.send()
}
}

View File

@ -1,7 +0,0 @@
package oss.krtirtho.spotube.glance
import HomeWidgetGlanceWidgetReceiver
class HomePlayerWidgetReceiver : HomeWidgetGlanceWidgetReceiver<HomePlayerWidget>() {
override val glanceAppWidget = HomePlayerWidget()
}

View File

@ -1,40 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
@Serializable
data class AlbumSimple(
@SerializedName("album_type")
val albumType: AlbumType?,
@SerializedName("available_markets")
val availableMarkets: List<Market>?,
val href: String?,
val id: String?,
val images: List<Image>?,
val name: String?,
@SerializedName("release_date")
val releaseDate: String?,
@SerializedName("release_date_precision")
val releaseDatePrecision: DatePrecision?,
val type: String?,
val uri: String?,
)
@Serializable
enum class AlbumType {
album,
single,
compilation
}
enum class DatePrecision {
year,
month,
day
}

View File

@ -1,25 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
@Serializable
data class Artist(
val href: String?,
val id: String?,
val name: String?,
val type: String?,
val uri: String?,
val followers: Followers?,
val genres: List<String>?,
val images: List<Image>?,
@SerializedName("popularity")
val popularity: Int?
)
@Serializable
data class Followers(
val total: Int?
)

View File

@ -1,10 +0,0 @@
package oss.krtirtho.spotube.glance.models
import kotlinx.serialization.Serializable
@Serializable
data class Image(
val height: Int?,
val width: Int?,
val path: String,
)

View File

@ -1,37 +0,0 @@
package oss.krtirtho.spotube.glance.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.milliseconds
@Serializable
data class Track(
val album: AlbumSimple?, val artists: List<Artist>?,
@SerializedName("available_markets") val availableMarkets: List<Market>?,
@SerializedName("disc_number") val discNumber: Int?,
@SerializedName("duration_ms") val durationMs: Int,
val explicit: Boolean?, val href: String?, val id: String?,
@SerializedName("is_playable") val isPlayable: Boolean?,
val name: String?,
@SerializedName("popularity") val popularity: Int?,
@SerializedName("preview_url") val previewUrl: String?,
@SerializedName("track_number") val trackNumber: Int?,
val type: String?, val uri: String?
) {
val duration: kotlin.time.Duration
get() = durationMs.toLong().milliseconds
}
enum class Market {
AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW,
}

View File

@ -1,14 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.graphics.BitmapFactory
import android.util.Base64
import androidx.glance.ImageProvider
@Suppress("FunctionName")
fun Base64ImageProvider(base64: String): ImageProvider {
var bytes = Base64.decode(base64, Base64.DEFAULT);
var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size);
return ImageProvider(bitmap)
}

View File

@ -1,14 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.content.Context
import android.graphics.BitmapFactory
import androidx.glance.ImageProvider
@Suppress("FunctionName")
fun FlutterAssetImageProvider(context: Context, path: String): ImageProvider {
var inputStream = context.assets.open("flutter_assets/$path")
return ImageProvider(
BitmapFactory.decodeStream(inputStream)
)
}

View File

@ -1,78 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.LocalSize
import androidx.glance.appwidget.cornerRadius
import androidx.glance.layout.Alignment
import androidx.glance.layout.Row
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
import androidx.glance.layout.Spacer
import androidx.glance.layout.size
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import oss.krtirtho.spotube.glance.Breakpoints
import oss.krtirtho.spotube.glance.models.Track
@Composable
fun TrackDetailsView(activeTrack: Track?) {
val context = LocalContext.current
val size = LocalSize.current
val artistStr = activeTrack?.artists?.map { it.name }?.joinToString(", ") ?: "<No Artist>"
val imgLocalPath = activeTrack?.album?.images?.get(0)?.path;
val title = activeTrack?.name ?: "<No Track>"
Image(
provider =
if (imgLocalPath == null)
ImageProvider(
BitmapFactory.decodeResource(
context.resources,
android.R.drawable.ic_delete
)
)
else ImageProvider(BitmapFactory.decodeFile(imgLocalPath)),
contentDescription = "Album Art",
modifier = GlanceModifier.cornerRadius(8.dp)
.size(
if (size.height < 200.dp) 50.dp
else 100.dp
),
contentScale = ContentScale.Fit
)
Spacer(modifier = GlanceModifier.size(6.dp))
Column {
Text(
text = title,
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = GlanceTheme.colors.onBackground
),
)
if (size != Breakpoints.SMALL_SQUARE) {
Spacer(modifier = GlanceModifier.size(6.dp))
Text(
text = artistStr,
style = TextStyle(
fontSize = 14.sp,
color = GlanceTheme.colors.onBackground
),
)
}
}
}

View File

@ -1,77 +0,0 @@
package oss.krtirtho.spotube.glance.widgets
import android.content.SharedPreferences
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.LocalSize
import androidx.glance.appwidget.LinearProgressIndicator
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.size
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import kotlin.math.max
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import oss.krtirtho.spotube.glance.Breakpoints
fun Duration.format(): String {
return this.toComponents { hour, minutes, seconds, nanoseconds ->
var paddedSeconds = seconds.toString().padStart(2, '0')
var paddedMinutes = minutes.toString().padStart(2, '0')
var paddedHour = hour.toString().padStart(2, '0')
if (hour == 0L) {
"$paddedMinutes:$paddedSeconds"
} else {
"$paddedHour:$paddedMinutes:$paddedSeconds"
}
}
}
@Composable
fun TrackProgress(prefs: SharedPreferences) {
val size = LocalSize.current
val position = prefs.getInt("position", 0).seconds
var duration = prefs.getInt("duration", 0).seconds
var progress = position.inWholeSeconds.toFloat() / max(duration.inWholeSeconds.toFloat(), 1.0f)
var textStyle =
TextStyle(
color = GlanceTheme.colors.onBackground,
)
if (size == Breakpoints.HORIZONTAL_RECTANGLE) {
Row(modifier = GlanceModifier.fillMaxWidth()) {
Text(text = position.format(), style = textStyle)
Spacer(modifier = GlanceModifier.size(6.dp))
LinearProgressIndicator(
progress = progress,
modifier = GlanceModifier.defaultWeight(),
color = GlanceTheme.colors.primary,
backgroundColor = GlanceTheme.colors.primaryContainer,
)
Spacer(modifier = GlanceModifier.size(6.dp))
Text(text = duration.format(), style = textStyle)
}
} else {
Column(modifier = GlanceModifier.fillMaxWidth()) {
LinearProgressIndicator(
progress = progress,
modifier = GlanceModifier.fillMaxWidth(),
color = GlanceTheme.colors.primary,
backgroundColor = GlanceTheme.colors.primaryContainer,
)
Spacer(modifier = GlanceModifier.size(6.dp))
Row(modifier = GlanceModifier.fillMaxWidth()) {
Text(text = position.format(), style = textStyle)
Spacer(modifier = GlanceModifier.defaultWeight())
Text(text = duration.format(), style = textStyle)
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -6,7 +6,7 @@
<item> <item>
<bitmap android:gravity="center" android:src="@drawable/splash"/> <bitmap android:gravity="center" android:src="@drawable/splash"/>
</item> </item>
<item android:bottom="0dp"> <item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/> <bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item> </item>
</layer-list> </layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -6,7 +6,7 @@
<item> <item>
<bitmap android:gravity="center" android:src="@drawable/splash"/> <bitmap android:gravity="center" android:src="@drawable/splash"/>
</item> </item>
<item android:bottom="0dp"> <item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/> <bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item> </item>
</layer-list> </layer-list>

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<inset <monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,7 +0,0 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="100dp"
android:minHeight="100dp"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="10000">
</appwidget-provider>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -6,7 +6,7 @@
<item> <item>
<bitmap android:gravity="center" android:src="@drawable/splash"/> <bitmap android:gravity="center" android:src="@drawable/splash"/>
</item> </item>
<item android:bottom="0dp"> <item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/> <bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item> </item>
</layer-list> </layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More