Merge branch 'KRTirtho:master' into master
@ -8,4 +8,7 @@ SPOTIFY_SECRETS=
|
||||
# 0 or 1
|
||||
# 0 = disable
|
||||
# 1 = enable
|
||||
ENABLE_UPDATE_CHECK=
|
||||
ENABLE_UPDATE_CHECK=
|
||||
|
||||
LASTFM_API_KEY=
|
||||
LASTFM_API_SECRET=
|
||||
|
||||
96
.github/workflows/spotube-release-binary.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
inputs:
|
||||
version:
|
||||
description: Version to release (x.x.x)
|
||||
default: 3.1.2
|
||||
default: 3.2.0
|
||||
required: true
|
||||
channel:
|
||||
type: choice
|
||||
@ -87,18 +87,22 @@ jobs:
|
||||
make choco
|
||||
mv dist/spotube.*.nupkg dist/Spotube-windows-x86_64.nupkg
|
||||
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
dist/Spotube-windows-x86_64.nupkg
|
||||
dist/Spotube-windows-x86_64-setup.exe
|
||||
|
||||
- name: Debug With SSH When fails
|
||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Spotube-Release-Binaries
|
||||
path: dist/
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -177,16 +181,23 @@ jobs:
|
||||
mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm
|
||||
mv dist/**/spotube-*-linux.AppImage dist/Spotube-linux-x86_64.AppImage
|
||||
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
dist/Spotube-linux-x86_64.AppImage
|
||||
dist/Spotube-linux-x86_64.deb
|
||||
dist/Spotube-linux-x86_64.rpm
|
||||
dist/spotube-linux-${{ env.BUILD_VERSION }}-x86_64.tar.xz
|
||||
|
||||
- name: Debug With SSH When fails
|
||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Spotube-Release-Binaries
|
||||
path: dist/
|
||||
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
@ -235,10 +246,8 @@ jobs:
|
||||
|
||||
- name: Build Apk
|
||||
run: |
|
||||
flutter build apk
|
||||
flutter build appbundle
|
||||
mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-all-arch.apk
|
||||
mv build/app/outputs/bundle/release/app-release.aab build/Spotube-playstore-all-arch.aab
|
||||
flutter build apk --flavor ${{ inputs.channel }}
|
||||
mv build/app/outputs/flutter-apk/app-${{ inputs.channel }}-release.apk build/Spotube-android-all-arch.apk
|
||||
|
||||
- name: Build Playstore AppBundle
|
||||
run: |
|
||||
@ -247,8 +256,17 @@ jobs:
|
||||
export MANIFEST=android/app/src/main/AndroidManifest.xml
|
||||
xmlstarlet ed -d '//meta-data[@android:name="com.google.android.gms.car.application"]' $MANIFEST > $MANIFEST.tmp
|
||||
mv $MANIFEST.tmp $MANIFEST
|
||||
flutter build appbundle
|
||||
mv build/app/outputs/bundle/release/app-release.aab build/Spotube-playstore-all-arch.aab
|
||||
flutter build appbundle --flavor ${{ inputs.channel }}
|
||||
mv build/app/outputs/bundle/${{ inputs.channel }}Release/app-${{ inputs.channel }}-release.aab build/Spotube-playstore-all-arch.aab
|
||||
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
build/Spotube-android-all-arch.apk
|
||||
build/Spotube-playstore-all-arch.aab
|
||||
|
||||
- name: Debug With SSH When fails
|
||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||
@ -256,14 +274,8 @@ jobs:
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
build/Spotube-android-all-arch.apk
|
||||
build/Spotube-playstore-all-arch.aab
|
||||
|
||||
macos:
|
||||
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -311,38 +323,23 @@ jobs:
|
||||
mkdir -p build/${{ env.BUILD_VERSION }}
|
||||
appdmg appdmg.json build/Spotube-macos-universal.dmg
|
||||
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
build/Spotube-macos-universal.dmg
|
||||
|
||||
- name: Debug With SSH When fails
|
||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
build/Spotube-macos-universal.dmg
|
||||
|
||||
# linux_arm:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - run: |
|
||||
# sudo apt-get update -y
|
||||
# sudo apt-get install -y curl
|
||||
|
||||
# - name: Extract branch name
|
||||
# shell: bash
|
||||
# run: echo "BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV
|
||||
|
||||
# - name: Trigger CircleCI Pipeline
|
||||
# run: |
|
||||
# curl -X POST https://circleci.com/api/v2/project/cci-f9azl/spotube/pipeline \
|
||||
# --header "Circle-Token: ${{secrets.CCI_TOKEN}}" \
|
||||
# --header "content-type: application/json" \
|
||||
# --data '{"branch": "${{env.BRANCH}}", "parameters":{"GHA_Action":"true","version":"${{inputs.version}}","channel":"${{inputs.channel}}","dry_run":${{inputs.dry_run}}}}'
|
||||
|
||||
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- windows
|
||||
- linux
|
||||
@ -366,6 +363,7 @@ jobs:
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
RELEASE.md5sum
|
||||
|
||||
6
.vscode/launch.json
vendored
@ -5,7 +5,11 @@
|
||||
"name": "spotube",
|
||||
"type": "dart",
|
||||
"request": "launch",
|
||||
"program": "lib/main.dart"
|
||||
"program": "lib/main.dart",
|
||||
"args": [
|
||||
"--flavor",
|
||||
"dev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spotube (profile)",
|
||||
|
||||
3
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
||||
"instrumentalness",
|
||||
"Mpris",
|
||||
"riverpod",
|
||||
"Scrobblenaut",
|
||||
"speechiness",
|
||||
"Spotube",
|
||||
"winget"
|
||||
@ -13,7 +14,7 @@
|
||||
"editor.formatOnSave": true,
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies",
|
||||
"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",
|
||||
}
|
||||
}
|
||||
38
CHANGELOG.md
@ -2,6 +2,44 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [3.2.0](https://github.com/KRTirtho/spotube/compare/v3.1.2...v3.2.0) (2023-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ability to select/copy lyrics [#802](https://github.com/KRTirtho/spotube/issues/802) ([0eb9ee8](https://github.com/KRTirtho/spotube/commit/0eb9ee8648bee43a8009e6752674b1be646c0916))
|
||||
* add Amoled theme [#724](https://github.com/KRTirtho/spotube/issues/724) ([5c5dbf6](https://github.com/KRTirtho/spotube/commit/5c5dbf69ecea95c92d3c3900ad690a500d75b4e2))
|
||||
* add audio normalization [#164](https://github.com/KRTirtho/spotube/issues/164) ([da10ab2](https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7))
|
||||
* add restore default settings button ([94c3866](https://github.com/KRTirtho/spotube/commit/94c386638f2e5a42d21c8f157835443333ee6d5c))
|
||||
* configurable audio normalization switch ([c325911](https://github.com/KRTirtho/spotube/commit/c325911c0d87758a203a52df02179c1513bad3fd))
|
||||
* customizable stream/download file formats ([#757](https://github.com/KRTirtho/spotube/issues/757)) ([e54762b](https://github.com/KRTirtho/spotube/commit/e54762be6add6524ab614d103fc3557a101c75f4))
|
||||
* improve and unify the logging framework ([#738](https://github.com/KRTirtho/spotube/issues/738)) ([c7432bb](https://github.com/KRTirtho/spotube/commit/c7432bbd986d576a93957f0a22bdbca5c1e87f20))
|
||||
* LastFM scrobbling support ([#761](https://github.com/KRTirtho/spotube/issues/761)) ([f5bd907](https://github.com/KRTirtho/spotube/commit/f5bd90731d9abc19d684c8bcb231eff399e73023))
|
||||
* loading indicator for genre and personalized pages ([ffe8d9c](https://github.com/KRTirtho/spotube/commit/ffe8d9ca6da25cb3e6fd2c781d5ed3a7b919510e))
|
||||
* manual offline detection ([854ab89](https://github.com/KRTirtho/spotube/commit/854ab8910dffb2837c011d3439173a1f0ebe9c6c))
|
||||
* show error dialog on failed to login ([101c325](https://github.com/KRTirtho/spotube/commit/101c32523d3be8c05527261f6f63f939d388ad79))
|
||||
* sliding up player support ([083319f](https://github.com/KRTirtho/spotube/commit/083319fd2445ab179e3dcda0a6aeaca6f13dda29))
|
||||
* swipe to open player view ([#765](https://github.com/KRTirtho/spotube/issues/765)) ([9aee056](https://github.com/KRTirtho/spotube/commit/9aee0568bf42eed9fea8d517e960a010abf0ebf2))
|
||||
* thicken the scrollbars & make 'em interactive for mobile ([#764](https://github.com/KRTirtho/spotube/issues/764)) ([84a4bcd](https://github.com/KRTirtho/spotube/commit/84a4bcd948ab459489aaf6f39d6954776c3401d7))
|
||||
* **translations:** add Arabic Translations ([#740](https://github.com/KRTirtho/spotube/issues/740)) ([38493f9](https://github.com/KRTirtho/spotube/commit/38493f9dd75303890857a626c0b276ee1ab75bb2))
|
||||
* **translations:** add Farsi Translations ([#760](https://github.com/KRTirtho/spotube/issues/760)) ([fe42cfe](https://github.com/KRTirtho/spotube/commit/fe42cfe8430035d9b67dd158fb7b835ee4071497))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add libmpv1 for ubuntu-based systems ([#739](https://github.com/KRTirtho/spotube/issues/739)) ([5115e04](https://github.com/KRTirtho/spotube/commit/5115e041e78c20fce798a80f1d844bfc60746958))
|
||||
* add xdg-user-dirs as deps ([f3e331e](https://github.com/KRTirtho/spotube/commit/f3e331ecf733995da24c9b907efc5ed4bd02ffdd))
|
||||
* **android :** file_selector getDirectoryPath returns unusable content urls [#720](https://github.com/KRTirtho/spotube/issues/720) ([b3cf639](https://github.com/KRTirtho/spotube/commit/b3cf639ee2f970f4df9b394b260c3ad8a5732a9c))
|
||||
* **android:** audio doesn't resume on interruption end ([15d466a](https://github.com/KRTirtho/spotube/commit/15d466a04538ec70c3a0c132f2baaaf8690f8d4e))
|
||||
* **android:** system navigator back doesn't close player ([20d7092](https://github.com/KRTirtho/spotube/commit/20d70927c909347e84ffa8e456f8fab88d49d179))
|
||||
* get rid of overflow errors & status bar dark color ([5bb8231](https://github.com/KRTirtho/spotube/commit/5bb8231782287faf75c778fadb3a03ac774d14f0))
|
||||
* keyboard shortcuts changing route but not update sidebar ([2d93441](https://github.com/KRTirtho/spotube/commit/2d934411887bd104d8265236df5bf595c5ad2278))
|
||||
* last track repeats ([ed6ca00](https://github.com/KRTirtho/spotube/commit/ed6ca006ce237ed8d509cde9ed47cd6ea3396b63))
|
||||
* minor glitches ([e5d0aaf](https://github.com/KRTirtho/spotube/commit/e5d0aaf80d22b2291b6f7e7c5e18dd99ae1a7a82))
|
||||
* not fetching all followed artists ([#759](https://github.com/KRTirtho/spotube/issues/759)) ([c09a572](https://github.com/KRTirtho/spotube/commit/c09a5729251d8df820442d55477455f78c19c52e))
|
||||
* use audio_service_mpris plugin ([e29cc25](https://github.com/KRTirtho/spotube/commit/e29cc2578cab36729e235b117c1b5489c3452902))
|
||||
* valid non-ASCII characters get removed from downloaded file name [#745](https://github.com/KRTirtho/spotube/issues/745) ([a7e102f](https://github.com/KRTirtho/spotube/commit/a7e102ffc726d00df369560ec9a7f742f9d387bb))
|
||||
|
||||
## [3.1.2](https://github.com/KRTirtho/spotube/compare/v3.1.1...v3.1.2) (2023-09-15)
|
||||
|
||||
|
||||
|
||||
14
README.md
@ -204,6 +204,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds.
|
||||
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. [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. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
|
||||
1. [cupertino_icons](https://pub.dev/packages/cupertino_icons) - Default icons asset for Cupertino widgets based on Apple styled icons
|
||||
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.
|
||||
@ -215,9 +216,6 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
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. [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_selector](https://pub.dev/packages/file_selector) - Flutter plugin for opening and saving files, or selecting directories, using native file selection UI.
|
||||
1. [fl_query](https://fl-query.vercel.app) - Asynchronous data caching, refetching & invalidation library for Flutter
|
||||
1. [fl_query_hooks](https://fl-query.vercel.app) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter
|
||||
1. [fl_query_devtools](https://fl-query.vercel.app) - Devtools support for Fl-Query
|
||||
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_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_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.
|
||||
@ -238,7 +236,6 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
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. [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. [internet_connection_checker](https://github.com/RounakTadvi/internet_connection_checker/tree/main) - A pure Dart library that checks for internet by opening a socket to a list of specified addresses, each with individual port and timeout. Defaults are provided for convenience.
|
||||
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. [introduction_screen](https://github.com/pyozer/introduction_screen) - Introduction/Onboarding package for flutter app with some customizations possibilities
|
||||
1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package.
|
||||
@ -265,11 +262,13 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
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. [titlebar_buttons](https://github.com/gtk-flutter/titlebar_buttons) - A package which provides most of the titlebar buttons from windows, linux and macos.
|
||||
1. [url_launcher](https://pub.dev/packages/url_launcher) - Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.
|
||||
1. [uuid](https://github.com/Daegalus/dart-uuid) - RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart
|
||||
1. [uuid](https://pub.dev/packages/uuid) - RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart
|
||||
1. [version](https://github.com/dartninja/version) - Provides a simple class for parsing and comparing semantic versions as defined by http://semver.org/
|
||||
1. [visibility_detector](https://pub.dev/packages/visibility_detector) - A widget that detects the visibility of its child and notifies a callback.
|
||||
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. [simple_icons](https://jlnrrg.github.io/) - The Simple Icon pack available as Flutter Icons. Provides over 1500 Free SVG icons for popular brands.
|
||||
1. [audio_service_mpris](https://github.com/bdrazhzhov/audio-service-mpris) - audio_service platform interface supporting Media Player Remote Interfacing Specification.
|
||||
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_distributor](https://distributor.leanflutter.org) - A complete tool for packaging and publishing your Flutter apps.
|
||||
@ -280,8 +279,11 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
|
||||
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. [catcher](https://github.com/jhomlala/catcher) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer.
|
||||
1. [fl_query](https://fl-query.vercel.app) - Asynchronous data caching, refetching & invalidation library for Flutter
|
||||
1. [fl_query_hooks](https://fl-query.vercel.app) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter
|
||||
1. [fl_query_devtools](https://fl-query.vercel.app) - Devtools support for Fl-Query
|
||||
1. [flutter_desktop_tools](https://github.com/KRTirtho/flutter_desktop_tools) - Essential collection of tools for flutter desktop app development
|
||||
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. [window_size](https://github.com/google/flutter-desktop-embedding.git) - Allows resizing and repositioning the window containing Flutter.
|
||||
</details>
|
||||
|
||||
|
||||
@ -72,6 +72,28 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "default"
|
||||
|
||||
productFlavors {
|
||||
nightly {
|
||||
dimension "default"
|
||||
resValue "string", "app_name", "Spotube Nightly"
|
||||
applicationIdSuffix ".nightly"
|
||||
versionNameSuffix "-nightly"
|
||||
}
|
||||
dev {
|
||||
dimension "default"
|
||||
resValue "string", "app_name", "Spotube Dev"
|
||||
applicationIdSuffix ".dev"
|
||||
versionNameSuffix "-dev"
|
||||
}
|
||||
stable {
|
||||
dimension "default"
|
||||
resValue "string", "app_name", "Spotube"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flutter {
|
||||
@ -92,4 +114,4 @@ dependencies {
|
||||
|
||||
// other deps so just ignore
|
||||
implementation 'com.android.support:multidex:2.0.1'
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:label="Spotube"
|
||||
android:label="@string/app_name"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true"
|
||||
|
||||
|
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/nightly/res/drawable-hdpi/android12splash.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
android/app/src/nightly/res/drawable-hdpi/branding.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
BIN
android/app/src/nightly/res/drawable-hdpi/splash.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
BIN
android/app/src/nightly/res/drawable-mdpi/android12splash.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
android/app/src/nightly/res/drawable-mdpi/branding.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
BIN
android/app/src/nightly/res/drawable-mdpi/splash.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 213 KiB |
BIN
android/app/src/nightly/res/drawable-v21/background.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/nightly/res/drawable-xhdpi/android12splash.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
android/app/src/nightly/res/drawable-xhdpi/branding.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/nightly/res/drawable-xhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 32 KiB |
BIN
android/app/src/nightly/res/drawable-xxhdpi/android12splash.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
android/app/src/nightly/res/drawable-xxhdpi/branding.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 24 KiB |
BIN
android/app/src/nightly/res/drawable-xxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 20 KiB |
BIN
android/app/src/nightly/res/drawable-xxxhdpi/android12splash.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
android/app/src/nightly/res/drawable-xxxhdpi/branding.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 39 KiB |
BIN
android/app/src/nightly/res/drawable-xxxhdpi/splash.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
android/app/src/nightly/res/drawable/background.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
12
android/app/src/nightly/res/drawable/launch_background.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
android/app/src/nightly/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
android/app/src/nightly/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
android/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
android/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
23
android/app/src/nightly/res/values-night-v31/styles.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:windowSplashScreenBackground">#000000</item>
|
||||
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
||||
<item name="android:windowSplashScreenIconBackgroundColor">#000000</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
22
android/app/src/nightly/res/values-night/styles.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
23
android/app/src/nightly/res/values-v31/styles.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:windowSplashScreenBackground">#000000</item>
|
||||
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
||||
<item name="android:windowSplashScreenIconBackgroundColor">#000000</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
4
android/app/src/nightly/res/values/colors.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#242832</color>
|
||||
</resources>
|
||||
22
android/app/src/nightly/res/values/styles.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
BIN
assets/spotube-hero-banner.png
Normal file
|
After Width: | Height: | Size: 771 KiB |
BIN
assets/spotube-nightly-logo-foreground.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/spotube-nightly-logo.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
359
assets/spotube-nightly-logo.svg
Normal file
@ -0,0 +1,359 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 762 762"
|
||||
version="1.1"
|
||||
id="svg270"
|
||||
sodipodi:docname="spotube-nightly-logo.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="spotube-logo.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
width="762"
|
||||
height="762"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:bx="https://boxy-svg.com"><sodipodi:namedview
|
||||
id="namedview272"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.26940768"
|
||||
inkscape:cx="-413.87091"
|
||||
inkscape:cy="562.34478"
|
||||
inkscape:window-width="1518"
|
||||
inkscape:window-height="1080"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g236"
|
||||
inkscape:lockguides="false"><inkscape:page
|
||||
x="0"
|
||||
y="0"
|
||||
width="762"
|
||||
height="762"
|
||||
id="page3136" /><inkscape:page
|
||||
x="640.44641"
|
||||
y="132.29141"
|
||||
width="89.999939"
|
||||
height="89.999985"
|
||||
id="page3138" /></sodipodi:namedview><defs
|
||||
id="defs220"><linearGradient
|
||||
id="linearGradient1211"><stop
|
||||
offset="0.113"
|
||||
style="stop-color:#ff4b4b;stop-opacity:1;"
|
||||
id="stop1205" /><stop
|
||||
offset="0.60799998"
|
||||
style="stop-color:#d6a400;stop-opacity:1;"
|
||||
id="stop1207" /><stop
|
||||
offset="0.94400001"
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
id="stop1209" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5535"><stop
|
||||
style="stop-color:#3b2100;stop-opacity:1;"
|
||||
offset="0.25885531"
|
||||
id="stop5531" /><stop
|
||||
style="stop-color:#004256;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5533" /></linearGradient><linearGradient
|
||||
id="linearGradient2809"><stop
|
||||
offset="0.113"
|
||||
style="stop-color:#ff4b4b;stop-opacity:1;"
|
||||
id="stop2803" /><stop
|
||||
offset="0.60799998"
|
||||
style="stop-color:#d6a400;stop-opacity:1;"
|
||||
id="stop2805" /><stop
|
||||
offset="0.94400001"
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
id="stop2807" /></linearGradient><linearGradient
|
||||
id="linearGradient938"><stop
|
||||
offset="0.113"
|
||||
style="stop-color:#ff4b4b;stop-opacity:1;"
|
||||
id="stop932" /><stop
|
||||
offset="0.60799998"
|
||||
style="stop-color:#d6a400;stop-opacity:1;"
|
||||
id="stop934" /><stop
|
||||
offset="0.94400001"
|
||||
style="stop-color:#fffcf1;stop-opacity:1;"
|
||||
id="stop936" /></linearGradient><radialGradient
|
||||
id="gradient-2-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="251.179"
|
||||
cy="248.821"
|
||||
r="241.45"
|
||||
gradientTransform="translate(-1.768285,0.589104)"
|
||||
xlink:href="#gradient-2" /><linearGradient
|
||||
id="gradient-2"><stop
|
||||
offset="0.841"
|
||||
style="stop-color: rgb(255, 255, 255);"
|
||||
id="stop169" /><stop
|
||||
offset="1"
|
||||
style="stop-color: rgb(201, 201, 201);"
|
||||
id="stop171" /></linearGradient><filter
|
||||
id="drop-shadow-filter-0"
|
||||
x="-0.050892502"
|
||||
y="-0.050892502"
|
||||
width="1.1017849"
|
||||
height="1.1017849"
|
||||
bx:preset="drop-shadow 1 0 0 10 0.42 rgba(201,201,201,1)"><feGaussianBlur
|
||||
in="SourceAlpha"
|
||||
stdDeviation="10"
|
||||
id="feGaussianBlur174" /><feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
id="feOffset176" /><feComponentTransfer
|
||||
result="offsetblur"
|
||||
id="feComponentTransfer179"><feFuncA
|
||||
id="spread-ctrl"
|
||||
type="linear"
|
||||
slope="0.84" /></feComponentTransfer><feFlood
|
||||
flood-color="rgba(201,201,201,1)"
|
||||
id="feFlood181" /><feComposite
|
||||
in2="offsetblur"
|
||||
operator="in"
|
||||
id="feComposite183" /><feMerge
|
||||
id="feMerge189"><feMergeNode
|
||||
id="feMergeNode185" /><feMergeNode
|
||||
in="SourceGraphic"
|
||||
id="feMergeNode187" /></feMerge></filter><linearGradient
|
||||
id="gradient-4-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="47.146"
|
||||
y1="18.044001"
|
||||
x2="47.146"
|
||||
y2="75.353996"
|
||||
xlink:href="#gradient-4" /><linearGradient
|
||||
id="gradient-4"><stop
|
||||
offset="0.113"
|
||||
style="stop-color: rgb(83, 240, 111);"
|
||||
id="stop193" /><stop
|
||||
offset="0.608"
|
||||
style="stop-color: rgb(0, 177, 86);"
|
||||
id="stop195" /><stop
|
||||
offset="0.944"
|
||||
style="stop-color: rgb(2, 167, 156);"
|
||||
id="stop197" /></linearGradient><filter
|
||||
id="inner-shadow-filter-0"
|
||||
x="-0.064836091"
|
||||
y="-0.071329232"
|
||||
width="1.1296722"
|
||||
height="1.108079"
|
||||
bx:preset="inner-shadow 1 0 0 4 0.5 rgba(0,0,0,0.7)"><feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
id="feOffset200" /><feGaussianBlur
|
||||
stdDeviation="4"
|
||||
id="feGaussianBlur202"
|
||||
result="result1" /><feComposite
|
||||
operator="out"
|
||||
in="SourceGraphic"
|
||||
in2="result1"
|
||||
id="feComposite204" /><feComponentTransfer
|
||||
result="choke"
|
||||
id="feComponentTransfer208"><feFuncA
|
||||
type="linear"
|
||||
slope="1"
|
||||
id="feFuncA206" /></feComponentTransfer><feFlood
|
||||
flood-color="rgba(0,0,0,0.7)"
|
||||
result="color"
|
||||
id="feFlood210" /><feComposite
|
||||
operator="in"
|
||||
in="color"
|
||||
in2="choke"
|
||||
result="shadow"
|
||||
id="feComposite212" /><feComposite
|
||||
operator="over"
|
||||
in="shadow"
|
||||
in2="SourceGraphic"
|
||||
id="feComposite214" /></filter><linearGradient
|
||||
id="gradient-4-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="82.026001"
|
||||
y1="144.832"
|
||||
x2="82.026001"
|
||||
y2="264.46201"
|
||||
xlink:href="#linearGradient2809"
|
||||
gradientTransform="translate(7.2213312)" /><linearGradient
|
||||
id="gradient-4-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="143.69299"
|
||||
y1="22.804001"
|
||||
x2="143.69299"
|
||||
y2="264.582"
|
||||
xlink:href="#linearGradient938" /><linearGradient
|
||||
id="gradient-4-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="205.862"
|
||||
y1="146.28"
|
||||
x2="205.862"
|
||||
y2="265.91"
|
||||
xlink:href="#gradient-4"
|
||||
gradientTransform="translate(-7.2213312)" /><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter2000"
|
||||
x="-0.3425389"
|
||||
y="-0.3425389"
|
||||
width="1.6850778"
|
||||
height="1.6850778"><feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1990" /><feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite1"
|
||||
id="feComposite1992" /><feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="29.980818"
|
||||
result="blur"
|
||||
id="feGaussianBlur1994" /><feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1996" /><feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="atop"
|
||||
result="fbSourceGraphic"
|
||||
id="feComposite1998" /><feColorMatrix
|
||||
result="fbSourceGraphicAlpha"
|
||||
in="fbSourceGraphic"
|
||||
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||
id="feColorMatrix2062" /><feFlood
|
||||
id="feFlood2064"
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
in="fbSourceGraphic" /><feComposite
|
||||
in2="fbSourceGraphic"
|
||||
id="feComposite2066"
|
||||
in="flood"
|
||||
operator="out"
|
||||
result="composite1" /><feGaussianBlur
|
||||
id="feGaussianBlur2068"
|
||||
in="composite1"
|
||||
stdDeviation="28.6433"
|
||||
result="blur" /><feOffset
|
||||
id="feOffset2070"
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset" /><feComposite
|
||||
in2="fbSourceGraphic"
|
||||
id="feComposite2072"
|
||||
in="offset"
|
||||
operator="atop"
|
||||
result="fbSourceGraphic" /><feColorMatrix
|
||||
result="fbSourceGraphicAlpha"
|
||||
in="fbSourceGraphic"
|
||||
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
|
||||
id="feColorMatrix3393" /><feFlood
|
||||
id="feFlood3395"
|
||||
flood-opacity="0.352941"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
in="fbSourceGraphic" /><feComposite
|
||||
in2="fbSourceGraphic"
|
||||
id="feComposite3397"
|
||||
in="flood"
|
||||
operator="in"
|
||||
result="composite1" /><feGaussianBlur
|
||||
id="feGaussianBlur3399"
|
||||
in="composite1"
|
||||
stdDeviation="6.59891"
|
||||
result="blur" /><feOffset
|
||||
id="feOffset3401"
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset" /><feComposite
|
||||
in2="offset"
|
||||
id="feComposite3403"
|
||||
in="fbSourceGraphic"
|
||||
operator="over"
|
||||
result="composite2" /></filter><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1211"
|
||||
id="linearGradient5506"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(117.34662)"
|
||||
x1="82.026001"
|
||||
y1="144.832"
|
||||
x2="82.026001"
|
||||
y2="264.46201" /><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5535"
|
||||
id="radialGradient5537"
|
||||
cx="143.6935"
|
||||
cy="143.69299"
|
||||
fx="143.6935"
|
||||
fy="143.69299"
|
||||
r="152.72653"
|
||||
gradientTransform="matrix(1,0,0,0.8506841,0,21.45565)"
|
||||
gradientUnits="userSpaceOnUse" /></defs><circle
|
||||
style="opacity:1;fill:#242832;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-dasharray:none;stroke-opacity:0.961795;filter:url(#filter2000)"
|
||||
id="path1157"
|
||||
cx="381.48901"
|
||||
cy="381.48901"
|
||||
inkscape:label="path1157"
|
||||
r="235.79112"
|
||||
sodipodi:insensitive="true" /><g
|
||||
transform="matrix(0.319972,0,0,0.323174,379.08153,437.03375)"
|
||||
id="g228"><g
|
||||
style="opacity:1;fill:none;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none"
|
||||
transform="matrix(3.89,0,0,3.89,-175.05,-175.05)"
|
||||
id="g226" /></g><g
|
||||
id="g236"
|
||||
style="fill:none;filter:url(#inner-shadow-filter-0)"
|
||||
transform="matrix(1.107829,0,0,1.106267,221.95533,199.03714)"><path
|
||||
d="m 78.642332,155.437 v 98.42 c 0,5.867 4.741,10.605 10.605,10.605 5.854,0 10.604995,-4.738 10.604995,-10.605 v -98.42 c 0,-5.856 -4.750995,-10.605 -10.604995,-10.605 -5.864,0 -10.605,4.744 -10.605,10.605 z"
|
||||
style="fill:none;fill-opacity:1;stroke:url(#gradient-4-1);stroke-width:9.80924px;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="path230" /><path
|
||||
d="m 29.456,264.582 h 23.351 v -116.85 c 0.064,-0.56 0.166,-1.119 0.166,-1.693 0,-50.412 40.69,-91.42 90.698,-91.42 50.002,0 90.692,41.008 90.692,91.42 0,0.771 0.113,1.518 0.228,2.263 v 116.28 h 23.354 c 16.254,0 29.442,-13.64 29.442,-30.469 v -60.936 c 0,-13.878 -8.989,-25.57 -21.261,-29.249 C 264.997,76.957 210.518,22.804 143.676,22.804 76.816,22.804 22.329,76.962 21.211,143.954 8.956,147.638 0,159.32 0,173.187 v 60.926 c 0,16.819 13.187,30.469 29.456,30.469 z"
|
||||
style="fill:url(#radialGradient5537);fill-opacity:1;stroke:url(#gradient-4-2);stroke-width:18.0661;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="path232" /><path
|
||||
d="M 49.735541,279.35822 C 23.7214,267.48486 38.122112,248.62719 80.85964,237.45225 c 14.400662,-3.49216 25.08508,-5.12184 43.66659,-4.88901 11.61348,0.23282 24.62053,3.49216 24.62053,3.49216 0,-42.13877 -0.46471,-121.7601 -0.46471,-160.872338 4.6454,0 7.89719,-0.232827 14.40071,-0.232827 0,2.328107 0,4.190613 0,6.053093 0,2.095305 0,3.259358 0.46471,4.656212 4.6454,14.66709 11.14893,20.48736 43.66659,38.41381 41.34392,23.04827 53.42195,36.78411 53.42195,55.17616 -0.46471,17.22802 -30.65954,54.01213 -37.16306,52.61528 9.29075,-13.03741 22.2978,-27.00606 25.54958,-38.64661 4.18085,-14.20147 -7.43263,-34.2232 -26.01414,-44.69971 -14.86522,-8.8468 -50.17016,-16.52957 -59.92547,-16.52957 0,0 -0.46472,84.74317 -0.46472,116.87109 0,5.35464 -9.7553,14.89989 -15.32977,18.15925 -25.54958,15.36551 -75.25519,22.34984 -97.553043,12.33896 z"
|
||||
id="path3079"
|
||||
style="stroke-width:3.28861" /><path
|
||||
d="m 188.76763,155.437 v 98.42 c 0,5.867 4.741,10.605 10.60501,10.605 5.854,0 10.605,-4.738 10.605,-10.605 v -98.42 c 0,-5.856 -4.751,-10.605 -10.605,-10.605 -5.86401,0 -10.60501,4.744 -10.60501,10.605 z"
|
||||
style="fill:none;stroke:url(#linearGradient5506);stroke-width:9.80924px;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="path5502" /></g><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g240" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g242" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g244" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g246" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g248" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g250" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g252" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g254" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g256" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g258" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g260" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g262" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g264" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g266" /><g
|
||||
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
|
||||
id="g268" /></svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/spotube-nightly-logo_android12.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
BIN
assets/spotube-tall-capsule.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/spotube-wide-capsule-large.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/spotube-wide-capsule-small.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
@ -10,6 +10,7 @@ pkgbase = spotube-bin
|
||||
depends = libsecret
|
||||
depends = jsoncpp
|
||||
depends = libnotify
|
||||
depends = xdg-user-dirs
|
||||
source = https://github.com/KRTirtho/spotube/releases/download/v2.3.0/Spotube-linux-x86_64.tar.xz
|
||||
md5sums = 8cd6a7385c5c75d203dccd762f1d63ec
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ arch=(x86_64)
|
||||
url="https://github.com/KRTirtho/spotube/"
|
||||
license=('BSD-4-Clause')
|
||||
groups=()
|
||||
depends=('mpv' 'libappindicator-gtk3' 'libsecret' 'jsoncpp' 'libnotify')
|
||||
depends=('mpv' 'libappindicator-gtk3' 'libsecret' 'jsoncpp' 'libnotify' 'xdg-user-dirs')
|
||||
makedepends=()
|
||||
checkdepends=()
|
||||
optdepends=()
|
||||
|
||||
@ -68,6 +68,7 @@ void main() async {
|
||||
),
|
||||
);
|
||||
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
packageInfo
|
||||
.map(
|
||||
@ -76,6 +77,7 @@ void main() async {
|
||||
)
|
||||
.join('\n'),
|
||||
);
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
gitPubspecs.map(
|
||||
(package) {
|
||||
|
||||
@ -35,6 +35,7 @@ void main(List<String> args) {
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
const JsonEncoder.withIndent(' ').convert(
|
||||
args.isNotEmpty ? messagesWithValues[args.first] : messagesWithValues,
|
||||
|
||||
5
flutter_launcher_icons-nightly.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
image_path: "assets/spotube-nightly-logo.png"
|
||||
adaptive_icon_foreground: "assets/spotube-nightly-logo-foreground.jpg"
|
||||
adaptive_icon_background: "#242832"
|
||||
9
flutter_native_splash-nightly.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
flutter_native_splash:
|
||||
background_image: assets/bengali-patterns-bg.jpg
|
||||
image: assets/spotube-nightly-logo.png
|
||||
branding: assets/branding.png
|
||||
android_12:
|
||||
image: assets/spotube-nightly-logo_android12.png
|
||||
branding: assets/branding.png
|
||||
color: "#000000"
|
||||
icon_background_color: "#000000"
|
||||
BIN
ios/Runner/Assets.xcassets/BrandingImageNightly.imageset/BrandingImage.png
vendored
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
ios/Runner/Assets.xcassets/BrandingImageNightly.imageset/BrandingImage@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
ios/Runner/Assets.xcassets/BrandingImageNightly.imageset/BrandingImage@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 32 KiB |
23
ios/Runner/Assets.xcassets/BrandingImageNightly.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "BrandingImage.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "BrandingImage@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "BrandingImage@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
ios/Runner/Assets.xcassets/LaunchBackgroundNightly.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "background.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/Runner/Assets.xcassets/LaunchBackgroundNightly.imageset/background.png
vendored
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
23
ios/Runner/Assets.xcassets/LaunchImageNightly.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "LaunchImage.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ios/Runner/Assets.xcassets/LaunchImageNightly.imageset/LaunchImage.png
vendored
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
ios/Runner/Assets.xcassets/LaunchImageNightly.imageset/LaunchImage@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
ios/Runner/Assets.xcassets/LaunchImageNightly.imageset/LaunchImage@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 96 KiB |
48
ios/Runner/Base.lproj/LaunchScreenNightly.storyboard
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackgroundNightly" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImageNightly" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="BrandingImageNightly" translatesAutoresizingMaskIntoConstraints="NO" id="Uyq-Kz-ftE"/>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="Uyq-Kz-ftE" secondAttribute="bottom" id="8Yb-q4-8bl"/>
|
||||
<constraint firstItem="Uyq-Kz-ftE" firstAttribute="centerX" secondItem="YRO-k0-Ey4" secondAttribute="centerX" id="3kg-TC-cPP"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImageNightly" width="509" height="509"/>
|
||||
<image name="LaunchBackgroundNightly" width="1" height="1"/>
|
||||
<image name="BrandingImageNightly" width="1" height="1"/>
|
||||
</resources>
|
||||
</document>
|
||||
@ -1,64 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Sptube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true />
|
||||
</dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true />
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false />
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Sptube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -36,6 +36,8 @@ class Assets {
|
||||
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
||||
static const AssetGenImage placeholder =
|
||||
AssetGenImage('assets/placeholder.png');
|
||||
static const AssetGenImage spotubeHeroBanner =
|
||||
AssetGenImage('assets/spotube-hero-banner.png');
|
||||
static const AssetGenImage spotubeLogoForeground =
|
||||
AssetGenImage('assets/spotube-logo-foreground.jpg');
|
||||
static const String spotubeLogoIco = 'assets/spotube-logo.ico';
|
||||
@ -44,8 +46,21 @@ class Assets {
|
||||
static const String spotubeLogoSvg = 'assets/spotube-logo.svg';
|
||||
static const AssetGenImage spotubeLogoAndroid12 =
|
||||
AssetGenImage('assets/spotube-logo_android12.png');
|
||||
static const AssetGenImage spotubeNightlyLogoForeground =
|
||||
AssetGenImage('assets/spotube-nightly-logo-foreground.jpg');
|
||||
static const AssetGenImage spotubeNightlyLogoPng =
|
||||
AssetGenImage('assets/spotube-nightly-logo.png');
|
||||
static const String spotubeNightlyLogoSvg = 'assets/spotube-nightly-logo.svg';
|
||||
static const AssetGenImage spotubeNightlyLogoAndroid12 =
|
||||
AssetGenImage('assets/spotube-nightly-logo_android12.png');
|
||||
static const AssetGenImage spotubeScreenshot =
|
||||
AssetGenImage('assets/spotube-screenshot.png');
|
||||
static const AssetGenImage spotubeTallCapsule =
|
||||
AssetGenImage('assets/spotube-tall-capsule.png');
|
||||
static const AssetGenImage spotubeWideCapsuleLarge =
|
||||
AssetGenImage('assets/spotube-wide-capsule-large.png');
|
||||
static const AssetGenImage spotubeWideCapsuleSmall =
|
||||
AssetGenImage('assets/spotube-wide-capsule-small.png');
|
||||
static const AssetGenImage spotubeBanner =
|
||||
AssetGenImage('assets/spotube_banner.png');
|
||||
static const AssetGenImage success = AssetGenImage('assets/success.png');
|
||||
@ -60,12 +75,20 @@ class Assets {
|
||||
branding,
|
||||
emptyBox,
|
||||
placeholder,
|
||||
spotubeHeroBanner,
|
||||
spotubeLogoForeground,
|
||||
spotubeLogoIco,
|
||||
spotubeLogoPng,
|
||||
spotubeLogoSvg,
|
||||
spotubeLogoAndroid12,
|
||||
spotubeNightlyLogoForeground,
|
||||
spotubeNightlyLogoPng,
|
||||
spotubeNightlyLogoSvg,
|
||||
spotubeNightlyLogoAndroid12,
|
||||
spotubeScreenshot,
|
||||
spotubeTallCapsule,
|
||||
spotubeWideCapsuleLarge,
|
||||
spotubeWideCapsuleSmall,
|
||||
spotubeBanner,
|
||||
success,
|
||||
userPlaceholder
|
||||
|
||||
@ -13,6 +13,12 @@ abstract class Env {
|
||||
@EnviedField(varName: 'SPOTIFY_SECRETS')
|
||||
static final String rawSpotifySecrets = _Env.rawSpotifySecrets;
|
||||
|
||||
@EnviedField(varName: 'LASTFM_API_KEY')
|
||||
static final String lastFmApiKey = _Env.lastFmApiKey;
|
||||
|
||||
@EnviedField(varName: 'LASTFM_API_SECRET')
|
||||
static final String lastFmApiSecret = _Env.lastFmApiSecret;
|
||||
|
||||
static final spotifySecrets = rawSpotifySecrets.split(',').map((e) {
|
||||
final secrets = e.trim().split(":").map((e) => e.trim());
|
||||
return {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:spotube/components/player/player_controls.dart';
|
||||
@ -8,7 +9,6 @@ import 'package:spotube/models/logger.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class PlayPauseIntent extends Intent {
|
||||
final WidgetRef ref;
|
||||
@ -115,7 +115,7 @@ class CloseAppAction extends Action<CloseAppIntent> {
|
||||
@override
|
||||
invoke(intent) {
|
||||
if (kIsDesktop) {
|
||||
windowManager.close();
|
||||
DesktopTools.window.close();
|
||||
} else {
|
||||
SystemNavigator.pop();
|
||||
}
|
||||
|
||||
@ -36,10 +36,10 @@ abstract class LanguageLocals {
|
||||
// name: "Amharic",
|
||||
// nativeName: "አማርኛ",
|
||||
// ),
|
||||
// "ar": const ISOLanguageName(
|
||||
// name: "Arabic",
|
||||
// nativeName: "العربية",
|
||||
// ),
|
||||
"ar": const ISOLanguageName(
|
||||
name: "Arabic",
|
||||
nativeName: "العربية",
|
||||
),
|
||||
// "an": const ISOLanguageName(
|
||||
// name: "Aragonese",
|
||||
// nativeName: "Aragonés",
|
||||
@ -508,10 +508,10 @@ abstract class LanguageLocals {
|
||||
// name: "Pāli",
|
||||
// nativeName: "पाऴि",
|
||||
// ),
|
||||
// "fa": const ISOLanguageName(
|
||||
// name: "Persian",
|
||||
// nativeName: "فارسی",
|
||||
// ),
|
||||
"fa": const ISOLanguageName(
|
||||
name: "Persian",
|
||||
nativeName: "فارسی",
|
||||
),
|
||||
"pl": const ISOLanguageName(
|
||||
name: "Polish",
|
||||
nativeName: "polski",
|
||||
@ -684,10 +684,10 @@ abstract class LanguageLocals {
|
||||
// name: "Uighur, Uyghur",
|
||||
// nativeName: "Uyƣurqə, ئۇيغۇرچە",
|
||||
// ),
|
||||
// "uk": const ISOLanguageName(
|
||||
// name: "Ukrainian",
|
||||
// nativeName: "українська",
|
||||
// ),
|
||||
"uk": const ISOLanguageName(
|
||||
name: "Ukrainian",
|
||||
nativeName: "українська",
|
||||
),
|
||||
// "ur": const ISOLanguageName(
|
||||
// name: "Urdu",
|
||||
// nativeName: "اردو",
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import 'package:catcher/catcher.dart';
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:spotify/spotify.dart' hide Search;
|
||||
import 'package:spotube/pages/home/home.dart';
|
||||
import 'package:spotube/pages/lastfm_login/lastfm_login.dart';
|
||||
import 'package:spotube/pages/library/playlist_generate/playlist_generate.dart';
|
||||
import 'package:spotube/pages/lyrics/mini_lyrics.dart';
|
||||
import 'package:spotube/pages/search/search.dart';
|
||||
@ -18,7 +19,6 @@ import 'package:spotube/pages/library/library.dart';
|
||||
import 'package:spotube/pages/desktop_login/login_tutorial.dart';
|
||||
import 'package:spotube/pages/desktop_login/desktop_login.dart';
|
||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||
import 'package:spotube/pages/player/player.dart';
|
||||
import 'package:spotube/pages/playlist/playlist.dart';
|
||||
import 'package:spotube/pages/root/root_app.dart';
|
||||
import 'package:spotube/pages/settings/settings.dart';
|
||||
@ -26,7 +26,7 @@ import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
||||
|
||||
import '../pages/library/playlist_generate/playlist_generate_result.dart';
|
||||
|
||||
final rootNavigatorKey = Catcher.navigatorKey;
|
||||
final rootNavigatorKey = Catcher2.navigatorKey;
|
||||
final shellRouteNavigatorKey = GlobalKey<NavigatorState>();
|
||||
final router = GoRouter(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
@ -147,13 +147,10 @@ final router = GoRouter(
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/player",
|
||||
path: "/lastfm-login",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) {
|
||||
return const SpotubePage(
|
||||
child: PlayerView(),
|
||||
);
|
||||
},
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LastFMLoginPage()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@ -1,187 +1,189 @@
|
||||
// Country Codes contributed by momobobe <https://github.com/momobobe>
|
||||
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
final spotifyMarkets = [
|
||||
("AL", "Albania (AL)"),
|
||||
("DZ", "Algeria (DZ)"),
|
||||
("AD", "Andorra (AD)"),
|
||||
("AO", "Angola (AO)"),
|
||||
("AG", "Antigua and Barbuda (AG)"),
|
||||
("AR", "Argentina (AR)"),
|
||||
("AM", "Armenia (AM)"),
|
||||
("AU", "Australia (AU)"),
|
||||
("AT", "Austria (AT)"),
|
||||
("AZ", "Azerbaijan (AZ)"),
|
||||
("BH", "Bahrain (BH)"),
|
||||
("BD", "Bangladesh (BD)"),
|
||||
("BB", "Barbados (BB)"),
|
||||
("BY", "Belarus (BY)"),
|
||||
("BE", "Belgium (BE)"),
|
||||
("BZ", "Belize (BZ)"),
|
||||
("BJ", "Benin (BJ)"),
|
||||
("BT", "Bhutan (BT)"),
|
||||
("BO", "Bolivia (BO)"),
|
||||
("BA", "Bosnia and Herzegovina (BA)"),
|
||||
("BW", "Botswana (BW)"),
|
||||
("BR", "Brazil (BR)"),
|
||||
("BN", "Brunei Darussalam (BN)"),
|
||||
("BG", "Bulgaria (BG)"),
|
||||
("BF", "Burkina Faso (BF)"),
|
||||
("BI", "Burundi (BI)"),
|
||||
("CV", "Cabo Verde / Cape Verde (CV)"),
|
||||
("KH", "Cambodia (KH)"),
|
||||
("CM", "Cameroon (CM)"),
|
||||
("CA", "Canada (CA)"),
|
||||
("TD", "Chad (TD)"),
|
||||
("CL", "Chile (CL)"),
|
||||
("CO", "Colombia (CO)"),
|
||||
("KM", "Comoros (KM)"),
|
||||
("CR", "Costa Rica (CR)"),
|
||||
("HR", "Croatia (HR)"),
|
||||
("CW", "Curaçao (CW)"),
|
||||
("CY", "Cyprus (CY)"),
|
||||
("CZ", "Czech Republic (CZ)"),
|
||||
("CI", "Ivory Coast (CI)"),
|
||||
("CD", "Congo (CD)"),
|
||||
("DK", "Denmark (DK)"),
|
||||
("DJ", "Djibouti (DJ)"),
|
||||
("DM", "Dominica (DM)"),
|
||||
("DO", "Dominican Republic (DO)"),
|
||||
("EC", "Ecuador (EC)"),
|
||||
("EG", "Egypt (EG)"),
|
||||
("SV", "El Salvador (SV)"),
|
||||
("GQ", "Equatorial Guinea (GQ)"),
|
||||
("EE", "Estonia (EE)"),
|
||||
("SZ", "Eswatini (SZ)"),
|
||||
("FJ", "Fiji (FJ)"),
|
||||
("FI", "Finland (FI)"),
|
||||
("FR", "France (FR)"),
|
||||
("GA", "Gabon (GA)"),
|
||||
("GE", "Georgia (GE)"),
|
||||
("DE", "Germany (DE)"),
|
||||
("GH", "Ghana (GH)"),
|
||||
("GR", "Greece (GR)"),
|
||||
("GD", "Grenada (GD)"),
|
||||
("GT", "Guatemala (GT)"),
|
||||
("GN", "Guinea (GN)"),
|
||||
("GW", "Guinea-Bissau (GW)"),
|
||||
("GY", "Guyana (GY)"),
|
||||
("HT", "Haiti (HT)"),
|
||||
("HN", "Honduras (HN)"),
|
||||
("HK", "Hong Kong (HK)"),
|
||||
("HU", "Hungary (HU)"),
|
||||
("IS", "Iceland (IS)"),
|
||||
("IN", "India (IN)"),
|
||||
("ID", "Indonesia (ID)"),
|
||||
("IQ", "Iraq (IQ)"),
|
||||
("IE", "Ireland (IE)"),
|
||||
("IL", "Israel (IL)"),
|
||||
("IT", "Italy (IT)"),
|
||||
("JM", "Jamaica (JM)"),
|
||||
("JP", "Japan (JP)"),
|
||||
("JO", "Jordan (JO)"),
|
||||
("KZ", "Kazakhstan (KZ)"),
|
||||
("KE", "Kenya (KE)"),
|
||||
("KI", "Kiribati (KI)"),
|
||||
("XK", "Kosovo (XK)"),
|
||||
("KW", "Kuwait (KW)"),
|
||||
("KG", "Kyrgyzstan (KG)"),
|
||||
("LA", "Laos (LA)"),
|
||||
("LV", "Latvia (LV)"),
|
||||
("LB", "Lebanon (LB)"),
|
||||
("LS", "Lesotho (LS)"),
|
||||
("LR", "Liberia (LR)"),
|
||||
("LY", "Libya (LY)"),
|
||||
("LI", "Liechtenstein (LI)"),
|
||||
("LT", "Lithuania (LT)"),
|
||||
("LU", "Luxembourg (LU)"),
|
||||
("MO", "Macao / Macau (MO)"),
|
||||
("MG", "Madagascar (MG)"),
|
||||
("MW", "Malawi (MW)"),
|
||||
("MY", "Malaysia (MY)"),
|
||||
("MV", "Maldives (MV)"),
|
||||
("ML", "Mali (ML)"),
|
||||
("MT", "Malta (MT)"),
|
||||
("MH", "Marshall Islands (MH)"),
|
||||
("MR", "Mauritania (MR)"),
|
||||
("MU", "Mauritius (MU)"),
|
||||
("MX", "Mexico (MX)"),
|
||||
("FM", "Micronesia (FM)"),
|
||||
("MD", "Moldova (MD)"),
|
||||
("MC", "Monaco (MC)"),
|
||||
("MN", "Mongolia (MN)"),
|
||||
("ME", "Montenegro (ME)"),
|
||||
("MA", "Morocco (MA)"),
|
||||
("MZ", "Mozambique (MZ)"),
|
||||
("NA", "Namibia (NA)"),
|
||||
("NR", "Nauru (NR)"),
|
||||
("NP", "Nepal (NP)"),
|
||||
("NL", "Netherlands (NL)"),
|
||||
("NZ", "New Zealand (NZ)"),
|
||||
("NI", "Nicaragua (NI)"),
|
||||
("NE", "Niger (NE)"),
|
||||
("NG", "Nigeria (NG)"),
|
||||
("MK", "North Macedonia (MK)"),
|
||||
("NO", "Norway (NO)"),
|
||||
("OM", "Oman (OM)"),
|
||||
("PK", "Pakistan (PK)"),
|
||||
("PW", "Palau (PW)"),
|
||||
("PS", "Palestine (PS)"),
|
||||
("PA", "Panama (PA)"),
|
||||
("PG", "Papua New Guinea (PG)"),
|
||||
("PY", "Paraguay (PY)"),
|
||||
("PE", "Peru (PE)"),
|
||||
("PH", "Philippines (PH)"),
|
||||
("PL", "Poland (PL)"),
|
||||
("PT", "Portugal (PT)"),
|
||||
("QA", "Qatar (QA)"),
|
||||
("CG", "Congo (CG)"),
|
||||
("RO", "Romania (RO)"),
|
||||
("RU", "Russia (RU)"),
|
||||
("RW", "Rwanda (RW)"),
|
||||
("WS", "Samoa (WS)"),
|
||||
("SM", "San Marino (SM)"),
|
||||
("SA", "Saudi Arabia (SA)"),
|
||||
("SN", "Senegal (SN)"),
|
||||
("RS", "Serbia (RS)"),
|
||||
("SC", "Seychelles (SC)"),
|
||||
("SL", "Sierra Leone (SL)"),
|
||||
("SG", "Singapore (SG)"),
|
||||
("SK", "Slovakia (SK)"),
|
||||
("SI", "Slovenia (SI)"),
|
||||
("SB", "Solomon Islands (SB)"),
|
||||
("ZA", "South Africa (ZA)"),
|
||||
("KR", "South Korea (KR)"),
|
||||
("ES", "Spain (ES)"),
|
||||
("LK", "Sri Lanka (LK)"),
|
||||
("KN", "St. Kitts and Nevis (KN)"),
|
||||
("LC", "St. Lucia (LC)"),
|
||||
("SR", "Suriname (SR)"),
|
||||
("SE", "Sweden (SE)"),
|
||||
("CH", "Switzerland (CH)"),
|
||||
("ST", "São Tomé and Príncipe (ST)"),
|
||||
("TW", "Taiwan (TW)"),
|
||||
("TJ", "Tajikistan (TJ)"),
|
||||
("TZ", "Tanzania (TZ)"),
|
||||
("TH", "Thailand (TH)"),
|
||||
("BS", "The Bahamas (BS)"),
|
||||
("GM", "The Gambia (GM)"),
|
||||
("TL", "East Timor (TL)"),
|
||||
("TG", "Togo (TG)"),
|
||||
("TO", "Tonga (TO)"),
|
||||
("TT", "Trinidad and Tobago (TT)"),
|
||||
("TN", "Tunisia (TN)"),
|
||||
("TR", "Turkey (TR)"),
|
||||
("TV", "Tuvalu (TV)"),
|
||||
("UG", "Uganda (UG)"),
|
||||
("UA", "Ukraine (UA)"),
|
||||
("AE", "United Arab Emirates (AE)"),
|
||||
("GB", "United Kingdom (GB)"),
|
||||
("US", "United States (US)"),
|
||||
("UY", "Uruguay (UY)"),
|
||||
("UZ", "Uzbekistan (UZ)"),
|
||||
("VU", "Vanuatu (VU)"),
|
||||
("VE", "Venezuela (VE)"),
|
||||
("VN", "Vietnam (VN)"),
|
||||
("ZM", "Zambia (ZM)"),
|
||||
("ZW", "Zimbabwe (ZW)"),
|
||||
(Market.AL, "Albania (AL)"),
|
||||
(Market.DZ, "Algeria (DZ)"),
|
||||
(Market.AD, "Andorra (AD)"),
|
||||
(Market.AO, "Angola (AO)"),
|
||||
(Market.AG, "Antigua and Barbuda (AG)"),
|
||||
(Market.AR, "Argentina (AR)"),
|
||||
(Market.AM, "Armenia (AM)"),
|
||||
(Market.AU, "Australia (AU)"),
|
||||
(Market.AT, "Austria (AT)"),
|
||||
(Market.AZ, "Azerbaijan (AZ)"),
|
||||
(Market.BH, "Bahrain (BH)"),
|
||||
(Market.BD, "Bangladesh (BD)"),
|
||||
(Market.BB, "Barbados (BB)"),
|
||||
(Market.BY, "Belarus (BY)"),
|
||||
(Market.BE, "Belgium (BE)"),
|
||||
(Market.BZ, "Belize (BZ)"),
|
||||
(Market.BJ, "Benin (BJ)"),
|
||||
(Market.BT, "Bhutan (BT)"),
|
||||
(Market.BO, "Bolivia (BO)"),
|
||||
(Market.BA, "Bosnia and Herzegovina (BA)"),
|
||||
(Market.BW, "Botswana (BW)"),
|
||||
(Market.BR, "Brazil (BR)"),
|
||||
(Market.BN, "Brunei Darussalam (BN)"),
|
||||
(Market.BG, "Bulgaria (BG)"),
|
||||
(Market.BF, "Burkina Faso (BF)"),
|
||||
(Market.BI, "Burundi (BI)"),
|
||||
(Market.CV, "Cabo Verde / Cape Verde (CV)"),
|
||||
(Market.KH, "Cambodia (KH)"),
|
||||
(Market.CM, "Cameroon (CM)"),
|
||||
(Market.CA, "Canada (CA)"),
|
||||
(Market.TD, "Chad (TD)"),
|
||||
(Market.CL, "Chile (CL)"),
|
||||
(Market.CO, "Colombia (CO)"),
|
||||
(Market.KM, "Comoros (KM)"),
|
||||
(Market.CR, "Costa Rica (CR)"),
|
||||
(Market.HR, "Croatia (HR)"),
|
||||
(Market.CW, "Curaçao (CW)"),
|
||||
(Market.CY, "Cyprus (CY)"),
|
||||
(Market.CZ, "Czech Republic (CZ)"),
|
||||
(Market.CI, "Ivory Coast (CI)"),
|
||||
(Market.CD, "Congo (CD)"),
|
||||
(Market.DK, "Denmark (DK)"),
|
||||
(Market.DJ, "Djibouti (DJ)"),
|
||||
(Market.DM, "Dominica (DM)"),
|
||||
(Market.DO, "Dominican Republic (DO)"),
|
||||
(Market.EC, "Ecuador (EC)"),
|
||||
(Market.EG, "Egypt (EG)"),
|
||||
(Market.SV, "El Salvador (SV)"),
|
||||
(Market.GQ, "Equatorial Guinea (GQ)"),
|
||||
(Market.EE, "Estonia (EE)"),
|
||||
(Market.SZ, "Eswatini (SZ)"),
|
||||
(Market.FJ, "Fiji (FJ)"),
|
||||
(Market.FI, "Finland (FI)"),
|
||||
(Market.FR, "France (FR)"),
|
||||
(Market.GA, "Gabon (GA)"),
|
||||
(Market.GE, "Georgia (GE)"),
|
||||
(Market.DE, "Germany (DE)"),
|
||||
(Market.GH, "Ghana (GH)"),
|
||||
(Market.GR, "Greece (GR)"),
|
||||
(Market.GD, "Grenada (GD)"),
|
||||
(Market.GT, "Guatemala (GT)"),
|
||||
(Market.GN, "Guinea (GN)"),
|
||||
(Market.GW, "Guinea-Bissau (GW)"),
|
||||
(Market.GY, "Guyana (GY)"),
|
||||
(Market.HT, "Haiti (HT)"),
|
||||
(Market.HN, "Honduras (HN)"),
|
||||
(Market.HK, "Hong Kong (HK)"),
|
||||
(Market.HU, "Hungary (HU)"),
|
||||
(Market.IS, "Iceland (IS)"),
|
||||
(Market.IN, "India (IN)"),
|
||||
(Market.ID, "Indonesia (ID)"),
|
||||
(Market.IQ, "Iraq (IQ)"),
|
||||
(Market.IE, "Ireland (IE)"),
|
||||
(Market.IL, "Israel (IL)"),
|
||||
(Market.IT, "Italy (IT)"),
|
||||
(Market.JM, "Jamaica (JM)"),
|
||||
(Market.JP, "Japan (JP)"),
|
||||
(Market.JO, "Jordan (JO)"),
|
||||
(Market.KZ, "Kazakhstan (KZ)"),
|
||||
(Market.KE, "Kenya (KE)"),
|
||||
(Market.KI, "Kiribati (KI)"),
|
||||
(Market.XK, "Kosovo (XK)"),
|
||||
(Market.KW, "Kuwait (KW)"),
|
||||
(Market.KG, "Kyrgyzstan (KG)"),
|
||||
(Market.LA, "Laos (LA)"),
|
||||
(Market.LV, "Latvia (LV)"),
|
||||
(Market.LB, "Lebanon (LB)"),
|
||||
(Market.LS, "Lesotho (LS)"),
|
||||
(Market.LR, "Liberia (LR)"),
|
||||
(Market.LY, "Libya (LY)"),
|
||||
(Market.LI, "Liechtenstein (LI)"),
|
||||
(Market.LT, "Lithuania (LT)"),
|
||||
(Market.LU, "Luxembourg (LU)"),
|
||||
(Market.MO, "Macao / Macau (MO)"),
|
||||
(Market.MG, "Madagascar (MG)"),
|
||||
(Market.MW, "Malawi (MW)"),
|
||||
(Market.MY, "Malaysia (MY)"),
|
||||
(Market.MV, "Maldives (MV)"),
|
||||
(Market.ML, "Mali (ML)"),
|
||||
(Market.MT, "Malta (MT)"),
|
||||
(Market.MH, "Marshall Islands (MH)"),
|
||||
(Market.MR, "Mauritania (MR)"),
|
||||
(Market.MU, "Mauritius (MU)"),
|
||||
(Market.MX, "Mexico (MX)"),
|
||||
(Market.FM, "Micronesia (FM)"),
|
||||
(Market.MD, "Moldova (MD)"),
|
||||
(Market.MC, "Monaco (MC)"),
|
||||
(Market.MN, "Mongolia (MN)"),
|
||||
(Market.ME, "Montenegro (ME)"),
|
||||
(Market.MA, "Morocco (MA)"),
|
||||
(Market.MZ, "Mozambique (MZ)"),
|
||||
(Market.NA, "Namibia (NA)"),
|
||||
(Market.NR, "Nauru (NR)"),
|
||||
(Market.NP, "Nepal (NP)"),
|
||||
(Market.NL, "Netherlands (NL)"),
|
||||
(Market.NZ, "New Zealand (NZ)"),
|
||||
(Market.NI, "Nicaragua (NI)"),
|
||||
(Market.NE, "Niger (NE)"),
|
||||
(Market.NG, "Nigeria (NG)"),
|
||||
(Market.MK, "North Macedonia (MK)"),
|
||||
(Market.NO, "Norway (NO)"),
|
||||
(Market.OM, "Oman (OM)"),
|
||||
(Market.PK, "Pakistan (PK)"),
|
||||
(Market.PW, "Palau (PW)"),
|
||||
(Market.PS, "Palestine (PS)"),
|
||||
(Market.PA, "Panama (PA)"),
|
||||
(Market.PG, "Papua New Guinea (PG)"),
|
||||
(Market.PY, "Paraguay (PY)"),
|
||||
(Market.PE, "Peru (PE)"),
|
||||
(Market.PH, "Philippines (PH)"),
|
||||
(Market.PL, "Poland (PL)"),
|
||||
(Market.PT, "Portugal (PT)"),
|
||||
(Market.QA, "Qatar (QA)"),
|
||||
(Market.CG, "Congo (CG)"),
|
||||
(Market.RO, "Romania (RO)"),
|
||||
(Market.RU, "Russia (RU)"),
|
||||
(Market.RW, "Rwanda (RW)"),
|
||||
(Market.WS, "Samoa (WS)"),
|
||||
(Market.SM, "San Marino (SM)"),
|
||||
(Market.SA, "Saudi Arabia (SA)"),
|
||||
(Market.SN, "Senegal (SN)"),
|
||||
(Market.RS, "Serbia (RS)"),
|
||||
(Market.SC, "Seychelles (SC)"),
|
||||
(Market.SL, "Sierra Leone (SL)"),
|
||||
(Market.SG, "Singapore (SG)"),
|
||||
(Market.SK, "Slovakia (SK)"),
|
||||
(Market.SI, "Slovenia (SI)"),
|
||||
(Market.SB, "Solomon Islands (SB)"),
|
||||
(Market.ZA, "South Africa (ZA)"),
|
||||
(Market.KR, "South Korea (KR)"),
|
||||
(Market.ES, "Spain (ES)"),
|
||||
(Market.LK, "Sri Lanka (LK)"),
|
||||
(Market.KN, "St. Kitts and Nevis (KN)"),
|
||||
(Market.LC, "St. Lucia (LC)"),
|
||||
(Market.SR, "Suriname (SR)"),
|
||||
(Market.SE, "Sweden (SE)"),
|
||||
(Market.CH, "Switzerland (CH)"),
|
||||
(Market.ST, "São Tomé and Príncipe (ST)"),
|
||||
(Market.TW, "Taiwan (TW)"),
|
||||
(Market.TJ, "Tajikistan (TJ)"),
|
||||
(Market.TZ, "Tanzania (TZ)"),
|
||||
(Market.TH, "Thailand (TH)"),
|
||||
(Market.BS, "The Bahamas (BS)"),
|
||||
(Market.GM, "The Gambia (GM)"),
|
||||
(Market.TL, "East Timor (TL)"),
|
||||
(Market.TG, "Togo (TG)"),
|
||||
(Market.TO, "Tonga (TO)"),
|
||||
(Market.TT, "Trinidad and Tobago (TT)"),
|
||||
(Market.TN, "Tunisia (TN)"),
|
||||
(Market.TR, "Turkey (TR)"),
|
||||
(Market.TV, "Tuvalu (TV)"),
|
||||
(Market.UG, "Uganda (UG)"),
|
||||
(Market.UA, "Ukraine (UA)"),
|
||||
(Market.AE, "United Arab Emirates (AE)"),
|
||||
(Market.GB, "United Kingdom (GB)"),
|
||||
(Market.US, "United States (US)"),
|
||||
(Market.UY, "Uruguay (UY)"),
|
||||
(Market.UZ, "Uzbekistan (UZ)"),
|
||||
(Market.VU, "Vanuatu (VU)"),
|
||||
(Market.VE, "Venezuela (VE)"),
|
||||
(Market.VN, "Vietnam (VN)"),
|
||||
(Market.ZM, "Zambia (ZM)"),
|
||||
(Market.ZW, "Zimbabwe (ZW)"),
|
||||
];
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:simple_icons/simple_icons.dart';
|
||||
|
||||
abstract class SpotubeIcons {
|
||||
static const home = FluentIcons.home_12_regular;
|
||||
@ -97,4 +98,12 @@ abstract class SpotubeIcons {
|
||||
static const user = FeatherIcons.user;
|
||||
static const edit = FeatherIcons.edit;
|
||||
static const web = FeatherIcons.globe;
|
||||
static const amoled = FeatherIcons.sunset;
|
||||
static const file = FeatherIcons.file;
|
||||
static const stream = Icons.stream_rounded;
|
||||
static const lastFm = SimpleIcons.lastdotfm;
|
||||
static const spotify = SimpleIcons.spotify;
|
||||
static const eye = FeatherIcons.eye;
|
||||
static const noEye = FeatherIcons.eyeOff;
|
||||
static const normalize = FeatherIcons.barChart2;
|
||||
}
|
||||
|
||||
@ -11,24 +11,7 @@ import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/utils/service_utils.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
|
||||
enum AlbumType {
|
||||
album,
|
||||
single,
|
||||
compilation;
|
||||
|
||||
factory AlbumType.from(String? type) {
|
||||
switch (type) {
|
||||
case "album":
|
||||
return AlbumType.album;
|
||||
case "single":
|
||||
return AlbumType.single;
|
||||
case "compilation":
|
||||
return AlbumType.compilation;
|
||||
default:
|
||||
return AlbumType.album;
|
||||
}
|
||||
}
|
||||
|
||||
extension FormattedAlbumType on AlbumType {
|
||||
String get formatted => name.replaceFirst(name[0], name[0].toUpperCase());
|
||||
}
|
||||
|
||||
@ -71,7 +54,7 @@ class AlbumCard extends HookConsumerWidget {
|
||||
isLoading: isPlaylistPlaying && playlist.isFetching == true,
|
||||
title: album.name!,
|
||||
description:
|
||||
"${AlbumType.from(album.albumType!).formatted} • ${TypeConversionUtils.artists_X_String<ArtistSimple>(album.artists ?? [])}",
|
||||
"${album.albumType?.formatted} • ${TypeConversionUtils.artists_X_String<ArtistSimple>(album.artists ?? [])}",
|
||||
onTap: () {
|
||||
ServiceUtils.push(context, "/album/${album.id}", extra: album);
|
||||
},
|
||||
|
||||
@ -20,6 +20,8 @@ class TokenLoginForm extends HookConsumerWidget {
|
||||
final keyCodeController = useTextEditingController();
|
||||
final mounted = useIsMounted();
|
||||
|
||||
final isLoading = useState(false);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 400,
|
||||
@ -45,27 +47,35 @@ class TokenLoginForm extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
if (keyCodeController.text.isEmpty ||
|
||||
directCodeController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.fill_in_all_fields),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final cookieHeader =
|
||||
"sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}";
|
||||
onPressed: isLoading.value
|
||||
? null
|
||||
: () async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
if (keyCodeController.text.isEmpty ||
|
||||
directCodeController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.fill_in_all_fields),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final cookieHeader =
|
||||
"sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}";
|
||||
|
||||
authenticationNotifier.setCredentials(
|
||||
await AuthenticationCredentials.fromCookie(cookieHeader),
|
||||
);
|
||||
if (mounted()) {
|
||||
onDone?.call();
|
||||
}
|
||||
},
|
||||
authenticationNotifier.setCredentials(
|
||||
await AuthenticationCredentials.fromCookie(
|
||||
cookieHeader),
|
||||
);
|
||||
if (mounted()) {
|
||||
onDone?.call();
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
},
|
||||
child: Text(context.l10n.submit),
|
||||
)
|
||||
],
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:spotify/spotify.dart';
|
||||
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/album/album_card.dart';
|
||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||
import 'package:spotube/components/shared/waypoint.dart';
|
||||
@ -70,39 +71,42 @@ class UserAlbums extends HookConsumerWidget {
|
||||
child: SearchBar(
|
||||
onChanged: (value) => searchText.value = value,
|
||||
leading: const Icon(SpotubeIcons.filter),
|
||||
hintText: context.l10n.filter_artist,
|
||||
hintText: context.l10n.filter_albums,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SizedBox.expand(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: InterScrollbar(
|
||||
controller: controller,
|
||||
child: Wrap(
|
||||
runSpacing: 20,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
if (albums.isEmpty)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const ShimmerPlaybuttonCard(count: 4),
|
||||
),
|
||||
for (final album in albums)
|
||||
AlbumCard(
|
||||
TypeConversionUtils.simpleAlbum_X_Album(album),
|
||||
),
|
||||
if (albumsQuery.hasNextPage)
|
||||
Waypoint(
|
||||
controller: controller,
|
||||
isGrid: true,
|
||||
onTouchEdge: albumsQuery.fetchNext,
|
||||
child: const ShimmerPlaybuttonCard(count: 1),
|
||||
)
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
controller: controller,
|
||||
child: Wrap(
|
||||
runSpacing: 20,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
if (albums.isEmpty)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const ShimmerPlaybuttonCard(count: 4),
|
||||
),
|
||||
for (final album in albums)
|
||||
AlbumCard(
|
||||
TypeConversionUtils.simpleAlbum_X_Album(album),
|
||||
),
|
||||
if (albumsQuery.hasNextPage)
|
||||
Waypoint(
|
||||
controller: controller,
|
||||
isGrid: true,
|
||||
onTouchEdge: albumsQuery.fetchNext,
|
||||
child: const ShimmerPlaybuttonCard(count: 1),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||
import 'package:spotube/components/artist/artist_card.dart';
|
||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
@ -78,18 +79,21 @@ class UserArtists extends HookConsumerWidget {
|
||||
onRefresh: () async {
|
||||
await artistQuery.refresh();
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
child: InterScrollbar(
|
||||
controller: controller,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Wrap(
|
||||
spacing: 15,
|
||||
runSpacing: 5,
|
||||
children: filteredArtists
|
||||
.mapIndexed((index, artist) => ArtistCard(artist))
|
||||
.toList(),
|
||||
child: SingleChildScrollView(
|
||||
controller: controller,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Wrap(
|
||||
spacing: 15,
|
||||
runSpacing: 5,
|
||||
children: filteredArtists
|
||||
.mapIndexed((index, artist) => ArtistCard(artist))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:catcher/catcher.dart';
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -17,6 +17,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/expandable_search/expandable_search.dart';
|
||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
|
||||
import 'package:spotube/components/shared/sort_tracks_dropdown.dart';
|
||||
import 'package:spotube/components/shared/track_table/track_tile.dart';
|
||||
@ -28,7 +29,8 @@ import 'package:spotube/provider/user_preferences_provider.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:spotube/utils/service_utils.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FfiException;
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'
|
||||
show FfiException;
|
||||
|
||||
const supportedAudioTypes = [
|
||||
"audio/webm",
|
||||
@ -76,14 +78,14 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
|
||||
final mimetype = lookupMimeType(file.path);
|
||||
return mimetype != null && supportedAudioTypes.contains(mimetype);
|
||||
}).map(
|
||||
(f) async {
|
||||
(file) async {
|
||||
try {
|
||||
final metadata = await MetadataGod.readMetadata(file: f.path);
|
||||
final metadata = await MetadataGod.readMetadata(file: file.path);
|
||||
|
||||
final imageFile = File(join(
|
||||
(await getTemporaryDirectory()).path,
|
||||
"spotube",
|
||||
basenameWithoutExtension(f.path) +
|
||||
basenameWithoutExtension(file.path) +
|
||||
imgMimeToExt[metadata.picture?.mimeType ?? "image/jpeg"]!,
|
||||
));
|
||||
if (!await imageFile.exists() && metadata.picture != null) {
|
||||
@ -94,12 +96,12 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
|
||||
);
|
||||
}
|
||||
|
||||
return {"metadata": metadata, "file": f, "art": imageFile.path};
|
||||
return {"metadata": metadata, "file": file, "art": imageFile.path};
|
||||
} catch (e, stack) {
|
||||
if (e is FfiException) {
|
||||
return {"file": f};
|
||||
return {"file": file};
|
||||
}
|
||||
Catcher.reportCheckedError(e, stack);
|
||||
Catcher2.reportCheckedError(e, stack);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
@ -123,7 +125,7 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
|
||||
|
||||
return tracks;
|
||||
} catch (e, stack) {
|
||||
Catcher.reportCheckedError(e, stack);
|
||||
Catcher2.reportCheckedError(e, stack);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
@ -286,24 +288,26 @@ class UserLocalTracks extends HookConsumerWidget {
|
||||
onRefresh: () async {
|
||||
ref.refresh(localTracksProvider);
|
||||
},
|
||||
child: ListView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemCount: filteredTracks.length,
|
||||
itemBuilder: (context, index) {
|
||||
final track = filteredTracks[index];
|
||||
return TrackTile(
|
||||
index: index,
|
||||
track: track,
|
||||
userPlaylist: false,
|
||||
onTap: () async {
|
||||
await playLocalTracks(
|
||||
ref,
|
||||
sortedTracks,
|
||||
currentTrack: track,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: InterScrollbar(
|
||||
child: ListView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemCount: filteredTracks.length,
|
||||
itemBuilder: (context, index) {
|
||||
final track = filteredTracks[index];
|
||||
return TrackTile(
|
||||
index: index,
|
||||
track: track,
|
||||
userPlaylist: false,
|
||||
onTap: () async {
|
||||
await playLocalTracks(
|
||||
ref,
|
||||
sortedTracks,
|
||||
currentTrack: track,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||
@ -79,59 +80,63 @@ class UserPlaylists extends HookConsumerWidget {
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: playlistsQuery.refresh,
|
||||
child: SingleChildScrollView(
|
||||
child: InterScrollbar(
|
||||
controller: controller,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Waypoint(
|
||||
child: SingleChildScrollView(
|
||||
controller: controller,
|
||||
onTouchEdge: () {
|
||||
if (playlistsQuery.hasNextPage) {
|
||||
playlistsQuery.fetchNext();
|
||||
}
|
||||
},
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: SearchBar(
|
||||
onChanged: (value) => searchText.value = value,
|
||||
hintText: context.l10n.filter_playlists,
|
||||
leading: const Icon(SpotubeIcons.filter),
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Waypoint(
|
||||
controller: controller,
|
||||
onTouchEdge: () {
|
||||
if (playlistsQuery.hasNextPage) {
|
||||
playlistsQuery.fetchNext();
|
||||
}
|
||||
},
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: SearchBar(
|
||||
onChanged: (value) => searchText.value = value,
|
||||
hintText: context.l10n.filter_playlists,
|
||||
leading: const Icon(SpotubeIcons.filter),
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedCrossFade(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
crossFadeState: playlistsQuery.isLoadingPage ||
|
||||
!playlistsQuery.hasPageData
|
||||
? CrossFadeState.showFirst
|
||||
: CrossFadeState.showSecond,
|
||||
firstChild:
|
||||
const Center(child: ShimmerPlaybuttonCard(count: 7)),
|
||||
secondChild: Wrap(
|
||||
runSpacing: 10,
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
const PlaylistCreateDialogButton(),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(SpotubeIcons.magic),
|
||||
label: Text(context.l10n.generate_playlist),
|
||||
onPressed: () {
|
||||
GoRouter.of(context).push("/library/generate");
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
...playlists.map((playlist) => PlaylistCard(playlist))
|
||||
],
|
||||
AnimatedCrossFade(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
crossFadeState: !playlistsQuery.hasPageData &&
|
||||
!playlistsQuery.hasPageError &&
|
||||
!playlistsQuery.isLoadingNextPage
|
||||
? CrossFadeState.showFirst
|
||||
: CrossFadeState.showSecond,
|
||||
firstChild:
|
||||
const Center(child: ShimmerPlaybuttonCard(count: 7)),
|
||||
secondChild: Wrap(
|
||||
runSpacing: 10,
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
const PlaylistCreateDialogButton(),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(SpotubeIcons.magic),
|
||||
label: Text(context.l10n.generate_playlist),
|
||||
onPressed: () {
|
||||
GoRouter.of(context).push("/library/generate");
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
...playlists.map((playlist) => PlaylistCard(playlist))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -15,6 +15,7 @@ import 'package:spotube/components/shared/animated_gradient.dart';
|
||||
import 'package:spotube/components/shared/dialogs/track_details_dialog.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/components/shared/panels/sliding_up_panel.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
||||
@ -26,8 +27,10 @@ import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
|
||||
class PlayerView extends HookConsumerWidget {
|
||||
final PanelController panelController;
|
||||
const PlayerView({
|
||||
Key? key,
|
||||
required this.panelController,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -45,7 +48,7 @@ class PlayerView extends HookConsumerWidget {
|
||||
useEffect(() {
|
||||
if (mediaQuery.lgAndUp) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
GoRouter.of(context).pop();
|
||||
panelController.close();
|
||||
});
|
||||
}
|
||||
return null;
|
||||
@ -60,64 +63,96 @@ class PlayerView extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
final palette = usePaletteGenerator(albumArt);
|
||||
final bgColor = palette.dominantColor?.color ?? theme.colorScheme.primary;
|
||||
final titleTextColor = palette.dominantColor?.titleTextColor;
|
||||
final bodyTextColor = palette.dominantColor?.bodyTextColor;
|
||||
|
||||
final bgColor = palette.dominantColor?.color ?? theme.colorScheme.primary;
|
||||
|
||||
final GlobalKey<ScaffoldState> scaffoldKey =
|
||||
useMemoized(() => GlobalKey(), []);
|
||||
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.renderView.automaticSystemUiAdjustment = false;
|
||||
|
||||
return () {
|
||||
WidgetsBinding.instance.renderView.automaticSystemUiAdjustment = true;
|
||||
};
|
||||
}, [panelController.isPanelOpen]);
|
||||
|
||||
useCustomStatusBarColor(
|
||||
bgColor,
|
||||
GoRouterState.of(context).matchedLocation == "/player",
|
||||
panelController.isPanelOpen,
|
||||
noSetBGColor: true,
|
||||
automaticSystemUiAdjustment: false,
|
||||
);
|
||||
|
||||
return IconTheme(
|
||||
data: theme.iconTheme.copyWith(color: bodyTextColor),
|
||||
child: Scaffold(
|
||||
appBar: PageWindowTitleBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: titleTextColor,
|
||||
toolbarOpacity: 1,
|
||||
leading: const BackButton(),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(SpotubeIcons.info, size: 18),
|
||||
tooltip: context.l10n.details,
|
||||
style: IconButton.styleFrom(foregroundColor: bodyTextColor),
|
||||
onPressed: currentTrack == null
|
||||
? null
|
||||
: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return TrackDetailsDialog(
|
||||
track: currentTrack,
|
||||
);
|
||||
});
|
||||
},
|
||||
)
|
||||
final topPadding = MediaQueryData.fromView(View.of(context)).padding.top;
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
panelController.close();
|
||||
return false;
|
||||
},
|
||||
child: IconTheme(
|
||||
data: theme.iconTheme.copyWith(color: bodyTextColor),
|
||||
child: AnimateGradient(
|
||||
animateAlignments: true,
|
||||
primaryBegin: Alignment.topLeft,
|
||||
primaryEnd: Alignment.bottomLeft,
|
||||
secondaryBegin: Alignment.bottomRight,
|
||||
secondaryEnd: Alignment.topRight,
|
||||
duration: const Duration(seconds: 15),
|
||||
primaryColors: [
|
||||
palette.dominantColor?.color ?? theme.colorScheme.primary,
|
||||
palette.mutedColor?.color ?? theme.colorScheme.secondary,
|
||||
],
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: SizedBox(
|
||||
height: double.infinity,
|
||||
child: AnimateGradient(
|
||||
animateAlignments: true,
|
||||
primaryBegin: Alignment.topLeft,
|
||||
primaryEnd: Alignment.bottomLeft,
|
||||
secondaryBegin: Alignment.bottomRight,
|
||||
secondaryEnd: Alignment.topRight,
|
||||
duration: const Duration(seconds: 15),
|
||||
primaryColors: [
|
||||
palette.dominantColor?.color ?? theme.colorScheme.primary,
|
||||
palette.mutedColor?.color ?? theme.colorScheme.secondary,
|
||||
],
|
||||
secondaryColors: [
|
||||
(palette.darkVibrantColor ?? palette.lightVibrantColor)?.color ??
|
||||
theme.colorScheme.primaryContainer,
|
||||
(palette.darkMutedColor ?? palette.lightMutedColor)?.color ??
|
||||
theme.colorScheme.secondaryContainer,
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
secondaryColors: [
|
||||
(palette.darkVibrantColor ?? palette.lightVibrantColor)?.color ??
|
||||
theme.colorScheme.primaryContainer,
|
||||
(palette.darkMutedColor ?? palette.lightMutedColor)?.color ??
|
||||
theme.colorScheme.secondaryContainer,
|
||||
],
|
||||
child: Scaffold(
|
||||
key: scaffoldKey,
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: Size.fromHeight(
|
||||
kToolbarHeight + topPadding,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: topPadding),
|
||||
child: PageWindowTitleBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: titleTextColor,
|
||||
toolbarOpacity: 1,
|
||||
leading: IconButton(
|
||||
icon: const Icon(SpotubeIcons.angleDown, size: 18),
|
||||
onPressed: panelController.close,
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(SpotubeIcons.info, size: 18),
|
||||
tooltip: context.l10n.details,
|
||||
style:
|
||||
IconButton.styleFrom(foregroundColor: bodyTextColor),
|
||||
onPressed: currentTrack == null
|
||||
? null
|
||||
: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return TrackDetailsDialog(
|
||||
track: currentTrack,
|
||||
);
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: SingleChildScrollView(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
@ -190,7 +225,7 @@ class PlayerView extends HookConsumerWidget {
|
||||
color: bodyTextColor,
|
||||
),
|
||||
onRouteChange: (route) {
|
||||
GoRouter.of(context).pop();
|
||||
panelController.close();
|
||||
GoRouter.of(context).push(route);
|
||||
},
|
||||
),
|
||||
@ -13,7 +13,6 @@ import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/extensions/duration.dart';
|
||||
import 'package:spotube/models/local_track.dart';
|
||||
import 'package:spotube/models/logger.dart';
|
||||
import 'package:spotube/models/spotube_track.dart';
|
||||
import 'package:spotube/provider/download_manager_provider.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
|
||||
@ -2,16 +2,17 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/player/player_track_details.dart';
|
||||
import 'package:spotube/components/root/spotube_navigation_bar.dart';
|
||||
import 'package:spotube/components/shared/panels/sliding_up_panel.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/collections/intents.dart';
|
||||
import 'package:spotube/hooks/use_progress.dart';
|
||||
import 'package:spotube/components/player/player.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/utils/service_utils.dart';
|
||||
|
||||
class PlayerOverlay extends HookConsumerWidget {
|
||||
final String albumArt;
|
||||
@ -39,22 +40,32 @@ class PlayerOverlay extends HookConsumerWidget {
|
||||
topRight: Radius.circular(10),
|
||||
);
|
||||
|
||||
return GestureDetector(
|
||||
onVerticalDragEnd: (details) {
|
||||
int sensitivity = 8;
|
||||
if (details.primaryVelocity != null &&
|
||||
details.primaryVelocity! < -sensitivity) {
|
||||
ServiceUtils.push(context, "/player");
|
||||
}
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
final panelController = useMemoized(() => PanelController(), []);
|
||||
|
||||
useEffect(() {
|
||||
return () {
|
||||
panelController.dispose();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return SlidingUpPanel(
|
||||
maxHeight: mediaQuery.size.height,
|
||||
backdropEnabled: false,
|
||||
minHeight: canShow ? 53 : 0,
|
||||
onPanelSlide: (position) {
|
||||
final invertedPosition = 1 - position;
|
||||
ref.read(navigationPanelHeight.notifier).state = 50 * invertedPosition;
|
||||
},
|
||||
child: ClipRRect(
|
||||
controller: panelController,
|
||||
collapsed: ClipRRect(
|
||||
borderRadius: radius,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: canShow ? 53 : 0,
|
||||
width: mediaQuery.size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer.withOpacity(.8),
|
||||
borderRadius: radius,
|
||||
@ -95,18 +106,16 @@ class PlayerOverlay extends HookConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () =>
|
||||
GoRouter.of(context).push("/player"),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
child: PlayerTrackDetails(
|
||||
albumArt: albumArt,
|
||||
color: textColor,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
panelController.open();
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
child: PlayerTrackDetails(
|
||||
albumArt: albumArt,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -165,6 +174,26 @@ class PlayerOverlay extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
panelBuilder: (position) {
|
||||
// this is the reason we're getting an update
|
||||
final navigationHeight = ref.watch(navigationPanelHeight);
|
||||
|
||||
if (navigationHeight == 50) return const SizedBox();
|
||||
|
||||
return IgnorePointer(
|
||||
ignoring: !panelController.isPanelOpen,
|
||||
child: AnimatedContainer(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
decoration: navigationHeight == 0
|
||||
? const BoxDecoration(borderRadius: BorderRadius.zero)
|
||||
: const BoxDecoration(borderRadius: radius),
|
||||
child: HorizontalScrollableWidget(
|
||||
child: PlayerView(panelController: panelController),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||