changed lots of different stuff i don't remember
pre-running dart fix
|
@ -0,0 +1,3 @@
|
|||
[submodule "dart_blowfish"]
|
||||
path = dart_blowfish
|
||||
url = https://github.com/titoo-dev/dart_blowfish
|
55
.metadata
|
@ -1,10 +1,45 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: android
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: ios
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: linux
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: macos
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: web
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
- platform: windows
|
||||
create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>app</name>
|
||||
<comment>Project app created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1690499516319</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
|
@ -32,6 +32,7 @@ apply plugin: 'com.android.application'
|
|||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
namespace 'f.f.freezer'
|
||||
compileSdkVersion 33
|
||||
|
||||
lintOptions {
|
||||
|
@ -42,7 +43,7 @@ android {
|
|||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "f.f.freezer"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
@ -58,13 +59,13 @@ android {
|
|||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
// TODO: Put back signingConfig.release
|
||||
signingConfig signingConfigs.debug
|
||||
shrinkResources false
|
||||
minifyEnabled true
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
shrinkResources false
|
||||
minifyEnabled false
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ android {
|
|||
dependencies {
|
||||
//implementation group: 'org', name: 'jaudiotagger', version: '2.0.3'
|
||||
implementation files('libs/jaudiotagger-2.2.3.jar')
|
||||
implementation files('libs/extension-flac.aar')
|
||||
// implementation files('libs/extension-flac.aar')
|
||||
implementation group: 'org.nanohttpd', name: 'nanohttpd', version: '2.3.1'
|
||||
implementation group: 'androidx.core', name: 'core', version: '1.6.0'
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.software.LEANBACK"
|
||||
|
@ -117,23 +116,24 @@
|
|||
|
||||
<service
|
||||
android:name="com.ryanheise.audioservice.AudioService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true"
|
||||
tools:ignore="Instantiatable"
|
||||
>
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.media.browse.MediaBrowserService"
|
||||
/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
||||
android:exported="true"
|
||||
tools:ignore="Instantiatable"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application"
|
||||
|
|
|
@ -80,8 +80,8 @@ public class Download {
|
|||
}
|
||||
|
||||
//Used to send data to Flutter
|
||||
HashMap toHashMap() {
|
||||
HashMap map = new HashMap();
|
||||
HashMap<String, Object> toHashMap() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("id", id);
|
||||
map.put("path", path);
|
||||
map.put("private", priv);
|
||||
|
|
|
@ -90,7 +90,7 @@ public class MainActivity extends AudioServiceActivity {
|
|||
//TX
|
||||
db.beginTransaction();
|
||||
|
||||
ArrayList<HashMap> downloads = call.arguments();
|
||||
ArrayList<HashMap<String, Object>> downloads = call.arguments();
|
||||
for (int i=0; i<downloads.size(); i++) {
|
||||
//Check if exists
|
||||
Cursor cursor = db.rawQuery("SELECT id, state, quality FROM Downloads WHERE trackId == ? AND path == ?",
|
||||
|
@ -128,7 +128,7 @@ public class MainActivity extends AudioServiceActivity {
|
|||
//Get all downloads from DB
|
||||
if (call.method.equals("getDownloads")) {
|
||||
Cursor cursor = db.query("Downloads", null, null, null, null, null, null);
|
||||
ArrayList downloads = new ArrayList();
|
||||
ArrayList<HashMap<String, Object>> downloads = new ArrayList<>();
|
||||
//Parse downloads
|
||||
while (cursor.moveToNext()) {
|
||||
Download download = Download.fromSQL(cursor);
|
||||
|
@ -258,6 +258,8 @@ public class MainActivity extends AudioServiceActivity {
|
|||
eventSink = null;
|
||||
}
|
||||
}));
|
||||
|
||||
super.configureFlutterEngine(flutterEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -280,6 +282,7 @@ public class MainActivity extends AudioServiceActivity {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
|
@ -305,26 +308,28 @@ public class MainActivity extends AudioServiceActivity {
|
|||
DownloadsDatabase dbHelper = new DownloadsDatabase(getApplicationContext());
|
||||
db = dbHelper.getWritableDatabase();
|
||||
|
||||
//Trust all SSL Certs - Credits to Kilowatt36
|
||||
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||
new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}
|
||||
};
|
||||
SSLContext sc;
|
||||
try {
|
||||
sc = SSLContext.getInstance("SSL");
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
Log.e(this.getLocalClassName(), e.getMessage());
|
||||
}
|
||||
|
||||
// what the fuck?
|
||||
// //Trust all SSL Certs - Credits to Kilowatt36
|
||||
// TrustManager[] trustAllCerts = new TrustManager[]{
|
||||
// new X509TrustManager() {
|
||||
// public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
// return null;
|
||||
// }
|
||||
// public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||
// }
|
||||
// public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// SSLContext sc;
|
||||
// try {
|
||||
// sc = SSLContext.getInstance("SSL");
|
||||
// sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||
// } catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
// Log.e(this.getLocalClassName(), e.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -395,10 +400,10 @@ public class MainActivity extends AudioServiceActivity {
|
|||
if (eventSink == null) break;
|
||||
if (msg.getData().getParcelableArrayList("downloads").size() > 0) {
|
||||
//Generate HashMap ArrayList for sending to flutter
|
||||
ArrayList<HashMap> data = new ArrayList<>();
|
||||
ArrayList<HashMap<String, Number>> data = new ArrayList<>();
|
||||
for (int i=0; i<msg.getData().getParcelableArrayList("downloads").size(); i++) {
|
||||
Bundle bundle = (Bundle) msg.getData().getParcelableArrayList("downloads").get(i);
|
||||
HashMap out = new HashMap();
|
||||
HashMap<String, Number> out = new HashMap<>();
|
||||
out.put("id", bundle.getInt("id"));
|
||||
out.put("state", bundle.getInt("state"));
|
||||
out.put("received", bundle.getLong("received"));
|
||||
|
@ -407,7 +412,7 @@ public class MainActivity extends AudioServiceActivity {
|
|||
data.add(out);
|
||||
}
|
||||
//Wrapper
|
||||
HashMap out = new HashMap();
|
||||
HashMap<String, Object> out = new HashMap<>();
|
||||
out.put("action", "onProgress");
|
||||
out.put("data", data);
|
||||
eventSink.success(out);
|
||||
|
@ -418,7 +423,7 @@ public class MainActivity extends AudioServiceActivity {
|
|||
case DownloadService.SERVICE_ON_STATE_CHANGE:
|
||||
if (eventSink == null) break;
|
||||
Bundle b = msg.getData();
|
||||
HashMap out = new HashMap();
|
||||
HashMap<String, Object> out = new HashMap<>();
|
||||
out.put("running", b.getBoolean("running"));
|
||||
out.put("queueSize", b.getInt("queueSize"));
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public class StreamServer {
|
|||
public HashMap<String, StreamInfo> streams = new HashMap<>();
|
||||
|
||||
private WebServer server;
|
||||
private String host = "0.0.0.0";
|
||||
private String host = "127.0.0.1";
|
||||
private int port = 36958;
|
||||
private String offlinePath;
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package f.f.freezer
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
#android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableDexingArtifactTransform=false
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c874b07df26b7be681f5241773f4305cc40b2974
|
|
@ -1,32 +1,34 @@
|
|||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
**/*sync/
|
||||
.sconsign.dblite
|
||||
.tags*
|
||||
**/.vagrant/
|
||||
**/DerivedData/
|
||||
Icon?
|
||||
**/Pods/
|
||||
**/.symlinks/
|
||||
profile
|
||||
xcuserdata
|
||||
**/.generated/
|
||||
Flutter/App.framework
|
||||
Flutter/Flutter.framework
|
||||
Flutter/Flutter.podspec
|
||||
Flutter/Generated.xcconfig
|
||||
Flutter/app.flx
|
||||
Flutter/app.zip
|
||||
Flutter/flutter_assets/
|
||||
Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||
**/dgph
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
**/*sync/
|
||||
.sconsign.dblite
|
||||
.tags*
|
||||
**/.vagrant/
|
||||
**/DerivedData/
|
||||
Icon?
|
||||
**/Pods/
|
||||
**/.symlinks/
|
||||
profile
|
||||
xcuserdata
|
||||
**/.generated/
|
||||
Flutter/App.framework
|
||||
Flutter/Flutter.framework
|
||||
Flutter/Flutter.podspec
|
||||
Flutter/Generated.xcconfig
|
||||
Flutter/ephemeral/
|
||||
Flutter/app.flx
|
||||
Flutter/app.zip
|
||||
Flutter/flutter_assets/
|
||||
Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<?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>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>11.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "Generated.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1 +1 @@
|
|||
#include "Generated.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?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>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,91 +1,98 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "331C8080294A63A400263BE5"
|
||||
BuildableName = "RunnerTests.xctest"
|
||||
BlueprintName = "RunnerTests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?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>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,122 +1,122 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 406 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 282 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 406 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 862 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 862 B |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 762 B |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Launch Screen Assets
|
||||
|
||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||
|
||||
# Launch Screen Assets
|
||||
|
||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||
|
||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
|
@ -1,37 +1,37 @@
|
|||
<?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 opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</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="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
||||
<?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 opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</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="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
@ -1,45 +1,51 @@
|
|||
<?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>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>freezer</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>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
<?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>Freezer</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>freezer</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>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1 +1 @@
|
|||
#import "GeneratedPluginRegistrant.h"
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import Flutter
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
class RunnerTests: XCTestCase {
|
||||
|
||||
func testExample() {
|
||||
// If you add code to the Runner application, consider adding tests here.
|
||||
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c29d7a5a4b34927d67b6e3b72c35aedeb8f35bc9
|
|
@ -416,13 +416,18 @@ class DeezerAPI {
|
|||
}
|
||||
|
||||
//Log song listen to deezer
|
||||
Future logListen(String trackId) async {
|
||||
Future logListen(String trackId,
|
||||
{int seek = 0, int pause = 0, int sync = 1, int? timestamp}) async {
|
||||
await callApi('log.listen', params: {
|
||||
'params': {
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
'timestamp': timestamp ?? DateTime.now().millisecondsSinceEpoch,
|
||||
'ts_listen': DateTime.now().millisecondsSinceEpoch,
|
||||
'type': 1,
|
||||
'stat': {'seek': 0, 'pause': 0, 'sync': 1},
|
||||
'stat': {
|
||||
'seek': seek, // amount of times seeked
|
||||
'pause': pause, // amount of times paused
|
||||
'sync': sync
|
||||
},
|
||||
'media': {'id': trackId, 'type': 'song', 'format': 'MP3_128'}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:typed_data';
|
|||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
@ -17,15 +18,15 @@ import 'package:logging/logging.dart';
|
|||
class DeezerAudioSource extends StreamAudioSource {
|
||||
final _logger = Logger("DeezerAudioSource");
|
||||
|
||||
late int _quality;
|
||||
late int _initialQuality;
|
||||
late AudioQuality? _quality;
|
||||
late AudioQuality? _initialQuality;
|
||||
late String _trackId;
|
||||
late String _md5origin;
|
||||
late String _mediaVersion;
|
||||
final StreamInfoCallback? onStreamObtained;
|
||||
|
||||
DeezerAudioSource({
|
||||
required int quality,
|
||||
required AudioQuality quality,
|
||||
required String trackId,
|
||||
required String md5origin,
|
||||
required String mediaVersion,
|
||||
|
@ -38,7 +39,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
this._mediaVersion = mediaVersion;
|
||||
}
|
||||
|
||||
int get quality => _quality;
|
||||
AudioQuality? get quality => _quality;
|
||||
String get trackId => _trackId;
|
||||
String get md5origin => _md5origin;
|
||||
String get mediaVersion => _mediaVersion;
|
||||
|
@ -115,15 +116,16 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
_logger.warning(
|
||||
"quality fallback, response code: $rc, current quality: ${this.quality}");
|
||||
switch (_quality) {
|
||||
case Quality.MP3_128:
|
||||
_quality = -1;
|
||||
case AudioQuality.FLAC:
|
||||
_quality = AudioQuality.MP3_320;
|
||||
break;
|
||||
case AudioQuality.MP3_320:
|
||||
_quality = AudioQuality.MP3_128;
|
||||
break;
|
||||
case AudioQuality.MP3_128:
|
||||
default:
|
||||
_quality = null;
|
||||
throw QualityException("No quality to fallback to!");
|
||||
case Quality.MP3_320:
|
||||
_quality = Quality.MP3_128;
|
||||
break;
|
||||
case Quality.FLAC:
|
||||
_quality = Quality.MP3_320;
|
||||
break;
|
||||
}
|
||||
|
||||
return await _qualityFallback();
|
||||
|
@ -138,7 +140,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
final step1 = <int>[
|
||||
...md5origin.codeUnits,
|
||||
magic,
|
||||
...quality.toString().codeUnits,
|
||||
...quality!.toDeezerQualityInt().toString().codeUnits,
|
||||
magic,
|
||||
...trackId.codeUnits,
|
||||
magic,
|
||||
|
@ -232,7 +234,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
_logger
|
||||
.finest("request sent! rc: $rc, content-length: ${res.contentLength}");
|
||||
this.onStreamObtained?.call(StreamQualityInfo(
|
||||
format: quality == Quality.FLAC ? Format.FLAC : Format.MP3,
|
||||
format: quality == AudioQuality.FLAC ? Format.FLAC : Format.MP3,
|
||||
source: Source.stream,
|
||||
size: start + res.contentLength!,
|
||||
quality: quality,
|
||||
|
@ -306,6 +308,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
contentLength: cl,
|
||||
offset: start,
|
||||
stream: controller.stream,
|
||||
contentType: quality == 9 ? "audio/flac" : "audio/mpeg");
|
||||
contentType:
|
||||
quality == AudioQuality.FLAC ? "audio/flac" : "audio/mpeg");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/page_routes/blur_slide.dart';
|
||||
import 'package:freezer/page_routes/fade.dart';
|
||||
|
@ -459,6 +458,7 @@ class Playlist extends DeezerMediaItem {
|
|||
@HiveField(2)
|
||||
List<Track>? tracks;
|
||||
@HiveField(3)
|
||||
@JsonImageDetailsConverter()
|
||||
ImageDetails? image;
|
||||
@HiveField(4)
|
||||
Duration? duration;
|
||||
|
@ -563,11 +563,29 @@ class User {
|
|||
Map<String, dynamic> toJson() => _$UserToJson(this);
|
||||
}
|
||||
|
||||
abstract class ImageDetails {
|
||||
String get full => throw UnimplementedError("get full is not implemented");
|
||||
String get thumb => throw UnimplementedError("get full is not implemented");
|
||||
class JsonImageDetailsConverter
|
||||
extends JsonConverter<ImageDetails, Map<String, dynamic>> {
|
||||
const JsonImageDetailsConverter();
|
||||
|
||||
Map<String, dynamic> toJson(ImageDetails details) => details.toJson();
|
||||
|
||||
ImageDetails fromJson(Map<String, dynamic> json) {
|
||||
if (json.containsKey('full') || json.containsKey('thumb')) {
|
||||
return UrlImageDetails.fromJson(json);
|
||||
} else {
|
||||
return DeezerImageDetails.fromJson(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ImageDetails {
|
||||
String get full => throw UnimplementedError("get full is not implemented");
|
||||
String get thumb => throw UnimplementedError("get thumb is not implemented");
|
||||
Map<String, dynamic> toJson() =>
|
||||
throw UnimplementedError("toJson() is not implemented");
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class UrlImageDetails extends ImageDetails {
|
||||
final String full;
|
||||
final String thumb;
|
||||
|
@ -575,6 +593,10 @@ class UrlImageDetails extends ImageDetails {
|
|||
|
||||
factory UrlImageDetails.single(String url) =>
|
||||
UrlImageDetails(full: url, thumb: url);
|
||||
|
||||
factory UrlImageDetails.fromJson(Map<String, dynamic> json) =>
|
||||
_$UrlImageDetailsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$UrlImageDetailsToJson(this);
|
||||
}
|
||||
|
||||
// TODO: migrate to Uri instead of String
|
||||
|
@ -604,8 +626,8 @@ class DeezerImageDetails extends ImageDetails {
|
|||
DeezerImageDetails(json['md5'].split('-').first, type: json['type']);
|
||||
|
||||
factory DeezerImageDetails.fromJson(Map<String, dynamic> json) =>
|
||||
_$ImageDetailsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$ImageDetailsToJson(this);
|
||||
_$DeezerImageDetailsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$DeezerImageDetailsToJson(this);
|
||||
}
|
||||
|
||||
class SearchResults {
|
||||
|
@ -830,7 +852,7 @@ class HomePage {
|
|||
static Future<bool> exists(String channel) async {
|
||||
if (!await Hive.boxExists(_boxName)) return false;
|
||||
|
||||
return (await Hive.openLazyBox(_boxName)).containsKey(channel);
|
||||
return (await Hive.openLazyBox<HomePage>(_boxName)).containsKey(channel);
|
||||
}
|
||||
|
||||
Future<void> save(String channel) async {
|
||||
|
@ -843,7 +865,7 @@ class HomePage {
|
|||
static Future<HomePage?> local(String channel) async {
|
||||
final LazyBox<HomePage> box;
|
||||
try {
|
||||
box = await Hive.openLazyBox(_boxName);
|
||||
box = await Hive.openLazyBox<HomePage>(_boxName);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return null;
|
||||
|
@ -1290,7 +1312,7 @@ class StreamQualityInfo {
|
|||
// file size
|
||||
final int? size;
|
||||
// not available if offline
|
||||
final int? quality;
|
||||
final AudioQuality? quality;
|
||||
final Source source;
|
||||
|
||||
StreamQualityInfo({
|
||||
|
@ -1401,25 +1423,27 @@ extension ToLoopMode on AudioServiceRepeatMode {
|
|||
// }
|
||||
|
||||
extension PushRoute on NavigatorState {
|
||||
Future<T?> pushRoute<T extends Object?>({required WidgetBuilder builder}) {
|
||||
Future<T?> pushRoute<T extends Object?>(
|
||||
{required WidgetBuilder builder, RouteSettings? s}) {
|
||||
final PageRoute<T> route;
|
||||
switch (settings.navigatorRouteType) {
|
||||
case NavigatorRouteType.blur_slide:
|
||||
route = BlurSlidePageRoute<T>(builder: builder);
|
||||
route = BlurSlidePageRoute<T>(builder: builder, settings: s);
|
||||
break;
|
||||
case NavigatorRouteType.material:
|
||||
route = MaterialPageRoute<T>(builder: builder);
|
||||
route = MaterialPageRoute<T>(builder: builder, settings: s);
|
||||
break;
|
||||
case NavigatorRouteType.cupertino:
|
||||
route = CupertinoPageRoute<T>(builder: builder);
|
||||
route = CupertinoPageRoute<T>(builder: builder, settings: s);
|
||||
break;
|
||||
case NavigatorRouteType.fade:
|
||||
route = FadePageRoute<T>(builder: builder);
|
||||
route = FadePageRoute<T>(builder: builder, settings: s);
|
||||
break;
|
||||
case NavigatorRouteType.fade_blur:
|
||||
route = FadePageRoute<T>(builder: builder, blur: true);
|
||||
route = FadePageRoute<T>(builder: builder, settings: s, blur: true);
|
||||
break;
|
||||
}
|
||||
|
||||
return push(route);
|
||||
}
|
||||
}
|
||||
|
@ -1468,12 +1492,6 @@ class QualityException implements Exception {
|
|||
String get message => _message ?? "$runtimeType";
|
||||
}
|
||||
|
||||
class Quality {
|
||||
static const int MP3_128 = 1;
|
||||
static const int MP3_320 = 3;
|
||||
static const int FLAC = 9;
|
||||
}
|
||||
|
||||
// different kinds of flow (scrapped from web)
|
||||
enum FlowConfig {
|
||||
motivation,
|
||||
|
|
|
@ -224,7 +224,7 @@ class PlaylistAdapter extends TypeAdapter<Playlist> {
|
|||
id: fields[0] as String,
|
||||
title: fields[1] as String?,
|
||||
tracks: (fields[2] as List?)?.cast<Track>(),
|
||||
image: fields[3] as DeezerImageDetails?,
|
||||
image: fields[3] as ImageDetails?,
|
||||
trackCount: fields[5] as int?,
|
||||
duration: fields[4] as Duration?,
|
||||
user: fields[6] as User?,
|
||||
|
@ -311,7 +311,7 @@ class UserAdapter extends TypeAdapter<User> {
|
|||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
class ImageDetailsAdapter extends TypeAdapter<DeezerImageDetails> {
|
||||
class DeezerImageDetailsAdapter extends TypeAdapter<DeezerImageDetails> {
|
||||
@override
|
||||
final int typeId = 6;
|
||||
|
||||
|
@ -343,7 +343,7 @@ class ImageDetailsAdapter extends TypeAdapter<DeezerImageDetails> {
|
|||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ImageDetailsAdapter &&
|
||||
other is DeezerImageDetailsAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
@ -1254,9 +1254,8 @@ Playlist _$PlaylistFromJson(Map<String, dynamic> json) => Playlist(
|
|||
tracks: (json['tracks'] as List<dynamic>?)
|
||||
?.map((e) => Track.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
image: json['image'] == null
|
||||
? null
|
||||
: DeezerImageDetails.fromJson(json['image'] as Map<String, dynamic>),
|
||||
image: _$JsonConverterFromJson<Map<String, dynamic>, ImageDetails>(
|
||||
json['image'], const JsonImageDetailsConverter().fromJson),
|
||||
trackCount: json['trackCount'] as int?,
|
||||
duration: json['duration'] == null
|
||||
? null
|
||||
|
@ -1273,7 +1272,8 @@ Map<String, dynamic> _$PlaylistToJson(Playlist instance) => <String, dynamic>{
|
|||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'tracks': instance.tracks,
|
||||
'image': instance.image,
|
||||
'image': _$JsonConverterToJson<Map<String, dynamic>, ImageDetails>(
|
||||
instance.image, const JsonImageDetailsConverter().toJson),
|
||||
'duration': instance.duration?.inMicroseconds,
|
||||
'trackCount': instance.trackCount,
|
||||
'user': instance.user,
|
||||
|
@ -1282,6 +1282,18 @@ Map<String, dynamic> _$PlaylistToJson(Playlist instance) => <String, dynamic>{
|
|||
'description': instance.description,
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
Object? json,
|
||||
Value? Function(Json json) fromJson,
|
||||
) =>
|
||||
json == null ? null : fromJson(json as Json);
|
||||
|
||||
Json? _$JsonConverterToJson<Json, Value>(
|
||||
Value? value,
|
||||
Json? Function(Value value) toJson,
|
||||
) =>
|
||||
value == null ? null : toJson(value);
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> json) => User(
|
||||
id: json['id'] as String?,
|
||||
name: json['name'] as String?,
|
||||
|
@ -1297,13 +1309,25 @@ Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
|||
'picture': instance.picture,
|
||||
};
|
||||
|
||||
DeezerImageDetails _$ImageDetailsFromJson(Map<String, dynamic> json) =>
|
||||
UrlImageDetails _$UrlImageDetailsFromJson(Map<String, dynamic> json) =>
|
||||
UrlImageDetails(
|
||||
full: json['full'] as String,
|
||||
thumb: json['thumb'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UrlImageDetailsToJson(UrlImageDetails instance) =>
|
||||
<String, dynamic>{
|
||||
'full': instance.full,
|
||||
'thumb': instance.thumb,
|
||||
};
|
||||
|
||||
DeezerImageDetails _$DeezerImageDetailsFromJson(Map<String, dynamic> json) =>
|
||||
DeezerImageDetails(
|
||||
json['md5'] as String,
|
||||
type: json['type'] as String? ?? 'cover',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ImageDetailsToJson(DeezerImageDetails instance) =>
|
||||
Map<String, dynamic> _$DeezerImageDetailsToJson(DeezerImageDetails instance) =>
|
||||
<String, dynamic>{
|
||||
'type': instance.type,
|
||||
'md5': instance.md5,
|
||||
|
@ -1315,12 +1339,14 @@ Lyrics _$LyricsFromJson(Map<String, dynamic> json) => Lyrics(
|
|||
lyrics: (json['lyrics'] as List<dynamic>?)
|
||||
?.map((e) => Lyric.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
sync: json['sync'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LyricsToJson(Lyrics instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'writers': instance.writers,
|
||||
'lyrics': instance.lyrics,
|
||||
'sync': instance.sync,
|
||||
};
|
||||
|
||||
Lyric _$LyricFromJson(Map<String, dynamic> json) => Lyric(
|
||||
|
@ -1490,7 +1516,7 @@ Show _$ShowFromJson(Map<String, dynamic> json) => Show(
|
|||
Map<String, dynamic> _$ShowToJson(Show instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'art': instance.art?.toJson(),
|
||||
'art': instance.art,
|
||||
'id': instance.id,
|
||||
};
|
||||
|
||||
|
@ -1523,7 +1549,7 @@ StreamQualityInfo _$StreamQualityInfoFromJson(Map<String, dynamic> json) =>
|
|||
StreamQualityInfo(
|
||||
format: $enumDecode(_$FormatEnumMap, json['format']),
|
||||
source: $enumDecode(_$SourceEnumMap, json['source']),
|
||||
quality: json['quality'] as int?,
|
||||
quality: $enumDecodeNullable(_$AudioQualityEnumMap, json['quality']),
|
||||
size: json['size'] as int?,
|
||||
);
|
||||
|
||||
|
@ -1531,7 +1557,7 @@ Map<String, dynamic> _$StreamQualityInfoToJson(StreamQualityInfo instance) =>
|
|||
<String, dynamic>{
|
||||
'format': _$FormatEnumMap[instance.format]!,
|
||||
'size': instance.size,
|
||||
'quality': instance.quality,
|
||||
'quality': _$AudioQualityEnumMap[instance.quality],
|
||||
'source': _$SourceEnumMap[instance.source]!,
|
||||
};
|
||||
|
||||
|
@ -1544,3 +1570,10 @@ const _$SourceEnumMap = {
|
|||
Source.offline: 'offline',
|
||||
Source.stream: 'stream',
|
||||
};
|
||||
|
||||
const _$AudioQualityEnumMap = {
|
||||
AudioQuality.MP3_128: 'MP3_128',
|
||||
AudioQuality.MP3_320: 'MP3_320',
|
||||
AudioQuality.FLAC: 'FLAC',
|
||||
AudioQuality.ASK: 'ASK',
|
||||
};
|
||||
|
|
|
@ -737,8 +737,8 @@ class Download {
|
|||
"md5origin": t.playbackDetails![0],
|
||||
"mediaVersion": t.playbackDetails![1],
|
||||
"quality": private
|
||||
? settings.getQualityInt(settings.offlineQuality)
|
||||
: settings.getQualityInt((quality ?? settings.downloadQuality)),
|
||||
? settings.offlineQuality.toDeezerQualityInt()
|
||||
: (quality ?? settings.downloadQuality).toDeezerQualityInt(),
|
||||
"title": t.title,
|
||||
"path": path,
|
||||
"image": t.albumArt?.thumb
|
||||
|
|
|
@ -35,7 +35,6 @@ class PlayerHelper {
|
|||
late StreamSubscription _playbackStateStreamSubscription;
|
||||
QueueSource? queueSource;
|
||||
AudioServiceRepeatMode repeatType = AudioServiceRepeatMode.none;
|
||||
Timer? _timer;
|
||||
int? audioSession;
|
||||
int? _prevAudioSession;
|
||||
bool equalizerOpen = false;
|
||||
|
@ -64,11 +63,11 @@ class PlayerHelper {
|
|||
int get queueIndex => _queueIndex;
|
||||
|
||||
Future<void> initAudioHandler() async {
|
||||
final initArgs = AudioPlayerTaskInitArguments.from(
|
||||
settings: settings, deezerAPI: deezerAPI);
|
||||
// initialize our audiohandler instance
|
||||
audioHandler = await AudioService.init(
|
||||
builder: () => AudioPlayerTask(
|
||||
ignoreInterruptions: settings.ignoreInterruptions,
|
||||
deezerAPI: deezerAPI),
|
||||
builder: () => AudioPlayerTask(initArgs),
|
||||
config: AudioServiceConfig(
|
||||
notificationColor: settings.primaryColor,
|
||||
androidStopForegroundOnPause: false,
|
||||
|
@ -87,14 +86,12 @@ class PlayerHelper {
|
|||
_started = true;
|
||||
//Subscribe to custom events
|
||||
_customEventSubscription = audioHandler.customEvent.listen((event) async {
|
||||
if (!(event is Map)) return;
|
||||
Logger('PlayerHelper').fine("event received: " + event['action']);
|
||||
if (event is! Map) return;
|
||||
Logger('PlayerHelper').fine("event received: ${event['action']}");
|
||||
switch (event['action']) {
|
||||
case 'onLoad':
|
||||
//After audio_service is loaded, load queue, set quality
|
||||
await settings.updateAudioServiceQuality();
|
||||
await audioHandler.customAction('load');
|
||||
await authorizeLastFM();
|
||||
break;
|
||||
case 'onRestore':
|
||||
//Load queueSource from isolate
|
||||
|
@ -102,13 +99,6 @@ class PlayerHelper {
|
|||
repeatType = event['repeatMode'] as AudioServiceRepeatMode;
|
||||
_queueIndex = getQueueIndex();
|
||||
break;
|
||||
case 'screenAndroidAuto':
|
||||
List<MediaItem> data = await androidAuto.getScreen(event['id']);
|
||||
await audioHandler.customAction('screenAndroidAuto', {'value': data});
|
||||
break;
|
||||
case 'tracksAndroidAuto':
|
||||
await androidAuto.playItem(event['id']);
|
||||
break;
|
||||
case 'audioSession':
|
||||
if (!settings.enableEqualizer) break;
|
||||
//Save
|
||||
|
@ -123,8 +113,9 @@ class PlayerHelper {
|
|||
}
|
||||
//Change session id
|
||||
if (_prevAudioSession != audioSession) {
|
||||
if (_prevAudioSession != null)
|
||||
if (_prevAudioSession != null) {
|
||||
Equalizer.removeAudioSessionId(_prevAudioSession!);
|
||||
}
|
||||
Equalizer.setAudioSessionId(audioSession!);
|
||||
}
|
||||
break;
|
||||
|
@ -146,36 +137,21 @@ class PlayerHelper {
|
|||
//Save queue
|
||||
await audioHandler.customAction('saveQueue', {});
|
||||
//Add to history
|
||||
if (cache.history.length > 0 && cache.history.last.id == mediaItem.id)
|
||||
if (cache.history.isNotEmpty && cache.history.last.id == mediaItem.id) {
|
||||
return;
|
||||
}
|
||||
cache.history.add(Track.fromMediaItem(mediaItem));
|
||||
cache.save();
|
||||
});
|
||||
|
||||
//Logging listen timer
|
||||
_timer = Timer.periodic(Duration(seconds: 2), (timer) async {
|
||||
if (audioHandler.mediaItem.value == null ||
|
||||
!audioHandler.playbackState.value.playing) return;
|
||||
if (audioHandler.playbackState.value.position.inSeconds >
|
||||
(audioHandler.mediaItem.value!.duration!.inSeconds * 0.75)) {
|
||||
if (cache.loggedTrackId == audioHandler.mediaItem.value!.id) return;
|
||||
cache.loggedTrackId = audioHandler.mediaItem.value!.id;
|
||||
await cache.save();
|
||||
|
||||
//Log to Deezer
|
||||
if (settings.logListen) {
|
||||
deezerAPI.logListen(audioHandler.mediaItem.value!.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Start audio_service
|
||||
// await startService(); it is already ready, there is no need to start it
|
||||
}
|
||||
|
||||
Future authorizeLastFM() async {
|
||||
if (settings.lastFMUsername == null || settings.lastFMPassword == null)
|
||||
Future<void> authorizeLastFM() async {
|
||||
if (settings.lastFMUsername == null || settings.lastFMPassword == null) {
|
||||
return;
|
||||
}
|
||||
await audioHandler.customAction('authorizeLastFM', {
|
||||
'username': settings.lastFMUsername,
|
||||
'password': settings.lastFMPassword
|
||||
|
@ -237,7 +213,7 @@ class PlayerHelper {
|
|||
tracks[0].id,
|
||||
QueueSource(
|
||||
id: trackId,
|
||||
text: 'Mix based on'.i18n + ' $trackTitle',
|
||||
text: '${'Mix based on'.i18n} $trackTitle',
|
||||
source: 'mix'));
|
||||
}
|
||||
|
||||
|
@ -332,6 +308,41 @@ class PlayerHelper {
|
|||
// }
|
||||
}
|
||||
|
||||
class AudioPlayerTaskInitArguments {
|
||||
final bool ignoreInterruptions;
|
||||
final DeezerAPI deezerAPI;
|
||||
final bool logListen;
|
||||
final String? lastFMUsername;
|
||||
final String? lastFMPassword;
|
||||
|
||||
AudioPlayerTaskInitArguments({
|
||||
required this.deezerAPI,
|
||||
required this.ignoreInterruptions,
|
||||
required this.logListen,
|
||||
required this.lastFMUsername,
|
||||
required this.lastFMPassword,
|
||||
});
|
||||
|
||||
static AudioPlayerTaskInitArguments from(
|
||||
{required Settings settings, required DeezerAPI deezerAPI}) {
|
||||
return AudioPlayerTaskInitArguments(
|
||||
deezerAPI: deezerAPI,
|
||||
logListen: settings.logListen,
|
||||
ignoreInterruptions: settings.ignoreInterruptions,
|
||||
lastFMUsername: settings.lastFMUsername,
|
||||
lastFMPassword: settings.lastFMPassword);
|
||||
}
|
||||
|
||||
static Future<AudioPlayerTaskInitArguments> loadSettings() async {
|
||||
final settings = await Settings.load();
|
||||
|
||||
final deezerAPI = DeezerAPI(arl: settings.arl);
|
||||
await deezerAPI.authorize();
|
||||
|
||||
return from(settings: settings, deezerAPI: deezerAPI);
|
||||
}
|
||||
}
|
||||
|
||||
class AudioPlayerTask extends BaseAudioHandler {
|
||||
late AudioPlayer _player;
|
||||
late DeezerAPI _deezerAPI;
|
||||
|
@ -349,28 +360,41 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
StreamSubscription? _audioSessionSub;
|
||||
StreamSubscription? _visualizerSubscription;
|
||||
|
||||
late final AndroidAuto _androidAuto;
|
||||
|
||||
//Loaded from file/frontendjust
|
||||
int? mobileQuality;
|
||||
int? wifiQuality;
|
||||
AudioQuality mobileQuality = AudioQuality.MP3_128;
|
||||
AudioQuality wifiQuality = AudioQuality.MP3_128;
|
||||
QueueSource? queueSource;
|
||||
Duration? _lastPosition;
|
||||
AudioServiceRepeatMode _repeatMode = AudioServiceRepeatMode.none;
|
||||
|
||||
Completer<List<MediaItem>>? _androidAutoCallback;
|
||||
Scrobblenaut? _scrobblenaut;
|
||||
// Last logged track id
|
||||
String? _loggedTrackId;
|
||||
|
||||
// track logging
|
||||
bool _shouldLogTracks = false;
|
||||
int _amountPaused = 0;
|
||||
int _amountSeeked = 0;
|
||||
int? _timestamp;
|
||||
|
||||
MediaItem get currentMediaItem => queue.value[_queueIndex];
|
||||
late final LazyBox _box;
|
||||
|
||||
AudioPlayerTask(
|
||||
{bool ignoreInterruptions = false, required DeezerAPI deezerAPI}) {
|
||||
_deezerAPI = deezerAPI;
|
||||
unawaited(_init(ignoreInterruptions));
|
||||
AudioPlayerTask([AudioPlayerTaskInitArguments? initArgs]) {
|
||||
if (initArgs == null) {
|
||||
unawaited(AudioPlayerTaskInitArguments.loadSettings().then(_init));
|
||||
return;
|
||||
}
|
||||
unawaited(_init(initArgs));
|
||||
}
|
||||
|
||||
Future<void> _init(bool ignoreInterruptions) async {
|
||||
Future<void> _init(AudioPlayerTaskInitArguments initArgs) async {
|
||||
_deezerAPI = initArgs.deezerAPI;
|
||||
_androidAuto = AndroidAuto(deezerAPI: _deezerAPI);
|
||||
_shouldLogTracks = initArgs.logListen;
|
||||
|
||||
final session = await AudioSession.instance;
|
||||
session.configure(AudioSessionConfiguration.music());
|
||||
|
||||
|
@ -378,7 +402,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
path: (await getExternalCacheDirectories())?[0].path ??
|
||||
(await getExternalStorageDirectory())?.path);
|
||||
|
||||
if (ignoreInterruptions) {
|
||||
if (initArgs.ignoreInterruptions) {
|
||||
_player = AudioPlayer(handleInterruptions: false);
|
||||
session.interruptionEventStream.listen((_) {});
|
||||
session.becomingNoisyEventStream.listen((_) {});
|
||||
|
@ -388,6 +412,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
|
||||
//Update track index
|
||||
_player.currentIndexStream.listen((index) {
|
||||
_timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
if (index != null && queue.value.isNotEmpty) {
|
||||
_queueIndex = index;
|
||||
mediaItem.add(currentMediaItem);
|
||||
|
@ -399,13 +424,6 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
});
|
||||
//Update state on all clients on change
|
||||
_eventSub = _player.playbackEventStream.listen((event) {
|
||||
//Quality string
|
||||
if (_queueIndex != -1 && _queueIndex < queue.value.length) {
|
||||
Map extras = currentMediaItem.extras!;
|
||||
extras['qualityString'] = '';
|
||||
queue.value[_queueIndex] =
|
||||
currentMediaItem.copyWith(extras: extras as Map<String, dynamic>?);
|
||||
}
|
||||
//Update
|
||||
_broadcastState();
|
||||
});
|
||||
|
@ -413,11 +431,12 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
switch (state) {
|
||||
case ProcessingState.completed:
|
||||
//Player ended, get more songs
|
||||
if (_queueIndex == queue.value.length - 1)
|
||||
if (_queueIndex == queue.value.length - 1) {
|
||||
customEvent.add({
|
||||
'action': 'queueEnd',
|
||||
'queueSource': (queueSource ?? QueueSource()).toJson()
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -431,6 +450,14 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
|
||||
//Load queue
|
||||
// queue.add(_queue);
|
||||
|
||||
await _loadQueueFile();
|
||||
|
||||
if (initArgs.lastFMUsername != null && initArgs.lastFMPassword != null) {
|
||||
await _authorizeLastFM(
|
||||
initArgs.lastFMUsername!, initArgs.lastFMPassword!);
|
||||
}
|
||||
|
||||
customEvent.add({'action': 'onLoad'});
|
||||
}
|
||||
|
||||
|
@ -438,6 +465,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
Future skipToQueueItem(int index) async {
|
||||
_lastPosition = null;
|
||||
|
||||
unawaited(_logListenedTrack());
|
||||
//Skip in player
|
||||
await _player.seek(Duration.zero, index: index);
|
||||
_queueIndex = index;
|
||||
|
@ -461,15 +489,22 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
track: currentMediaItem.title,
|
||||
artist: currentMediaItem.artist!,
|
||||
album: currentMediaItem.album,
|
||||
duration: currentMediaItem.duration,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future pause() => _player.pause();
|
||||
Future<void> pause() {
|
||||
_amountPaused++;
|
||||
return _player.pause();
|
||||
}
|
||||
|
||||
@override
|
||||
Future seek(Duration? pos) => _player.seek(pos);
|
||||
Future<void> seek(Duration? pos) {
|
||||
_amountSeeked++;
|
||||
return _player.seek(pos);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> fastForward() =>
|
||||
|
@ -507,6 +542,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
_lastPosition = null;
|
||||
if (_queueIndex == queue.value.length - 1) return;
|
||||
//Update buffering state
|
||||
unawaited(_logListenedTrack());
|
||||
_queueIndex++;
|
||||
await _player.seekToNext();
|
||||
_broadcastState();
|
||||
|
@ -518,21 +554,29 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
//Update buffering state
|
||||
//_skipState = AudioProcessingState.skippingToPrevious;
|
||||
//Normal skip to previous
|
||||
unawaited(_logListenedTrack());
|
||||
_queueIndex--;
|
||||
await _player.seekToPrevious();
|
||||
//_skipState = null;
|
||||
}
|
||||
|
||||
Future<void> _logListenedTrack() async {
|
||||
if (!_shouldLogTracks) return;
|
||||
|
||||
//Log to Deezer
|
||||
deezerAPI.logListen(
|
||||
currentMediaItem.id,
|
||||
seek: _amountSeeked,
|
||||
pause: _amountPaused,
|
||||
sync: _amountSeeked == 0 && _amountPaused == 0 ? 1 : 0,
|
||||
timestamp: _timestamp,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MediaItem>> getChildren(String parentMediaId,
|
||||
[Map<String, dynamic>? options]) async {
|
||||
customEvent.add({'action': 'screenAndroidAuto', 'id': parentMediaId});
|
||||
|
||||
//Wait for data from main thread
|
||||
_androidAutoCallback = Completer<List<MediaItem>>();
|
||||
final data = await _androidAutoCallback!.future;
|
||||
_androidAutoCallback = null;
|
||||
return data;
|
||||
return await _androidAuto.getScreen(parentMediaId);
|
||||
}
|
||||
|
||||
//While seeking, jump 10s every 1s
|
||||
|
@ -550,8 +594,9 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
Duration newPos = _player.position + offset;
|
||||
//Out of bounds check
|
||||
if (newPos < Duration.zero) newPos = Duration.zero;
|
||||
if (newPos > currentMediaItem.duration!)
|
||||
if (newPos > currentMediaItem.duration!) {
|
||||
newPos = currentMediaItem.duration!;
|
||||
}
|
||||
|
||||
await _player.seek(newPos);
|
||||
}
|
||||
|
@ -565,21 +610,17 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
_player.playing ? MediaControl.pause : MediaControl.play,
|
||||
/*if (_queueIndex != _queue!.length - 1)*/ MediaControl
|
||||
.skipToNext,
|
||||
//Stop
|
||||
MediaControl(
|
||||
androidIcon: 'drawable/ic_action_stop',
|
||||
label: 'stop',
|
||||
action: MediaAction.stop),
|
||||
//Stop -- USELESS.
|
||||
// MediaControl(
|
||||
// androidIcon: 'drawable/ic_action_stop',
|
||||
// label: 'stop',
|
||||
// action: MediaAction.stop),
|
||||
// i mean, the user can just swipe the notification away to stop
|
||||
]
|
||||
: [
|
||||
MediaControl.rewind,
|
||||
_player.playing ? MediaControl.pause : MediaControl.play,
|
||||
MediaControl.fastForward,
|
||||
MediaControl.rewind,
|
||||
MediaControl(
|
||||
androidIcon: 'drawable/ic_action_stop',
|
||||
label: 'stop',
|
||||
action: MediaAction.stop),
|
||||
],
|
||||
systemActions: queueSource?.source != 'show'
|
||||
? const {
|
||||
|
@ -675,23 +716,25 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
}
|
||||
|
||||
//Show episode direct link
|
||||
if (mediaItem.extras!['showUrl'] != null)
|
||||
if (mediaItem.extras!['showUrl'] != null) {
|
||||
return UrlAudioSource(
|
||||
Uri.parse(mediaItem.extras!['showUrl']),
|
||||
onStreamObtained: (qualityInfo) =>
|
||||
customEvent.add({'action': 'streamInfo', 'data': qualityInfo}),
|
||||
);
|
||||
}
|
||||
|
||||
//Due to current limitations of just_audio, quality fallback moved to DeezerDataSource in ExoPlayer
|
||||
//This just returns fake url that contains metadata
|
||||
List? playbackDetails = jsonDecode(mediaItem.extras!['playbackDetails']);
|
||||
//Quality
|
||||
ConnectivityResult conn = await Connectivity().checkConnectivity();
|
||||
int? quality = mobileQuality;
|
||||
AudioQuality quality = mobileQuality;
|
||||
if (conn == ConnectivityResult.wifi) quality = wifiQuality;
|
||||
|
||||
if ((playbackDetails ?? []).length < 2)
|
||||
if ((playbackDetails ?? []).length < 2) {
|
||||
throw Exception('not enough playback details');
|
||||
}
|
||||
|
||||
//String url = 'https://dzcdn.net/?md5=${playbackDetails[0]}&mv=${playbackDetails[1]}&q=${quality.toString()}#${mediaItem.id}';
|
||||
// final uri = Uri.http('localhost:36958', '', {
|
||||
|
@ -721,8 +764,8 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
case 'updateQuality':
|
||||
//Pass wifi & mobile quality by custom action
|
||||
//Isolate can't access globals
|
||||
wifiQuality = args!['wifiQuality'];
|
||||
mobileQuality = args['mobileQuality'];
|
||||
wifiQuality = args!['wifiQuality'] as AudioQuality;
|
||||
mobileQuality = args['mobileQuality'] as AudioQuality;
|
||||
break;
|
||||
//Update queue source
|
||||
case 'queueSource':
|
||||
|
@ -737,15 +780,6 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
case 'saveQueue':
|
||||
await _saveQueue();
|
||||
break;
|
||||
//Load queue after some initialization in frontend
|
||||
case 'load':
|
||||
await _loadQueueFile();
|
||||
break;
|
||||
|
||||
//Android audio callback
|
||||
case 'screenAndroidAuto':
|
||||
_androidAutoCallback?.complete((args!['value'] as List<MediaItem>?));
|
||||
break;
|
||||
//Reorder tracks, args = [old, new]
|
||||
case 'reorder':
|
||||
final oldIndex = args!['oldIndex']! as int;
|
||||
|
@ -790,16 +824,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
case 'authorizeLastFM':
|
||||
final username = args!['username']! as String;
|
||||
final password = args['password']! as String;
|
||||
try {
|
||||
final lastFM = await LastFM.authenticateWithPasswordHash(
|
||||
apiKey: 'b6ab5ae967bcd8b10b23f68f42493829',
|
||||
apiSecret: '861b0dff9a8a574bec747f9dab8b82bf',
|
||||
username: username,
|
||||
passwordHash: password);
|
||||
_scrobblenaut = Scrobblenaut(lastFM: lastFM);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
await _authorizeLastFM(username, password);
|
||||
break;
|
||||
case 'disableLastFM':
|
||||
_scrobblenaut = null;
|
||||
|
@ -905,10 +930,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
[Map<String, dynamic>? args]) async {
|
||||
//Android auto load tracks
|
||||
if (mediaId.startsWith(AndroidAuto.prefix)) {
|
||||
customEvent.add({
|
||||
'action': 'tracksAndroidAuto',
|
||||
'id': mediaId.replaceFirst(AndroidAuto.prefix, '')
|
||||
});
|
||||
await _androidAuto.playItem(mediaId.substring(AndroidAuto.prefix.length));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -993,6 +1015,15 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
tracks.map<Future<MediaItem>>((t) => t.toMediaItem()));
|
||||
await addQueueItems(mi);
|
||||
}
|
||||
|
||||
Future<void> _authorizeLastFM(String username, String password) async {
|
||||
_scrobblenaut = Scrobblenaut(
|
||||
lastFM: await LastFM.authenticateWithPasswordHash(
|
||||
apiKey: 'b6ab5ae967bcd8b10b23f68f42493829',
|
||||
apiSecret: '861b0dff9a8a574bec747f9dab8b82bf',
|
||||
username: username,
|
||||
passwordHash: password));
|
||||
}
|
||||
}
|
||||
|
||||
//Seeker from audio_service example (why reinvent the wheel?)
|
||||
|
|
198
lib/main.dart
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
|
@ -10,6 +11,8 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
|||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/page_routes/blur_slide.dart';
|
||||
import 'package:freezer/page_routes/fade.dart';
|
||||
import 'package:freezer/page_routes/scale_fade.dart';
|
||||
import 'package:freezer/type_adapters/uri.dart';
|
||||
import 'package:freezer/ui/downloads_screen.dart';
|
||||
|
@ -21,7 +24,6 @@ import 'package:freezer/ui/settings_screen.dart';
|
|||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:move_to_background/move_to_background.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:quick_actions/quick_actions.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
|
@ -56,7 +58,7 @@ void main() async {
|
|||
..registerAdapter(ArtistAdapter())
|
||||
..registerAdapter(PlaylistAdapter())
|
||||
..registerAdapter(UserAdapter())
|
||||
..registerAdapter(ImageDetailsAdapter())
|
||||
..registerAdapter(DeezerImageDetailsAdapter())
|
||||
..registerAdapter(LyricsAdapter())
|
||||
..registerAdapter(LyricAdapter())
|
||||
..registerAdapter(SmartTrackListAdapter())
|
||||
|
@ -102,7 +104,6 @@ void main() async {
|
|||
}());
|
||||
|
||||
//Do on BG
|
||||
playerHelper.authorizeLastFM();
|
||||
await playerHelper.initAudioHandler();
|
||||
|
||||
runApp(FreezerApp());
|
||||
|
@ -250,7 +251,8 @@ class _LoginMainWrapperState extends State<LoginMainWrapper> {
|
|||
: SystemUiOverlayStyle.light)
|
||||
.copyWith(
|
||||
statusBarColor: Colors.transparent,
|
||||
systemNavigationBarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
systemNavigationBarColor:
|
||||
Colors.transparent, // Theme.of(context).scaffoldBackgroundColor,
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: LoginWidget(
|
||||
|
@ -261,22 +263,22 @@ class _LoginMainWrapperState extends State<LoginMainWrapper> {
|
|||
}
|
||||
}
|
||||
|
||||
class _PlayerBarSliverDelegate extends SliverPersistentHeaderDelegate {
|
||||
@override
|
||||
// TODO: implement minExtent
|
||||
double get minExtent => 59.0;
|
||||
double get maxExtent => 59.0;
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
||||
false;
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return PlayerBar();
|
||||
}
|
||||
}
|
||||
// class _PlayerBarSliverDelegate extends SliverPersistentHeaderDelegate {
|
||||
// @override
|
||||
// // TODO: implement minExtent
|
||||
// double get minExtent => 59.0;
|
||||
// double get maxExtent => 59.0;
|
||||
//
|
||||
// @override
|
||||
// bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
||||
// false;
|
||||
//
|
||||
// @override
|
||||
// Widget build(
|
||||
// BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
// return PlayerBar();
|
||||
// }
|
||||
// }
|
||||
|
||||
class MainScreen extends StatefulWidget {
|
||||
@override
|
||||
|
@ -290,6 +292,7 @@ class _MainScreenState extends State<MainScreen>
|
|||
0: '/',
|
||||
1: '/podcasts',
|
||||
2: '/library',
|
||||
3: '/search',
|
||||
};
|
||||
StreamSubscription? _urlLinkStream;
|
||||
int _keyPressed = 0;
|
||||
|
@ -478,66 +481,49 @@ class _MainScreenState extends State<MainScreen>
|
|||
return RawKeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKey: _handleKey,
|
||||
child: Scaffold(
|
||||
// bottomSheet: DraggableScrollableSheet(
|
||||
// minChildSize: 0.2,
|
||||
// maxChildSize: 1.0,
|
||||
// snap: true,
|
||||
// snapSizes: [0.2, 1.0],
|
||||
// initialChildSize: 0.2,
|
||||
// builder: (context, scrollController) => NestedScrollView(
|
||||
// headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
// SliverPersistentHeader(
|
||||
// delegate: _PlayerBarSliverDelegate(),
|
||||
// pinned: true,
|
||||
// floating: true,
|
||||
// )
|
||||
// ],
|
||||
// body: SizedBox.fromSize(
|
||||
// size: MediaQuery.of(context).size,
|
||||
// child: PlayerScreen(),
|
||||
// ))),
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: FocusScope(
|
||||
node: navigationBarFocusNode,
|
||||
child:
|
||||
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
PlayerBar(),
|
||||
ValueListenableBuilder<int>(
|
||||
valueListenable: _selected,
|
||||
builder: (context, value, _) {
|
||||
return NavigationBar(
|
||||
selectedIndex: value,
|
||||
onDestinationSelected: (int s) async {
|
||||
//Pop all routes until home screen
|
||||
navigatorKey.currentState!
|
||||
.popUntil((route) => route.isFirst);
|
||||
navigatorKey.currentState!
|
||||
.pushReplacementNamed(_destinations[s]!);
|
||||
child: FancyScaffold(
|
||||
bottomNavigationBar: FocusScope(
|
||||
node: navigationBarFocusNode,
|
||||
child: ValueListenableBuilder<int>(
|
||||
valueListenable: _selected,
|
||||
builder: (context, value, _) {
|
||||
return NavigationBar(
|
||||
selectedIndex: value,
|
||||
onDestinationSelected: (int s) async {
|
||||
//Pop all routes until home screen
|
||||
navigatorKey.currentState!
|
||||
.popUntil((route) => route.isFirst);
|
||||
navigatorKey.currentState!
|
||||
.pushReplacementNamed(_destinations[s]!);
|
||||
|
||||
if (_selected.value != s) _selected.value = s;
|
||||
},
|
||||
destinations: <NavigationDestination>[
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
selectedIcon: const Icon(Icons.home),
|
||||
label: 'Home'.i18n),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.podcasts),
|
||||
label: 'Podcasts'.i18n),
|
||||
// NavigationDestination(
|
||||
// icon: const Icon(Icons.search),
|
||||
// label: 'Search'.i18n),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.library_music_outlined),
|
||||
selectedIcon: const Icon(Icons.library_music),
|
||||
label: 'Library'.i18n)
|
||||
],
|
||||
);
|
||||
})
|
||||
]),
|
||||
),
|
||||
if (_selected.value != s) _selected.value = s;
|
||||
},
|
||||
destinations: <NavigationDestination>[
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
selectedIcon: const Icon(Icons.home),
|
||||
label: 'Home'.i18n),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.podcasts),
|
||||
label: 'Podcasts'.i18n),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.library_music_outlined),
|
||||
selectedIcon: const Icon(Icons.library_music),
|
||||
label: 'Library'.i18n),
|
||||
|
||||
// NavigationDestination(
|
||||
// icon: const Icon(Icons.search),
|
||||
// label: 'Search'.i18n),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
bottomPanel: PlayerBar(
|
||||
shouldHandleClicks: false,
|
||||
shouldHaveHero: false,
|
||||
),
|
||||
bottomPanelHeight: 68.0,
|
||||
expandedPanel: PlayerScreen(),
|
||||
body: Focus(
|
||||
focusNode: screenFocusNode,
|
||||
skipTraversal: true,
|
||||
|
@ -545,23 +531,21 @@ class _MainScreenState extends State<MainScreen>
|
|||
child: _MainRouteNavigator(
|
||||
navigatorKey: navigatorKey,
|
||||
routes: {
|
||||
'/': (context) => HomeScreen(),
|
||||
'/podcasts': (context) => Scaffold(
|
||||
appBar: AppBar(title: Text('Podcasts'.i18n)),
|
||||
body: HomePageScreen(
|
||||
cacheable: true,
|
||||
channel: DeezerChannel(target: 'channels/podcasts'),
|
||||
),
|
||||
'/': (context) => const HomeScreen(),
|
||||
'/podcasts': (context) => HomePageScreen(
|
||||
cacheable: true,
|
||||
channel: DeezerChannel(target: 'channels/podcasts'),
|
||||
title: 'Podcasts'.i18n,
|
||||
),
|
||||
'/library': (context) => LibraryScreen(),
|
||||
'/library/tracks': (context) => LibraryTracks(),
|
||||
'/library/albums': (context) => LibraryAlbums(),
|
||||
'/library/artists': (context) => LibraryArtists(),
|
||||
'/library/playlists': (context) => LibraryPlaylists(),
|
||||
'/library/history': (context) => HistoryScreen(),
|
||||
'/search': (context) => SearchScreen(),
|
||||
'/settings': (context) => SettingsScreen(),
|
||||
'/downloads': (context) => DownloadsScreen(),
|
||||
'/library': (context) => const LibraryScreen(),
|
||||
'/library/tracks': (context) => const LibraryTracks(),
|
||||
'/library/albums': (context) => const LibraryAlbums(),
|
||||
'/library/artists': (context) => const LibraryArtists(),
|
||||
'/library/playlists': (context) => const LibraryPlaylists(),
|
||||
'/library/history': (context) => const HistoryScreen(),
|
||||
'/search': (context) => const SearchScreen(),
|
||||
'/settings': (context) => const SettingsScreen(),
|
||||
'/downloads': (context) => const DownloadsScreen(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
|
@ -600,16 +584,30 @@ class _MainRouteNavigator extends StatelessWidget with WidgetsBindingObserver {
|
|||
);
|
||||
}
|
||||
|
||||
Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
|
||||
final routeBuilder = routes[settings.name];
|
||||
Route<T>? _onGenerateRoute<T>(RouteSettings s) {
|
||||
final routeBuilder = routes[s.name];
|
||||
if (routeBuilder == null) return null;
|
||||
return ScaleFadePageRoute(builder: routeBuilder, settings: settings);
|
||||
if (!navigatorKey.currentState!.canPop())
|
||||
return ScaleFadePageRoute<T>(builder: routeBuilder, settings: s);
|
||||
|
||||
switch (settings.navigatorRouteType) {
|
||||
case NavigatorRouteType.blur_slide:
|
||||
return BlurSlidePageRoute<T>(builder: routeBuilder, settings: s);
|
||||
case NavigatorRouteType.material:
|
||||
return MaterialPageRoute<T>(builder: routeBuilder, settings: s);
|
||||
case NavigatorRouteType.cupertino:
|
||||
return CupertinoPageRoute<T>(builder: routeBuilder, settings: s);
|
||||
case NavigatorRouteType.fade:
|
||||
return FadePageRoute<T>(builder: routeBuilder, settings: s);
|
||||
case NavigatorRouteType.fade_blur:
|
||||
return FadePageRoute<T>(builder: routeBuilder, settings: s, blur: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// class FreezerDrawer extends StatelessWidget {
|
||||
// const FreezerDrawer({Key? key}) : super(key: key);
|
||||
//
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Drawer(
|
||||
|
@ -651,7 +649,7 @@ class _MainRouteNavigator extends StatelessWidget with WidgetsBindingObserver {
|
|||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// class FreezerDrawerTile extends StatelessWidget {
|
||||
// final Widget? icon;
|
||||
// final String title;
|
||||
|
@ -659,7 +657,7 @@ class _MainRouteNavigator extends StatelessWidget with WidgetsBindingObserver {
|
|||
// const FreezerDrawerTile(
|
||||
// {Key? key, this.icon, required this.title, required this.route})
|
||||
// : super(key: key);
|
||||
//
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// print(route);
|
||||
|
|
|
@ -15,9 +15,12 @@ class BlurSlidePageRoute<T> extends BasicPageRoute<T> {
|
|||
this.animationCurve = Curves.linearToEaseOut,
|
||||
transitionDuration = const Duration(milliseconds: 300),
|
||||
maintainState = true,
|
||||
RouteSettings? settings,
|
||||
}) : super(
|
||||
transitionDuration: transitionDuration,
|
||||
maintainState: maintainState);
|
||||
transitionDuration: transitionDuration,
|
||||
maintainState: maintainState,
|
||||
settings: settings,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation,
|
||||
|
|
|
@ -10,9 +10,12 @@ class FadePageRoute<T> extends BasicPageRoute<T> {
|
|||
this.blur = false,
|
||||
transitionDuration = const Duration(milliseconds: 300),
|
||||
maintainState = true,
|
||||
RouteSettings? settings,
|
||||
}) : super(
|
||||
transitionDuration: transitionDuration,
|
||||
maintainState: maintainState);
|
||||
transitionDuration: transitionDuration,
|
||||
maintainState: maintainState,
|
||||
settings: settings,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation,
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'package:google_fonts/google_fonts.dart';
|
|||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
|
@ -249,25 +248,8 @@ class Settings {
|
|||
|
||||
Future<void> updateAudioServiceQuality() async {
|
||||
//Send wifi & mobile quality to audio service isolate
|
||||
await audioHandler.customAction('updateQuality', {
|
||||
'mobileQuality': getQualityInt(mobileQuality),
|
||||
'wifiQuality': getQualityInt(wifiQuality)
|
||||
});
|
||||
}
|
||||
|
||||
//AudioQuality to deezer int
|
||||
int getQualityInt(AudioQuality? q) {
|
||||
switch (q) {
|
||||
case AudioQuality.MP3_128:
|
||||
return 1;
|
||||
case AudioQuality.MP3_320:
|
||||
return 3;
|
||||
case AudioQuality.FLAC:
|
||||
return 9;
|
||||
//Deezer default
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
await audioHandler.customAction('updateQuality',
|
||||
{'mobileQuality': mobileQuality, 'wifiQuality': wifiQuality});
|
||||
}
|
||||
|
||||
// MaterialColor get _primarySwatch =>
|
||||
|
@ -389,6 +371,17 @@ enum AudioQuality {
|
|||
ASK
|
||||
}
|
||||
|
||||
extension ToDeezerInt on AudioQuality {
|
||||
int toDeezerQualityInt() {
|
||||
return const {
|
||||
AudioQuality.MP3_128: 1,
|
||||
AudioQuality.MP3_320: 3,
|
||||
AudioQuality.FLAC: 9,
|
||||
}[this] ??
|
||||
8;
|
||||
}
|
||||
}
|
||||
|
||||
@HiveType(typeId: 28)
|
||||
enum Themes {
|
||||
@HiveField(0)
|
||||
|
|
|
@ -55,7 +55,6 @@ class SettingsAdapter extends TypeAdapter<Settings> {
|
|||
? NavigatorRouteType.material
|
||||
: fields[33] as NavigatorRouteType
|
||||
..primaryColor = fields[34] == null ? Colors.blue : fields[34] as Color
|
||||
..materialYouAccent = fields[45] == null ? false : fields[45] as bool
|
||||
..useArtColor = fields[35] as bool
|
||||
..deezerLanguage = fields[36] as String
|
||||
..deezerCountry = fields[37] as String
|
||||
|
@ -65,13 +64,16 @@ class SettingsAdapter extends TypeAdapter<Settings> {
|
|||
..lastFMPassword = fields[41] as String?
|
||||
..spotifyClientId = fields[42] as String?
|
||||
..spotifyClientSecret = fields[43] as String?
|
||||
..spotifyCredentials = fields[44] as SpotifyCredentialsSave?;
|
||||
..spotifyCredentials = fields[44] as SpotifyCredentialsSave?
|
||||
..materialYouAccent = fields[45] == null ? false : fields[45] as bool
|
||||
..playerAlbumArtDropShadow =
|
||||
fields[46] == null ? true : fields[46] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Settings obj) {
|
||||
writer
|
||||
..writeByte(46)
|
||||
..writeByte(47)
|
||||
..writeByte(0)
|
||||
..write(obj.language)
|
||||
..writeByte(1)
|
||||
|
@ -142,8 +144,6 @@ class SettingsAdapter extends TypeAdapter<Settings> {
|
|||
..write(obj.navigatorRouteType)
|
||||
..writeByte(34)
|
||||
..write(obj.primaryColor)
|
||||
..writeByte(45)
|
||||
..write(obj.materialYouAccent)
|
||||
..writeByte(35)
|
||||
..write(obj.useArtColor)
|
||||
..writeByte(36)
|
||||
|
@ -163,7 +163,11 @@ class SettingsAdapter extends TypeAdapter<Settings> {
|
|||
..writeByte(43)
|
||||
..write(obj.spotifyClientSecret)
|
||||
..writeByte(44)
|
||||
..write(obj.spotifyCredentials);
|
||||
..write(obj.spotifyCredentials)
|
||||
..writeByte(45)
|
||||
..write(obj.materialYouAccent)
|
||||
..writeByte(46)
|
||||
..write(obj.playerAlbumArtDropShadow);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -362,7 +366,6 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings()
|
|||
..navigatorRouteType =
|
||||
$enumDecode(_$NavigatorRouteTypeEnumMap, json['navigatorRouteType'])
|
||||
..primaryColor = Settings._colorFromJson(json['primaryColor'] as int?)
|
||||
..materialYouAccent = json['materialYouAccent'] as bool
|
||||
..useArtColor = json['useArtColor'] as bool
|
||||
..deezerLanguage = json['deezerLanguage'] as String
|
||||
..deezerCountry = json['deezerCountry'] as String
|
||||
|
@ -375,7 +378,9 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings()
|
|||
..spotifyCredentials = json['spotifyCredentials'] == null
|
||||
? null
|
||||
: SpotifyCredentialsSave.fromJson(
|
||||
json['spotifyCredentials'] as Map<String, dynamic>);
|
||||
json['spotifyCredentials'] as Map<String, dynamic>)
|
||||
..materialYouAccent = json['materialYouAccent'] as bool
|
||||
..playerAlbumArtDropShadow = json['playerAlbumArtDropShadow'] as bool;
|
||||
|
||||
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||
'language': instance.language,
|
||||
|
@ -414,7 +419,6 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
|||
'navigatorRouteType':
|
||||
_$NavigatorRouteTypeEnumMap[instance.navigatorRouteType]!,
|
||||
'primaryColor': Settings._colorToJson(instance.primaryColor),
|
||||
'materialYouAccent': instance.materialYouAccent,
|
||||
'useArtColor': instance.useArtColor,
|
||||
'deezerLanguage': instance.deezerLanguage,
|
||||
'deezerCountry': instance.deezerCountry,
|
||||
|
@ -425,6 +429,8 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
|||
'spotifyClientId': instance.spotifyClientId,
|
||||
'spotifyClientSecret': instance.spotifyClientSecret,
|
||||
'spotifyCredentials': instance.spotifyCredentials,
|
||||
'materialYouAccent': instance.materialYouAccent,
|
||||
'playerAlbumArtDropShadow': instance.playerAlbumArtDropShadow,
|
||||
};
|
||||
|
||||
const _$AudioQualityEnumMap = {
|
||||
|
|
|
@ -4,15 +4,14 @@ import 'package:freezer/api/definitions.dart';
|
|||
import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
|
||||
final androidAuto = AndroidAuto._();
|
||||
|
||||
class AndroidAuto {
|
||||
AndroidAuto._();
|
||||
final DeezerAPI deezerAPI;
|
||||
AndroidAuto({required this.deezerAPI});
|
||||
//Prefix for "playable" MediaItem
|
||||
static const prefix = '_aa_';
|
||||
|
||||
//Get media items for parent id
|
||||
Future<List<MediaItem>> getScreen(String? parentId) async {
|
||||
Future<List<MediaItem>> getScreen(String parentId) async {
|
||||
print(parentId);
|
||||
|
||||
//Homescreen
|
||||
|
@ -92,7 +91,7 @@ class AndroidAuto {
|
|||
if (parentId == 'homescreen') {
|
||||
HomePage hp = await deezerAPI.homePage();
|
||||
List<MediaItem> out = [];
|
||||
for (HomePageSection section in hp.sections!) {
|
||||
for (HomePageSection section in hp.sections) {
|
||||
for (int i = 0; i < section.items!.length; i++) {
|
||||
//Limit to max 5 items
|
||||
if (i == 5) break;
|
||||
|
@ -154,7 +153,7 @@ class AndroidAuto {
|
|||
}
|
||||
|
||||
//Load virtual mediaItem
|
||||
Future playItem(String? id) async {
|
||||
Future<void> playItem(String id) async {
|
||||
print(id);
|
||||
|
||||
//Play flow
|
||||
|
|
|
@ -11,6 +11,8 @@ import 'cached_image.dart';
|
|||
import 'dart:async';
|
||||
|
||||
class DownloadsScreen extends StatefulWidget {
|
||||
const DownloadsScreen();
|
||||
|
||||
@override
|
||||
_DownloadsScreenState createState() => _DownloadsScreenState();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'package:freezer/ui/menu.dart';
|
|||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'tiles.dart';
|
||||
import 'details_screens.dart';
|
||||
import '../settings.dart';
|
||||
|
||||
class _SearchHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
@override
|
||||
|
@ -38,6 +37,8 @@ class _SearchHeaderDelegate extends SliverPersistentHeaderDelegate {
|
|||
}
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
|
@ -79,19 +80,46 @@ class FreezerTitle extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class HomePageScreen extends StatefulWidget {
|
||||
class HomePageScreen extends StatelessWidget {
|
||||
final String title;
|
||||
final HomePage? homePage;
|
||||
final bool cacheable;
|
||||
final DeezerChannel channel;
|
||||
|
||||
const HomePageScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.channel,
|
||||
this.homePage,
|
||||
this.cacheable = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(title)),
|
||||
body: HomePageWidget(
|
||||
homePage: homePage,
|
||||
channel: channel,
|
||||
cacheable: cacheable,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomePageWidget extends StatefulWidget {
|
||||
final HomePage? homePage;
|
||||
final bool cacheable;
|
||||
final DeezerChannel? channel;
|
||||
HomePageScreen(
|
||||
HomePageWidget(
|
||||
{this.homePage, this.channel, this.cacheable = false, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_HomePageScreenState createState() => _HomePageScreenState();
|
||||
_HomePageWidgetState createState() => _HomePageWidgetState();
|
||||
}
|
||||
|
||||
class _HomePageScreenState extends State<HomePageScreen> {
|
||||
class _HomePageWidgetState extends State<HomePageWidget> {
|
||||
HomePage? _homePage;
|
||||
bool _error = false;
|
||||
bool _loadExplicitlyRequested = false;
|
||||
|
@ -115,7 +143,7 @@ class _HomePageScreenState extends State<HomePageScreen> {
|
|||
setState(() => _error = true);
|
||||
return;
|
||||
}
|
||||
if (_hp!.sections.isEmpty) return;
|
||||
if (_hp.sections.isEmpty) return;
|
||||
if (widget.cacheable) _hp.save(widget.channel?.target ?? '');
|
||||
setState(() => _homePage = _hp!);
|
||||
}
|
||||
|
@ -161,7 +189,7 @@ class _HomePageScreenState extends State<HomePageScreen> {
|
|||
if (_error) return ErrorScreen();
|
||||
List<HomePageSection>? sections;
|
||||
if (_homePage != null) {
|
||||
sections = _homePage!.sections!;
|
||||
sections = _homePage!.sections;
|
||||
}
|
||||
return RefreshIndicator(
|
||||
key: _indicatorKey,
|
||||
|
@ -214,13 +242,11 @@ class HomepageRowSection extends StatelessWidget {
|
|||
style: TextStyle(fontSize: 20.0),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pushRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(title: Text(section.title!)),
|
||||
body: HomePageScreen(
|
||||
channel:
|
||||
DeezerChannel(target: section.pagePath),
|
||||
),
|
||||
)),
|
||||
builder: (context) => HomePageScreen(
|
||||
title: section.title!,
|
||||
channel: DeezerChannel(target: section.pagePath),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
|
@ -324,10 +350,11 @@ class HomePageItemWidget extends StatelessWidget {
|
|||
item.value,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(title: Text(item.value.title.toString())),
|
||||
body: HomePageScreen(channel: item.value),
|
||||
));
|
||||
builder: (context) => HomePageScreen(
|
||||
channel: item.value,
|
||||
title: item.value.title.toString(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
case HomePageItemType.SHOW:
|
||||
|
|
|
@ -34,20 +34,14 @@ class LibraryAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
Icons.file_download,
|
||||
semanticLabel: "Download".i18n,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pushRoute(builder: (context) => DownloadsScreen());
|
||||
},
|
||||
onPressed: () => Navigator.pushNamed(context, '/downloads'),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.settings,
|
||||
semanticLabel: "Settings".i18n,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pushRoute(builder: (context) => SettingsScreen());
|
||||
},
|
||||
onPressed: () => Navigator.pushNamed(context, '/settings'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -55,6 +49,8 @@ class LibraryAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
}
|
||||
|
||||
class LibraryScreen extends StatelessWidget {
|
||||
const LibraryScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -178,7 +174,7 @@ class LibraryScreen extends StatelessWidget {
|
|||
children: <Widget>[CircularProgressIndicator()],
|
||||
),
|
||||
);
|
||||
List<String> data = snapshot.data! as List<String>;
|
||||
List<String> data = snapshot.data!;
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
|
@ -219,6 +215,8 @@ class LibraryScreen extends StatelessWidget {
|
|||
}
|
||||
|
||||
class LibraryTracks extends StatefulWidget {
|
||||
const LibraryTracks();
|
||||
|
||||
@override
|
||||
_LibraryTracksState createState() => _LibraryTracksState();
|
||||
}
|
||||
|
@ -535,6 +533,8 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
}
|
||||
|
||||
class LibraryAlbums extends StatefulWidget {
|
||||
const LibraryAlbums();
|
||||
|
||||
@override
|
||||
_LibraryAlbumsState createState() => _LibraryAlbumsState();
|
||||
}
|
||||
|
@ -685,7 +685,7 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError ||
|
||||
!snapshot.hasData ||
|
||||
(snapshot.data! as List).isEmpty)
|
||||
(snapshot.data!).isEmpty)
|
||||
return Container(
|
||||
height: 0,
|
||||
width: 0,
|
||||
|
@ -731,6 +731,8 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
}
|
||||
|
||||
class LibraryArtists extends StatefulWidget {
|
||||
const LibraryArtists();
|
||||
|
||||
@override
|
||||
_LibraryArtistsState createState() => _LibraryArtistsState();
|
||||
}
|
||||
|
@ -886,6 +888,8 @@ class _LibraryArtistsState extends State<LibraryArtists> {
|
|||
}
|
||||
|
||||
class LibraryPlaylists extends StatefulWidget {
|
||||
const LibraryPlaylists();
|
||||
|
||||
@override
|
||||
_LibraryPlaylistsState createState() => _LibraryPlaylistsState();
|
||||
}
|
||||
|
@ -1137,6 +1141,8 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
}
|
||||
|
||||
class HistoryScreen extends StatefulWidget {
|
||||
const HistoryScreen();
|
||||
|
||||
@override
|
||||
_HistoryScreenState createState() => _HistoryScreenState();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/player.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ class MenuSheet {
|
|||
Widget offlineTrack(Track track) => FutureBuilder(
|
||||
future: downloadManager.checkOffline(track: track),
|
||||
builder: (context, snapshot) {
|
||||
bool isOffline = (snapshot.data as bool?) ?? (track.offline ?? false);
|
||||
bool isOffline = snapshot.data ?? (track.offline ?? false);
|
||||
return ListTile(
|
||||
title: Text(isOffline ? 'Remove offline'.i18n : 'Offline'.i18n),
|
||||
leading: Icon(Icons.offline_pin),
|
||||
|
@ -747,7 +747,7 @@ class _SelectPlaylistDialogState extends State<SelectPlaylistDialog> {
|
|||
),
|
||||
);
|
||||
|
||||
List<Playlist> playlists = snapshot.data! as List<Playlist>;
|
||||
List<Playlist> playlists = snapshot.data!;
|
||||
return SingleChildScrollView(
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
...List.generate(
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:async';
|
|||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/page_routes/fade.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
@ -12,6 +11,185 @@ import '../api/player.dart';
|
|||
import 'cached_image.dart';
|
||||
import 'player_screen.dart';
|
||||
|
||||
class FancyScaffold extends StatefulWidget {
|
||||
final Widget bottomPanel;
|
||||
final double bottomPanelHeight;
|
||||
final Widget expandedPanel;
|
||||
final Widget bottomNavigationBar;
|
||||
final Widget body;
|
||||
|
||||
const FancyScaffold({
|
||||
required this.bottomPanel,
|
||||
required this.bottomPanelHeight,
|
||||
required this.expandedPanel,
|
||||
required this.bottomNavigationBar,
|
||||
required this.body,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<FancyScaffold> createState() => _FancyScaffoldState();
|
||||
}
|
||||
|
||||
class _FancyScaffoldState extends State<FancyScaffold>
|
||||
with TickerProviderStateMixin {
|
||||
// goes from 0 to 1 (double)
|
||||
// 0 = preview, 1 = expanded
|
||||
late final AnimationController _dragController;
|
||||
final _status = ValueNotifier<AnimationStatus>(AnimationStatus.dismissed);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_dragController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 500));
|
||||
_dragController.addStatusListener((status) => _status.value = status);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_dragController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final systemPadding = MediaQuery.of(context).viewPadding;
|
||||
final defaultBottomPadding = 80.0 + systemPadding.bottom;
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
print('height: $screenHeight, padding: $systemPadding');
|
||||
final _sizeAnimation = Tween<double>(
|
||||
begin: widget.bottomPanelHeight / MediaQuery.of(context).size.height,
|
||||
end: 1.0,
|
||||
).animate(_dragController);
|
||||
print('route: ' + (ModalRoute.of(context) == null ? 'no' : 'yes'));
|
||||
return WillPopScope(
|
||||
onWillPop: () {
|
||||
print('BACK PRESSED! ${_dragController.value}');
|
||||
if (_status.value == AnimationStatus.completed ||
|
||||
_status.value == AnimationStatus.reverse) {
|
||||
print('flinging without popping!');
|
||||
_dragController.fling(velocity: -1.0);
|
||||
return Future.value(false);
|
||||
}
|
||||
|
||||
print('fuck this, no');
|
||||
|
||||
return Future.value(true);
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Scaffold(
|
||||
body: widget.body,
|
||||
bottomNavigationBar: Column(
|
||||
children: [
|
||||
SizedBox(height: widget.bottomPanelHeight),
|
||||
SizeTransition(
|
||||
axisAlignment: -1.0,
|
||||
sizeFactor:
|
||||
Tween(begin: 1.0, end: 0.0).animate(_sizeAnimation),
|
||||
child: widget.bottomNavigationBar,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: AnimatedBuilder(
|
||||
animation: _sizeAnimation,
|
||||
builder: (context, child) {
|
||||
final x = 1.0 - _sizeAnimation.value;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: (defaultBottomPadding /*+ 8.0*/) * x,
|
||||
//right: 8.0 * x,
|
||||
//left: 8.0 * x,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _status,
|
||||
builder: (context, state, child) {
|
||||
return GestureDetector(
|
||||
onVerticalDragEnd: _onVerticalDragEnd,
|
||||
onVerticalDragUpdate: _onVerticalDragUpdate,
|
||||
onTap: state == AnimationStatus.dismissed ? _onTap : null,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: SizeTransition(
|
||||
sizeFactor: _sizeAnimation,
|
||||
axisAlignment: -1.0,
|
||||
axis: Axis.vertical,
|
||||
child: SizedBox(
|
||||
height: screenHeight,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _status,
|
||||
builder: (context, state, _) => Stack(
|
||||
children: [
|
||||
if (state != AnimationStatus.dismissed)
|
||||
Positioned.fill(
|
||||
child: widget.expandedPanel,
|
||||
key: Key('player_screen'),
|
||||
),
|
||||
if (state != AnimationStatus.completed)
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: FadeTransition(
|
||||
opacity: Tween(begin: 1.0, end: 0.0)
|
||||
.animate(_dragController),
|
||||
child: SizedBox(
|
||||
height: widget.bottomPanelHeight,
|
||||
child: widget.bottomPanel),
|
||||
),
|
||||
key: Key('player_bar'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onTap() {
|
||||
_dragController.fling();
|
||||
}
|
||||
|
||||
void _onVerticalDragUpdate(DragUpdateDetails details) {
|
||||
_dragController.value -=
|
||||
details.delta.dy / MediaQuery.of(context).size.height;
|
||||
}
|
||||
|
||||
void _onVerticalDragEnd(DragEndDetails details) {
|
||||
// snap widget to size
|
||||
// this should be also handled by drag velocity and not only with bare size.
|
||||
|
||||
const double minFlingVelocity = 365.0;
|
||||
|
||||
if (details.velocity.pixelsPerSecond.dy.abs() > minFlingVelocity) {
|
||||
_dragController.fling(
|
||||
velocity: -details.velocity.pixelsPerSecond.dy /
|
||||
MediaQuery.of(context).size.height);
|
||||
return;
|
||||
}
|
||||
|
||||
_dragController.fling(velocity: _dragController.value > 0.5 ? 1.0 : -1.0);
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerBar extends StatefulWidget {
|
||||
final bool shouldHandleClicks;
|
||||
final bool shouldHaveHero;
|
||||
|
@ -67,104 +245,107 @@ class _PlayerBarState extends State<PlayerBar> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _isNothingPlaying
|
||||
? const SizedBox()
|
||||
: GestureDetector(
|
||||
onHorizontalDragUpdate: (details) async {
|
||||
if (_gestureRegistered) return;
|
||||
final double sensitivity = 12.69;
|
||||
//Right swipe
|
||||
_gestureRegistered = true;
|
||||
if (details.delta.dx > sensitivity) {
|
||||
await audioHandler.skipToPrevious();
|
||||
}
|
||||
//Left
|
||||
if (details.delta.dx < -sensitivity) {
|
||||
await audioHandler.skipToNext();
|
||||
}
|
||||
_gestureRegistered = false;
|
||||
return;
|
||||
},
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
StreamBuilder<MediaItem?>(
|
||||
stream: audioHandler.mediaItem,
|
||||
initialData: audioHandler.mediaItem.valueOrNull,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return const SizedBox();
|
||||
final currentMediaItem = snapshot.data!;
|
||||
final image = CachedImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
url: currentMediaItem.extras!['thumb'] ??
|
||||
currentMediaItem.artUri.toString(),
|
||||
);
|
||||
final leadingWidget = widget.shouldHaveHero
|
||||
? Hero(tag: currentMediaItem.id, child: image)
|
||||
: image;
|
||||
return Material(
|
||||
// For Android TV: indicate focus by grey
|
||||
color: focusNode.hasFocus
|
||||
? Color.lerp(backgroundColor, Colors.grey, 0.26)
|
||||
: backgroundColor,
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
focusNode: focusNode,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8.0),
|
||||
onTap: widget.shouldHandleClicks
|
||||
? _pushPlayerScreen
|
||||
: null,
|
||||
leading: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: leadingWidget),
|
||||
title: Text(
|
||||
currentMediaItem.displayTitle!,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Text(
|
||||
currentMediaItem.displaySubtitle ?? '',
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: IconTheme(
|
||||
data: IconThemeData(
|
||||
color: settings.isDark
|
||||
? Colors.white
|
||||
: Colors.grey[600]),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
PrevNextButton(
|
||||
iconSize,
|
||||
prev: true,
|
||||
return SizedBox(
|
||||
height: 68.0,
|
||||
child: _isNothingPlaying
|
||||
? null
|
||||
: GestureDetector(
|
||||
onHorizontalDragUpdate: (details) async {
|
||||
if (_gestureRegistered) return;
|
||||
final double sensitivity = 12.69;
|
||||
//Right swipe
|
||||
_gestureRegistered = true;
|
||||
if (details.delta.dx > sensitivity) {
|
||||
await audioHandler.skipToPrevious();
|
||||
}
|
||||
//Left
|
||||
if (details.delta.dx < -sensitivity) {
|
||||
await audioHandler.skipToNext();
|
||||
}
|
||||
_gestureRegistered = false;
|
||||
return;
|
||||
},
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Expanded(
|
||||
child: StreamBuilder<MediaItem?>(
|
||||
stream: audioHandler.mediaItem,
|
||||
initialData: audioHandler.mediaItem.valueOrNull,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return const SizedBox();
|
||||
final currentMediaItem = snapshot.data!;
|
||||
final image = CachedImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
url: currentMediaItem.extras!['thumb'] ??
|
||||
currentMediaItem.artUri.toString(),
|
||||
);
|
||||
final leadingWidget = widget.shouldHaveHero
|
||||
? Hero(tag: currentMediaItem.id, child: image)
|
||||
: image;
|
||||
return Material(
|
||||
child: ListTile(
|
||||
// For Android TV: indicate focus by grey
|
||||
|
||||
tileColor: focusNode.hasFocus
|
||||
? Color.lerp(
|
||||
backgroundColor, Colors.grey, 0.26)
|
||||
: backgroundColor,
|
||||
dense: true,
|
||||
focusNode: focusNode,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8.0),
|
||||
onTap: widget.shouldHandleClicks
|
||||
? _pushPlayerScreen
|
||||
: null,
|
||||
leading: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: leadingWidget),
|
||||
title: Text(
|
||||
currentMediaItem.displayTitle!,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Text(
|
||||
currentMediaItem.displaySubtitle ?? '',
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: IconTheme(
|
||||
data: IconThemeData(
|
||||
color: settings.isDark
|
||||
? Colors.white
|
||||
: Colors.grey[600]),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
PrevNextButton(
|
||||
iconSize,
|
||||
prev: true,
|
||||
),
|
||||
PlayPauseButton(iconSize),
|
||||
PrevNextButton(iconSize)
|
||||
],
|
||||
),
|
||||
PlayPauseButton(iconSize),
|
||||
PrevNextButton(iconSize)
|
||||
],
|
||||
),
|
||||
)));
|
||||
}),
|
||||
SizedBox(
|
||||
height: 3.0,
|
||||
child: StreamBuilder<Duration>(
|
||||
stream: AudioService.position,
|
||||
builder: (context, snapshot) {
|
||||
return LinearProgressIndicator(
|
||||
value: parsePosition(snapshot.data ?? Duration.zero),
|
||||
);
|
||||
}),
|
||||
),
|
||||
]),
|
||||
);
|
||||
)));
|
||||
}),
|
||||
),
|
||||
SizedBox(
|
||||
height: 3.0,
|
||||
child: StreamBuilder<Duration>(
|
||||
stream: AudioService.position,
|
||||
builder: (context, snapshot) {
|
||||
return LinearProgressIndicator(
|
||||
value: parsePosition(snapshot.data ?? Duration.zero),
|
||||
);
|
||||
}),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _pushPlayerScreen() {
|
||||
final builder = (BuildContext context) => PlayerScreen();
|
||||
if (settings.blurPlayerBackground) {
|
||||
Navigator.of(context).push(FadePageRoute(builder: builder));
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pushRoute(builder: builder);
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +473,7 @@ class _PlayPauseButtonState extends State<PlayPauseButton>
|
|||
iconSize: widget.size,
|
||||
onPressed: _playPause);
|
||||
child = InkWell(
|
||||
customBorder: CircleBorder(),
|
||||
customBorder: const CircleBorder(),
|
||||
child: IconTheme.merge(
|
||||
child: Center(child: icon),
|
||||
data: IconThemeData(
|
||||
|
@ -313,11 +494,16 @@ class _PlayPauseButtonState extends State<PlayPauseButton>
|
|||
if (widget.filled)
|
||||
return SizedBox.square(
|
||||
dimension: widget.size,
|
||||
child: Card(
|
||||
color: widget.color,
|
||||
elevation: 2.0,
|
||||
shape: CircleBorder(),
|
||||
child: child));
|
||||
child: Material(
|
||||
type: MaterialType.canvas,
|
||||
shape: const CircleBorder(),
|
||||
elevation: 2.0,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(seconds: 1),
|
||||
decoration:
|
||||
BoxDecoration(shape: BoxShape.circle, color: widget.color),
|
||||
child: child),
|
||||
));
|
||||
else
|
||||
return SizedBox.square(dimension: widget.size, child: child);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
import 'dart:ui';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/page_routes/fade.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
|
@ -21,14 +18,12 @@ import 'package:freezer/ui/menu.dart';
|
|||
import 'package:freezer/ui/player_bar.dart';
|
||||
import 'package:freezer/ui/queue_screen.dart';
|
||||
import 'package:freezer/ui/settings_screen.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:marquee/marquee.dart';
|
||||
import 'package:palette_generator/palette_generator.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
//Changing item in queue view and pressing back causes the pageView to skip song
|
||||
bool pageViewLock = false;
|
||||
|
||||
const _blurStrength = 90.0;
|
||||
|
||||
/// A simple [ChangeNotifier] that listens to the [AudioHandler.mediaItem] stream and
|
||||
|
@ -38,12 +33,13 @@ class BackgroundProvider extends ChangeNotifier {
|
|||
Color? _dominantColor;
|
||||
ImageProvider? _imageProvider;
|
||||
StreamSubscription? _mediaItemSub;
|
||||
bool _isDisposed = false;
|
||||
BackgroundProvider();
|
||||
|
||||
/// Calculate background color from [mediaItem]
|
||||
///
|
||||
/// Warning: this function is expensive to call, and should only be called when songs change!
|
||||
Future _updateColor(MediaItem mediaItem) async {
|
||||
Future<void> _updateColor(MediaItem mediaItem) async {
|
||||
if (!settings.colorGradientBackground &&
|
||||
!settings.blurPlayerBackground &&
|
||||
!settings.enableFilledPlayButton &&
|
||||
|
@ -54,7 +50,7 @@ class BackgroundProvider extends ChangeNotifier {
|
|||
_palette = await PaletteGenerator.fromImageProvider(imageProvider);
|
||||
_dominantColor = _palette!.dominantColor!.color;
|
||||
_imageProvider = settings.blurPlayerBackground ? imageProvider : null;
|
||||
notifyListeners();
|
||||
if (!_isDisposed) notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -77,6 +73,7 @@ class BackgroundProvider extends ChangeNotifier {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_isDisposed = true;
|
||||
_mediaItemSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
@ -121,21 +118,25 @@ class PlayerScreenBackground extends StatelessWidget {
|
|||
if (provider.imageProvider != null || settings.colorGradientBackground)
|
||||
Positioned.fill(
|
||||
child: provider.imageProvider != null
|
||||
? ImageFiltered(
|
||||
imageFilter: ImageFilter.blur(
|
||||
sigmaX: _blurStrength,
|
||||
sigmaY: _blurStrength,
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: provider.imageProvider!,
|
||||
fit: BoxFit.cover,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.white
|
||||
.withOpacity(settings.isDark ? 0.5 : 0.8),
|
||||
BlendMode.dstATop),
|
||||
)),
|
||||
? DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.black),
|
||||
child: ImageFiltered(
|
||||
imageFilter: ImageFilter.blur(
|
||||
tileMode: TileMode.decal,
|
||||
sigmaX: _blurStrength,
|
||||
sigmaY: _blurStrength,
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: provider.imageProvider!,
|
||||
fit: BoxFit.cover,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.white
|
||||
.withOpacity(settings.isDark ? 0.55 : 0.75),
|
||||
BlendMode.dstATop),
|
||||
)),
|
||||
),
|
||||
),
|
||||
)
|
||||
: DecoratedBox(
|
||||
|
@ -160,9 +161,7 @@ class PlayerScreenBackground extends StatelessWidget {
|
|||
final hasBackground = enabled &&
|
||||
(settings.blurPlayerBackground || settings.colorGradientBackground);
|
||||
if (!hasBackground) return null;
|
||||
final color = hasBackground
|
||||
? Colors.transparent
|
||||
: Theme.of(context).scaffoldBackgroundColor;
|
||||
final color = Colors.transparent;
|
||||
final brightness = hasBackground
|
||||
? Brightness.light
|
||||
: (ThemeData.estimateBrightnessForColor(color) == Brightness.light
|
||||
|
@ -224,23 +223,7 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
|||
flex: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: settings.playerAlbumArtDropShadow
|
||||
? Consumer<BackgroundProvider>(
|
||||
builder: (context, background, child) => DecoratedBox(
|
||||
decoration: BoxDecoration(boxShadow: [
|
||||
BoxShadow(
|
||||
color: background.dominantColor ??
|
||||
Colors.transparent,
|
||||
spreadRadius: 0.0,
|
||||
blurRadius: 100.0)
|
||||
]),
|
||||
child: child),
|
||||
child: BigAlbumArt(),
|
||||
)
|
||||
: BigAlbumArt(),
|
||||
),
|
||||
child: BigAlbumArt(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 56.0),
|
||||
|
@ -303,23 +286,7 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
|||
),
|
||||
child: PlayerScreenTopRow(),
|
||||
),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: settings.playerAlbumArtDropShadow
|
||||
? Consumer<BackgroundProvider>(
|
||||
builder: (context, background, child) => DecoratedBox(
|
||||
decoration: BoxDecoration(boxShadow: [
|
||||
BoxShadow(
|
||||
color: background.dominantColor ??
|
||||
Colors.transparent,
|
||||
spreadRadius: 0.0,
|
||||
blurRadius: 100.0)
|
||||
]),
|
||||
child: child),
|
||||
child: BigAlbumArt(),
|
||||
)
|
||||
: BigAlbumArt(),
|
||||
),
|
||||
BigAlbumArt(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: PlayerTextSubtext(textSize: 64.sp),
|
||||
|
@ -354,16 +321,17 @@ class PlayerTextSubtext extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
child: currentMediaItem.displayTitle!.length >= 26
|
||||
? Marquee(
|
||||
key: Key(currentMediaItem.displayTitle!),
|
||||
text: currentMediaItem.displayTitle!,
|
||||
style: TextStyle(
|
||||
fontSize: textSize, fontWeight: FontWeight.bold),
|
||||
blankSpace: 32.0,
|
||||
startPadding: 0.0,
|
||||
accelerationDuration: Duration(seconds: 1),
|
||||
pauseAfterRound: Duration(seconds: 2),
|
||||
accelerationDuration: const Duration(seconds: 1),
|
||||
pauseAfterRound: const Duration(seconds: 2),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
showFadingOnlyWhenScrolling: false,
|
||||
fadingEdgeEndFraction: 0.05,
|
||||
fadingEdgeStartFraction: 0.05,
|
||||
)
|
||||
: Text(
|
||||
currentMediaItem.displayTitle!,
|
||||
|
@ -399,9 +367,9 @@ class QualityInfoWidget extends StatelessWidget {
|
|||
String _getQualityStringFromInfo(StreamQualityInfo info) {
|
||||
if (audioHandler.mediaItem.value == null) return '';
|
||||
|
||||
int bitrate = info.quality == Quality.MP3_128
|
||||
int bitrate = info.quality == AudioQuality.MP3_128
|
||||
? 128
|
||||
: info.quality == Quality.MP3_320
|
||||
: info.quality == AudioQuality.MP3_320
|
||||
? 320
|
||||
: info.calculateBitrate(audioHandler.mediaItem.value!.duration!);
|
||||
|
||||
|
@ -597,12 +565,12 @@ class ForwardReplay30Button extends StatelessWidget {
|
|||
if (forward)
|
||||
return IconButton(
|
||||
onPressed: () => _seek(audioHandler.playbackState.value.position +
|
||||
Duration(seconds: 30)),
|
||||
const Duration(seconds: 30)),
|
||||
icon: const Icon(Icons.forward_30));
|
||||
|
||||
return IconButton(
|
||||
onPressed: () => _seek(
|
||||
audioHandler.playbackState.value.position - Duration(seconds: 30)),
|
||||
onPressed: () => _seek(audioHandler.playbackState.value.position -
|
||||
const Duration(seconds: 30)),
|
||||
icon: const Icon(Icons.replay_30));
|
||||
}
|
||||
}
|
||||
|
@ -664,7 +632,7 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
|||
viewportFraction: 1.0,
|
||||
);
|
||||
StreamSubscription? _currentItemSub;
|
||||
bool _animationLock = false;
|
||||
bool _userScroll = false;
|
||||
bool _initiatedByUser = false;
|
||||
|
||||
@override
|
||||
|
@ -677,10 +645,9 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
|||
if (!_pageController.hasClients) return;
|
||||
if (_pageController.page?.toInt() == playerHelper.queueIndex) return;
|
||||
print('animating controller to page');
|
||||
_animationLock = true;
|
||||
|
||||
await _pageController.animateToPage(playerHelper.queueIndex,
|
||||
duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
_animationLock = false;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
@ -693,12 +660,12 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onVerticalDragUpdate: (DragUpdateDetails details) {
|
||||
if (details.delta.dy > 16) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
final child = GestureDetector(
|
||||
// onVerticalDragUpdate: (DragUpdateDetails details) {
|
||||
// if (details.delta.dy > 16) {
|
||||
// Navigator.of(context).pop();
|
||||
// }
|
||||
// },
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
|
@ -718,33 +685,64 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
|||
BoxDecoration(color: Color.fromARGB(0x90, 0, 0, 0))),
|
||||
);
|
||||
})),
|
||||
child: StreamBuilder<List<MediaItem>>(
|
||||
stream: audioHandler.queue,
|
||||
initialData: audioHandler.queue.valueOrNull,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
final queue = snapshot.data!;
|
||||
return PageView.builder(
|
||||
controller: _pageController,
|
||||
onPageChanged: (int index) {
|
||||
print("PAGE CHANGED!!!!");
|
||||
if (pageViewLock || _animationLock) return;
|
||||
_initiatedByUser = true;
|
||||
audioHandler.skipToQueueItem(index);
|
||||
},
|
||||
itemCount: queue.length,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Hero(
|
||||
tag: queue[i].id,
|
||||
child: CachedImage(
|
||||
url: queue[i].artUri.toString(),
|
||||
fullThumb: true,
|
||||
child: NotificationListener(
|
||||
onNotification: (notification) {
|
||||
if (notification is ScrollStartNotification &&
|
||||
notification.dragDetails != null) _userScroll = true;
|
||||
if (notification is UserScrollNotification) _userScroll = true;
|
||||
if (notification is ScrollEndNotification) _userScroll = false;
|
||||
|
||||
return false;
|
||||
},
|
||||
child: StreamBuilder<List<MediaItem>>(
|
||||
stream: audioHandler.queue,
|
||||
initialData: audioHandler.queue.valueOrNull,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
final queue = snapshot.data!;
|
||||
return PageView.builder(
|
||||
controller: _pageController,
|
||||
onPageChanged: (int index) {
|
||||
if (!_userScroll) return;
|
||||
print("PAGE CHANGED!!!!");
|
||||
Logger('BigAlbumArt').info('skipping to media item');
|
||||
if (queue[index].id == audioHandler.mediaItem.value?.id)
|
||||
return;
|
||||
_initiatedByUser = true;
|
||||
audioHandler.skipToQueueItem(index);
|
||||
},
|
||||
itemCount: queue.length,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Hero(
|
||||
tag: queue[i].id,
|
||||
child: CachedImage(
|
||||
url: queue[i].artUri.toString(),
|
||||
fullThumb: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}),
|
||||
));
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: settings.playerAlbumArtDropShadow
|
||||
? Consumer<BackgroundProvider>(
|
||||
builder: (context, background, child) => AnimatedContainer(
|
||||
duration: const Duration(seconds: 1),
|
||||
decoration: BoxDecoration(boxShadow: [
|
||||
BoxShadow(
|
||||
color: background.dominantColor ?? Colors.transparent,
|
||||
spreadRadius: 0.0,
|
||||
blurRadius: 100.0)
|
||||
]),
|
||||
child: child),
|
||||
child: child,
|
||||
)
|
||||
: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -765,19 +763,21 @@ class PlayerScreenTopRow extends StatelessWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
(short ?? false)
|
||||
? (playerHelper.queueSource!.text ?? '')
|
||||
: 'Playing from:'.i18n +
|
||||
' ' +
|
||||
(playerHelper.queueSource?.text ?? ''),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(fontSize: this.textSize ?? ScreenUtil().setSp(38)),
|
||||
if (playerHelper.queueSource != null)
|
||||
Expanded(
|
||||
child: Text(
|
||||
(short ?? false)
|
||||
? (playerHelper.queueSource!.text ?? '')
|
||||
: 'Playing from:'.i18n +
|
||||
' ' +
|
||||
(playerHelper.queueSource?.text ?? ''),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.left,
|
||||
style:
|
||||
TextStyle(fontSize: this.textSize ?? ScreenUtil().setSp(38)),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.menu,
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'package:freezer/api/definitions.dart';
|
|||
import 'package:freezer/api/player.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:freezer/ui/menu.dart';
|
||||
import 'package:freezer/ui/player_screen.dart';
|
||||
import 'package:freezer/ui/tiles.dart';
|
||||
|
||||
class QueueScreen extends StatefulWidget {
|
||||
|
@ -147,10 +146,8 @@ class _QueueScreenState extends State<QueueScreen> {
|
|||
trailing: ReorderableDragStartListener(
|
||||
child: const Icon(Icons.drag_handle), index: index),
|
||||
onTap: () {
|
||||
pageViewLock = true;
|
||||
audioHandler.skipToQueueItem(index).then((value) {
|
||||
Navigator.of(context).pop();
|
||||
pageViewLock = false;
|
||||
});
|
||||
},
|
||||
onHold: () => MenuSheet(context).defaultTrackMenu(track),
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:connectivity_plus/connectivity_plus.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:fluttericon/font_awesome5_icons.dart';
|
||||
import 'package:fluttericon/typicons_icons.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
|
@ -13,7 +12,6 @@ import 'package:freezer/api/player.dart';
|
|||
import 'package:freezer/notifiers/list_notifier.dart';
|
||||
import 'package:freezer/ui/details_screens.dart';
|
||||
import 'package:freezer/ui/elements.dart';
|
||||
import 'package:freezer/ui/home_screen.dart';
|
||||
import 'package:freezer/ui/menu.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:fluttericon/font_awesome5_icons.dart';
|
||||
import 'package:fluttericon/web_symbols_icons.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
@ -27,6 +26,8 @@ import 'package:freezer/settings.dart';
|
|||
import 'package:freezer/main.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
const SettingsScreen();
|
||||
|
||||
@override
|
||||
_SettingsScreenState createState() => _SettingsScreenState();
|
||||
}
|
||||
|
@ -594,7 +595,9 @@ class _QualityPickerState extends State<QualityPicker> {
|
|||
}
|
||||
|
||||
//Update quality in settings
|
||||
void _updateQuality(AudioQuality q) async {
|
||||
void _updateQuality(AudioQuality? q) async {
|
||||
if (q == null) return;
|
||||
|
||||
setState(() {
|
||||
_quality = q;
|
||||
});
|
||||
|
@ -626,26 +629,26 @@ class _QualityPickerState extends State<QualityPicker> {
|
|||
title: Text('MP3 128kbps'),
|
||||
groupValue: _quality,
|
||||
value: AudioQuality.MP3_128,
|
||||
onChanged: (dynamic q) => _updateQuality(q),
|
||||
onChanged: (q) => _updateQuality(q),
|
||||
),
|
||||
RadioListTile(
|
||||
title: Text('MP3 320kbps'),
|
||||
groupValue: _quality,
|
||||
value: AudioQuality.MP3_320,
|
||||
onChanged: (dynamic q) => _updateQuality(q),
|
||||
onChanged: (q) => _updateQuality(q),
|
||||
),
|
||||
RadioListTile(
|
||||
title: Text('FLAC'),
|
||||
groupValue: _quality,
|
||||
value: AudioQuality.FLAC,
|
||||
onChanged: (dynamic q) => _updateQuality(q),
|
||||
onChanged: (q) => _updateQuality(q),
|
||||
),
|
||||
if (widget.field == 'download')
|
||||
RadioListTile(
|
||||
title: Text('Ask before downloading'.i18n),
|
||||
groupValue: _quality,
|
||||
value: AudioQuality.ASK,
|
||||
onChanged: (dynamic q) => _updateQuality(q),
|
||||
onChanged: (q) => _updateQuality(q),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
@ -515,7 +515,7 @@ class AlbumCard extends StatelessWidget {
|
|||
CachedImage(
|
||||
width: 128.0,
|
||||
height: 128.0,
|
||||
url: album!.art!.thumb,
|
||||
url: album.art!.thumb,
|
||||
rounded: true),
|
||||
Positioned(
|
||||
child: PlayItemButton(
|
||||
|
@ -533,7 +533,7 @@ class AlbumCard extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: 144.0,
|
||||
child: Text(
|
||||
album!.title!,
|
||||
album.title!,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
@ -544,7 +544,7 @@ class AlbumCard extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: 144.0,
|
||||
child: Text(
|
||||
album!.artistString,
|
||||
album.artistString,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
flutter/ephemeral
|
|
@ -0,0 +1,139 @@
|
|||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "freezer")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "f.f.freezer")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
# Load bundled libraries from the lib/ directory relative to the binary.
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||
|
||||
# Root filesystem for cross-building.
|
||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
||||
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
# Define build configuration options.
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
#
|
||||
# Be cautious about adding new options here, as plugins use this function by
|
||||
# default. In most cases, you should add new options to specific targets instead
|
||||
# of modifying this function.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_14)
|
||||
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
|
||||
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
|
||||
endfunction()
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
|
||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME above,
|
||||
# not the value here, or `flutter run` will no longer work.
|
||||
#
|
||||
# Any new source files that you add to the application should be added here.
|
||||
add_executable(${BINARY_NAME}
|
||||
"main.cc"
|
||||
"my_application.cc"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
)
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Add dependency libraries. Add any application-specific dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
|
||||
# Only the install-generated bundle's copy of the executable will launch
|
||||
# correctly, since the resources must in the right relative locations. To avoid
|
||||
# people trying to run the unbundled copy, put it in a subdirectory instead of
|
||||
# the default top-level location.
|
||||
set_target_properties(${BINARY_NAME}
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
|
||||
)
|
||||
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# By default, "installing" just makes a relocatable bundle in the build
|
||||
# directory.
|
||||
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
# Start with a clean build bundle directory every time.
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
|
@ -0,0 +1,88 @@
|
|||
# This file controls Flutter-level build steps. It should not be edited.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
|
||||
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
|
||||
# which isn't available in 3.10.
|
||||
function(list_prepend LIST_NAME PREFIX)
|
||||
set(NEW_LIST "")
|
||||
foreach(element ${${LIST_NAME}})
|
||||
list(APPEND NEW_LIST "${PREFIX}${element}")
|
||||
endforeach(element)
|
||||
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# === Flutter Library ===
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
||||
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"fl_basic_message_channel.h"
|
||||
"fl_binary_codec.h"
|
||||
"fl_binary_messenger.h"
|
||||
"fl_dart_project.h"
|
||||
"fl_engine.h"
|
||||
"fl_json_message_codec.h"
|
||||
"fl_json_method_codec.h"
|
||||
"fl_message_codec.h"
|
||||
"fl_method_call.h"
|
||||
"fl_method_channel.h"
|
||||
"fl_method_codec.h"
|
||||
"fl_method_response.h"
|
||||
"fl_plugin_registrar.h"
|
||||
"fl_plugin_registry.h"
|
||||
"fl_standard_message_codec.h"
|
||||
"fl_standard_method_codec.h"
|
||||
"fl_string_codec.h"
|
||||
"fl_value.h"
|
||||
"fl_view.h"
|
||||
"flutter_linux.h"
|
||||
)
|
||||
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
|
||||
target_link_libraries(flutter INTERFACE
|
||||
PkgConfig::GTK
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GIO
|
||||
)
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/_phony_
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
|
||||
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void fl_register_plugins(FlPluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
|
@ -0,0 +1,6 @@
|
|||
#include "my_application.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
g_autoptr(MyApplication) app = my_application_new();
|
||||
return g_application_run(G_APPLICATION(app), argc, argv);
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#include "my_application.h"
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
struct _MyApplication {
|
||||
GtkApplication parent_instance;
|
||||
char** dart_entrypoint_arguments;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||
|
||||
// Implements GApplication::activate.
|
||||
static void my_application_activate(GApplication* application) {
|
||||
MyApplication* self = MY_APPLICATION(application);
|
||||
GtkWindow* window =
|
||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||
|
||||
// Use a header bar when running in GNOME as this is the common style used
|
||||
// by applications and is the setup most users will be using (e.g. Ubuntu
|
||||
// desktop).
|
||||
// If running on X and not using GNOME then just use a traditional title bar
|
||||
// in case the window manager does more exotic layout, e.g. tiling.
|
||||
// If running on Wayland assume the header bar will work (may need changing
|
||||
// if future cases occur).
|
||||
gboolean use_header_bar = TRUE;
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
if (GDK_IS_X11_SCREEN(screen)) {
|
||||
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
|
||||
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
|
||||
use_header_bar = FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (use_header_bar) {
|
||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||
gtk_header_bar_set_title(header_bar, "freezer");
|
||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||
} else {
|
||||
gtk_window_set_title(window, "freezer");
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 1280, 720);
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
|
||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
|
||||
|
||||
FlView* view = fl_view_new(project);
|
||||
gtk_widget_show(GTK_WIDGET(view));
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
|
||||
|
||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||
|
||||
gtk_widget_grab_focus(GTK_WIDGET(view));
|
||||
}
|
||||
|
||||
// Implements GApplication::local_command_line.
|
||||
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
|
||||
MyApplication* self = MY_APPLICATION(application);
|
||||
// Strip out the first argument as it is the binary name.
|
||||
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
|
||||
|
||||
g_autoptr(GError) error = nullptr;
|
||||
if (!g_application_register(application, nullptr, &error)) {
|
||||
g_warning("Failed to register: %s", error->message);
|
||||
*exit_status = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_application_activate(application);
|
||||
*exit_status = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements GObject::dispose.
|
||||
static void my_application_dispose(GObject* object) {
|
||||
MyApplication* self = MY_APPLICATION(object);
|
||||
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
|
||||
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void my_application_class_init(MyApplicationClass* klass) {
|
||||
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
|
||||
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
|
||||
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
|
||||
}
|
||||
|
||||
static void my_application_init(MyApplication* self) {}
|
||||
|
||||
MyApplication* my_application_new() {
|
||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||
"application-id", APPLICATION_ID,
|
||||
"flags", G_APPLICATION_NON_UNIQUE,
|
||||
nullptr));
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef FLUTTER_MY_APPLICATION_H_
|
||||
#define FLUTTER_MY_APPLICATION_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
|
||||
GtkApplication)
|
||||
|
||||
/**
|
||||
* my_application_new:
|
||||
*
|
||||
* Creates a new Flutter-based application.
|
||||
*
|
||||
* Returns: a new #MyApplication.
|
||||
*/
|
||||
MyApplication* my_application_new();
|
||||
|
||||
#endif // FLUTTER_MY_APPLICATION_H_
|
|
@ -0,0 +1,7 @@
|
|||
# Flutter-related
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
|
||||
# Xcode-related
|
||||
**/dgph
|
||||
**/xcuserdata/
|
|
@ -0,0 +1 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
|
@ -0,0 +1 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import audio_service
|
||||
import audio_session
|
||||
import connectivity_plus
|
||||
import dynamic_color
|
||||
import flutter_local_notifications
|
||||
import just_audio
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import share_plus
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
import wakelock_plus
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||
}
|
|
@ -0,0 +1,695 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
|
||||
buildPhases = (
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Flutter Assemble";
|
||||
productName = FLX;
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
|
||||
remoteInfo = Runner;
|
||||
};
|
||||
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
|
||||
remoteInfo = FLX;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Bundle Framework";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* freezer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "freezer.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33BA886A226E78AF003329D5 /* Configs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
|
||||
);
|
||||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10E42044A3C60003C045 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33FAB671232836740065AC1E /* Runner */,
|
||||
33CEB47122A05771004F2AC0 /* Flutter */,
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10ED2044A3C60003C045 /* freezer.app */,
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC11242044D66E0003C045 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */,
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */,
|
||||
33CC10F72044A3C60003C045 /* Info.plist */,
|
||||
);
|
||||
name = Resources;
|
||||
path = ..;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CEB47122A05771004F2AC0 /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
|
||||
);
|
||||
path = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33FAB671232836740065AC1E /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
||||
33E51914231749380026EE4D /* Release.entitlements */,
|
||||
33CC11242044D66E0003C045 /* Resources */,
|
||||
33BA886A226E78AF003329D5 /* Configs */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
331C80D1294CF70F00263BE5 /* Sources */,
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||
331C80D3294CF70F00263BE5 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = RunnerTests;
|
||||
productName = RunnerTests;
|
||||
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
33CC10EC2044A3C60003C045 /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
33CC10E92044A3C60003C045 /* Sources */,
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||
33CC10EB2044A3C60003C045 /* Resources */,
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* freezer.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
33CC10E52044A3C60003C045 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
331C80D4294CF70F00263BE5 = {
|
||||
CreatedOnToolsVersion = 14.0;
|
||||
TestTargetID = 33CC10EC2044A3C60003C045;
|
||||
};
|
||||
33CC10EC2044A3C60003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
33CC111A2044C6BA0003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 33CC10E42044A3C60003C045;
|
||||
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
33CC10EC2044A3C60003C045 /* Runner */,
|
||||
331C80D4294CF70F00263BE5 /* RunnerTests */,
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
331C80D3294CF70F00263BE5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10EB2044A3C60003C045 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
|
||||
};
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterInputs.xcfilelist,
|
||||
);
|
||||
inputPaths = (
|
||||
Flutter/ephemeral/tripwire,
|
||||
);
|
||||
outputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterOutputs.xcfilelist,
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
331C80D1294CF70F00263BE5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10E92044A3C60003C045 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 33CC10EC2044A3C60003C045 /* Runner */;
|
||||
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
|
||||
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
33CC10F52044A3C60003C045 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = f.f.freezer.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/freezer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/freezer";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
331C80DC294CF71000263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = f.f.freezer.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/freezer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/freezer";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = f.f.freezer.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/freezer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/freezer";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CE9231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEA231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEB231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
33CC10F92044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FA2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC10FC2044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FD2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC111C2044C6BA0003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC111D2044C6BA0003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
331C80DB294CF71000263BE5 /* Debug */,
|
||||
331C80DC294CF71000263BE5 /* Release */,
|
||||
331C80DD294CF71000263BE5 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10F92044A3C60003C045 /* Debug */,
|
||||
33CC10FA2044A3C60003C045 /* Release */,
|
||||
338D0CE9231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10FC2044A3C60003C045 /* Debug */,
|
||||
33CC10FD2044A3C60003C045 /* Release */,
|
||||
338D0CEA231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC111C2044C6BA0003C045 /* Debug */,
|
||||
33CC111D2044C6BA0003C045 /* Release */,
|
||||
338D0CEB231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,98 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "freezer.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "freezer.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
|
||||
BuildableName = "RunnerTests.xctest"
|
||||
BlueprintName = "RunnerTests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "freezer.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "freezer.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,9 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 520 B |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 2.2 KiB |