commit 4dd509a1d9019eb3432de7b683d1eae7a0817e5c Author: LollipopKit Date: Mon Sep 13 14:25:54 2021 +0800 结构初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0fa6b675 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 00000000..be0f63d8 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# 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: 4cc385b4b84ac2f816d939a49ea1f328c4e0b48e + channel: stable + +project_type: app diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..c3691ce3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "toolbox", + "request": "launch", + "type": "dart" + }, + { + "name": "toolbox (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..cb87c5b0 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# toolbox + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/analysis_options.yaml @@ -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 diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 00000000..31930d5a --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,68 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "tech.lolli.toolbox" + minSdkVersion 17 + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..157b535d --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/toolbox/MainActivity.kt b/android/app/src/main/kotlin/com/example/toolbox/MainActivity.kt new file mode 100644 index 00000000..c33a370f --- /dev/null +++ b/android/app/src/main/kotlin/com/example/toolbox/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.toolbox + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..449a9f93 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..d74aa35c --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 00000000..ed45c658 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..bc6a58af --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 00000000..151026b9 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,33 @@ +*.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 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..8d4492f9 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..fa6c85f1 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,471 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.toolbox; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.toolbox; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.toolbox; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..a28140cf --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +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) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +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" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..28c6bf03 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..f091b6b0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cde1211 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..d0ef06e7 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..dcdc2306 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c8f9ed8f Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..75b2d164 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..c4df70d3 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..6a84f41e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..d0e1f585 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +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" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# 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. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 00000000..89604ce6 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + toolbox + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 00000000..354ed1e1 --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/page/home.dart'; + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'ToolBox', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'ToolBox'), + ); + } +} diff --git a/lib/core/analysis.dart b/lib/core/analysis.dart new file mode 100644 index 00000000..1454a4be --- /dev/null +++ b/lib/core/analysis.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import 'package:countly_flutter/countly_flutter.dart'; + +class Analysis { + static const _url = 'https://countly.xuty.cc'; + static const _key = '80372a2a66424b32d0ac8991bfa1ef058bd36b1f'; + + static bool _enabled = false; + + static Future init(bool debug) async { + if (_url.isEmpty || _key.isEmpty) { + return; + } + + _enabled = true; + await Countly.setLoggingEnabled(debug); + await Countly.init(_url, _key); + await Countly.start(); + await Countly.enableCrashReporting(); + await Countly.giveAllConsent(); + print('Countly init successfully.'); + } + + static void recordView(String view) { + if (!_enabled) return; + Countly.recordView(view); + } + + static void recordException(Object exception, [bool fatal = false]) { + if (!_enabled) return; + Countly.logException(exception.toString(), !fatal, null); + } +} diff --git a/lib/core/build_mode.dart b/lib/core/build_mode.dart new file mode 100644 index 00000000..413ba1e2 --- /dev/null +++ b/lib/core/build_mode.dart @@ -0,0 +1,25 @@ +/// See: https://github.com/flutter/flutter/issues/11392 +/// +enum _BuildMode { + release, + debug, + profile, +} + +_BuildMode _buildMode = (() { + if (const bool.fromEnvironment('dart.vm.product')) { + return _BuildMode.release; + } + var result = _BuildMode.profile; + assert(() { + result = _BuildMode.debug; + return true; + }()); + return result; +}()); + +class BuildMode { + static bool isDebug = (_buildMode == _BuildMode.debug); + static bool isProfile = (_buildMode == _BuildMode.profile); + static bool isRelease = (_buildMode == _BuildMode.release); +} diff --git a/lib/core/persistant_store.dart b/lib/core/persistant_store.dart new file mode 100644 index 00000000..5b12eeb2 --- /dev/null +++ b/lib/core/persistant_store.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; + +class PersistentStore { + late Box box; + + Future> init({String boxName = 'defaultBox'}) async { + box = await Hive.openBox(boxName); + return this; + } + + StoreProperty property(String key, {T? defaultValue}) { + return StoreProperty(box, key, defaultValue); + } +} + +class StoreProperty { + StoreProperty(this._box, this._key, this.defaultValue); + + final Box _box; + final String _key; + T? defaultValue; + + ValueListenable listenable() { + return PropertyListenable(_box, _key, defaultValue); + } + + T? fetch() { + return _box.get(_key, defaultValue: defaultValue); + } + + Future put(T value) { + return _box.put(_key, value); + } + + Future delete() { + return _box.delete(_key); + } +} + +class PropertyListenable extends ValueListenable { + PropertyListenable(this.box, this.key, this.defaultValue); + + final Box box; + final String key; + T? defaultValue; + + final List _listeners = []; + StreamSubscription? _subscription; + + @override + void addListener(VoidCallback listener) { + _subscription ??= box.watch().listen((event) { + if (key == event.key) { + for (var listener in _listeners) { + listener(); + } + } + }); + + _listeners.add(listener); + } + + @override + void removeListener(VoidCallback listener) { + _listeners.remove(listener); + + if (_listeners.isEmpty) { + _subscription?.cancel(); + _subscription = null; + } + } + + @override + T get value => box.get(key, defaultValue: defaultValue); +} diff --git a/lib/core/provider_base.dart b/lib/core/provider_base.dart new file mode 100644 index 00000000..d1ade13a --- /dev/null +++ b/lib/core/provider_base.dart @@ -0,0 +1,36 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; + +class ProviderBase with ChangeNotifier { + void setState(void Function() callback) { + callback(); + notifyListeners(); + } +} + +enum ProviderState { + idle, + busy, +} + +class BusyProvider extends ProviderBase { + bool _isBusy = false; + bool get isBusy => _isBusy; + + setBusyState([bool isBusy = true]) { + _isBusy = isBusy; + notifyListeners(); + } + + FutureOr busyRun(FutureOr Function() func) async { + setBusyState(true); + try { + return await Future.sync(func); + } catch (e) { + rethrow; + } finally { + setBusyState(false); + } + } +} diff --git a/lib/core/route.dart b/lib/core/route.dart new file mode 100644 index 00000000..6369628c --- /dev/null +++ b/lib/core/route.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/core/analysis.dart'; + +class AppRoute { + final Widget page; + final String title; + + AppRoute(this.page, this.title); + + void go(BuildContext context) { + Analysis.recordView(title); + Navigator.push(context, MaterialPageRoute(builder: (context) => page)); + } +} diff --git a/lib/core/update.dart b/lib/core/update.dart new file mode 100644 index 00000000..841f5489 --- /dev/null +++ b/lib/core/update.dart @@ -0,0 +1,42 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:toolbox/core/utils.dart'; +import 'package:toolbox/data/provider/app.dart'; +import 'package:toolbox/data/res/build_data.dart'; +import 'package:toolbox/data/service/app.dart'; +import 'package:toolbox/locator.dart'; + +Future isFileAvailable(String url) async { + try { + final resp = await Dio().head(url); + return resp.statusCode == 200; + } catch (e) { + print('update file not available: $e'); + return false; + } +} + +Future doUpdate(BuildContext context, {bool force = false}) async { + final update = await locator().getUpdate(); + + locator().setNewestBuild(update.newest); + + if (!force && update.newest <= BuildData.build) { + print('Update ignored due to current: ${BuildData.build}, ' + 'update: ${update.newest}'); + return; + } + print('Update available: ${update.newest}'); + + if (Platform.isAndroid && !await isFileAvailable(update.android)) { + return; + } + + showSnackBarWithAction( + context, + '${BuildData.name}有更新啦,Ver:${update.newest}\n${update.changelog}', + '更新', + () => openUrl(Platform.isAndroid ? update.android : update.ios)); +} diff --git a/lib/core/utils.dart b/lib/core/utils.dart new file mode 100644 index 00000000..97924745 --- /dev/null +++ b/lib/core/utils.dart @@ -0,0 +1,42 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +void unawaited(Future future) {} + +bool isDarkMode(BuildContext context) => + Theme.of(context).brightness == Brightness.dark; + +void showSnackBar(BuildContext context, Widget child) => + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: child)); + +void showSnackBarWithAction( + BuildContext context, String content, String action, Function onTap) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(content), + action: SnackBarAction( + label: action, + onPressed: () => onTap, + ), + )); +} + +Future openUrl(String url) async { + print('openUrl $url'); + + if (!await canLaunch(url)) { + print('canLaunch false'); + return false; + } + + final ok = await launch(url, forceSafariVC: false); + + if (ok == true) { + return true; + } + + print('launch $url failed'); + + return false; +} diff --git a/lib/data/model/update.dart b/lib/data/model/update.dart new file mode 100644 index 00000000..2d735e55 --- /dev/null +++ b/lib/data/model/update.dart @@ -0,0 +1,44 @@ +/// +/// Code generated by jsonToDartModel https://ashamp.github.io/jsonToDartModel/ +/// +class AppUpdate { +/* +{ + "newest": 33, + "android": "https://v2.custed.lolli.tech/res/tiku/apk/33.apk", + "ios": "https://", + "min": 30, + "changelog": "" +} +*/ + + late int newest; + late String android; + late String ios; + late int min; + late String changelog; + + AppUpdate({ + required this.newest, + required this.android, + required this.ios, + required this.min, + required this.changelog, + }); + AppUpdate.fromJson(Map json) { + newest = json["newest"]?.toInt(); + android = json["android"].toString(); + ios = json["ios"].toString(); + min = json["min"].toInt(); + changelog = json["changelog"].toString(); + } + Map toJson() { + final Map data = {}; + data["newest"] = newest; + data["android"] = android; + data["ios"] = ios; + data["min"] = min; + data["changelog"] = changelog; + return data; + } +} diff --git a/lib/data/provider/app.dart b/lib/data/provider/app.dart new file mode 100644 index 00000000..0632e079 --- /dev/null +++ b/lib/data/provider/app.dart @@ -0,0 +1,22 @@ +import 'package:toolbox/core/provider_base.dart'; +import 'package:toolbox/data/service/app.dart'; + +class AppProvider extends BusyProvider { + Map? _notify; + Map? get notify => _notify; + int? _newestBuild; + int? get newestBuild => _newestBuild; + + Future loadData() async { + setBusyState(true); + final service = AppService(); + _notify = await service.getNotify(); + setBusyState(false); + notifyListeners(); + } + + void setNewestBuild(int build) { + _newestBuild = build; + notifyListeners(); + } +} diff --git a/lib/data/provider/debug.dart b/lib/data/provider/debug.dart new file mode 100644 index 00000000..96c26a3e --- /dev/null +++ b/lib/data/provider/debug.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +class DebugProvider extends ChangeNotifier { + final widgets = []; + + void addText(String text) { + _addText(text); + notifyListeners(); + } + + void _addText(String text) { + _addWidget(Text(text)); + } + + void addError(Object error) { + _addError(error); + notifyListeners(); + } + + void _addError(Object error) { + _addMultiline(error, Colors.red); + } + + void addMultiline(Object data, [Color color = Colors.blue]) { + _addMultiline(data, color); + notifyListeners(); + } + + void _addMultiline(Object data, [Color color = Colors.blue]) { + final widget = Text( + '$data', + style: TextStyle( + color: color, + ), + ); + _addWidget(SingleChildScrollView( + child: widget, + scrollDirection: Axis.horizontal, + )); + } + + void addWidget(Widget widget) { + _addWidget(widget); + notifyListeners(); + } + + void _addWidget(Widget widget) { + final outlined = Container( + margin: const EdgeInsets.symmetric(vertical: 2), + decoration: BoxDecoration( + border: Border.all( + color: Colors.green, + ), + ), + child: widget, + ); + + widgets.add(outlined); + } + + void clear() { + widgets.clear(); + notifyListeners(); + } +} diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart new file mode 100644 index 00000000..c9bd77ba --- /dev/null +++ b/lib/data/res/build_data.dart @@ -0,0 +1,10 @@ +// This file is generated by ./make.dart + +class BuildData { + static const String name = "ToolBox"; + static const int build = 40; + static const String engine = + "Flutter 2.2.3 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision f4abaa0735 (9 weeks ago) • 2021-07-01 12:46:11 -0700\nEngine • revision 241c87ad80\nTools • Dart 2.13.4\n"; + static const String buildAt = "2021-09-04 09:34:17.507782"; + static const int modifications = 0; +} diff --git a/lib/data/res/color.dart b/lib/data/res/color.dart new file mode 100644 index 00000000..d94476a3 --- /dev/null +++ b/lib/data/res/color.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/core/utils.dart'; + +class DynamicColor { + /// 白天模式显示的颜色 + Color light; + + /// 暗黑模式显示的颜色 + Color dark; + + DynamicColor(this.light, this.dark); + + resolve(BuildContext context) => isDarkMode(context) ? dark : light; +} + +final mainColor = DynamicColor(Colors.black87, Colors.white70); diff --git a/lib/data/res/hikotoko.dart b/lib/data/res/hikotoko.dart new file mode 100644 index 00000000..d6388348 --- /dev/null +++ b/lib/data/res/hikotoko.dart @@ -0,0 +1,79 @@ +const hitokotos = [ + '与众不同的生活方式很累人呢,因为找不到借口', + '面对就好,去经历就好。', + '将愿望倾入不愿忘却的回忆中', + '努力是不会背叛自己的,虽然梦想有时会背叛自己', + '用你的笑容去改变这个世界,别让这个世界改变了你的笑容', + '我有在反省,但我不后悔', + '世界上没有一个人能代替另一个人。', + '我的腿让我停下,可是心却不允许我那么做', + '可是就算我们通一千次短信,我们各自的心,大概也只会相互靠近一厘米吧', + '勇气,就是即便害怕也会去做', + '只要有想见面的人,自己就不再是孤单一人', + '我很好奇!', + '在走廊上跌倒会流鼻血,在人生中跌倒会流眼泪。', + '已经无法回到过去了。也不知道将来会是什么模样。', + '相信十年后的八月,我们还会相遇。', + '看到的感受到的永远都不会消失永远都不会忘记,与各种各样的相遇一同永存。', + '我们走过风走过雨,就是没能走进彼此的内心。', + '即使从梦中醒来,还会有回忆留下。', + '悲伤教会了我喜悦。', + '梦总是有会醒来的时候,不会醒的梦总有一天会变成悲伤。', + '真正让我难受的,大概是因为让你看到如此狼狈的自己。', + '真正重要的东西,总是没有的人比拥有的人清楚。', + '我们所过的每个平凡的日常,也许就是连续发生的奇迹', + '要是因为烦恼很痛苦,就选择了轻松的选项,将来一定会后悔', + '心,可是很重的', + '人生在世何其痛苦,所以咖啡至少该甜一点', + '每个人的生命都由秒来计算,因为你永远都不知道下一秒会发生什么。', + '为什么要担心?如果努力了,担心不会让结果变得更好。', + '爱欲于人,犹如执炬,逆风而行,必有烧手之患', + '我们是独立的个体,却不是孤独的存在。', + '幸运的人一生都在被童年治愈,不幸的人一生都在治愈童年。', + '只要结局有可能变好,我们就不能胆怯,就像只要能看到一点蓝天,就不能对天气绝望。', + '无论我们能活多久,我们能够享受的只有无法分割的此刻,此外别无其他。', + '认真的思索,真诚的明辨是非,有这种态度,大概可算是善良吧。', + '多少事,从来急,天地转,光阴迫,一万年太久,只争朝夕。', + '物质决定意识,意识反作用于物质', + '因为痛苦太有价值,因为回忆太珍贵,所以我们更要继续往前走。', + '愿生活不太拥挤,愿笑容不必刻意。愿孤独不再长久,愿碎镜终有重圆。', + '世界之大为何我们相遇,难道是缘分,难道是天意。', + '我们生活在阴沟里,但有人依然仰望星空。', + '宇宙是蚂蚁的梦。', + '时间不在于你拥有多少,而在于你怎样使用。', + '要保持希望在每天清晨太阳升起。', + '我在最没有能力的年纪,遇见了最想照顾一生的人。', + '搞怪的不是红绿灯,而是我数不清的犹豫。', + '醉后不知天在水,满船清梦压星河。', + '人生得意须尽欢,莫让金樽空对月。', + '云想衣裳花想容,春风拂槛露华浓。', + '疏影横斜水清浅,暗香浮动月黄昏。', + '仰天大笑出门去,我辈岂是蓬蒿人。', + '溪云初起日沉阁,山雨欲来风满楼。', + '劝君莫惜金缕衣,劝君惜取少年时。', + '最是人间留不住,朱颜辞镜花辞树。', + '当人们做不到一些事情的时候,他们会对你说你也同样不能。', + '爱你所爱,行你所行,听从你心,无问西东。', + '你现在的气质里,藏着你走过的路,读过的书和爱过的人。', + '我希望在20出头的生命里,做一件到八十岁想起来都还会微笑的事。', + '时光会把你雕刻成你应有的样子。', + '冬天之所以那么冷是为了告诉大家身边人的温暖有多重要。', + '你需要找出面对明天的力量。', + '面对无知的嘲笑,我只能为他们默哀。', + '既然忘不掉,就把它留在心中,让时间去冲淡它。', + '我曾经失落失望失掉所有方向,直到看见平凡才是唯一的答案。', + '合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。', + '世间所有的相遇,都是久别重逢。', + '斜晖脉脉水悠悠,肠断白频洲.', + '不要跟过去的自己比,要期待未来的自己,珍爱现在的自己。', + '我知道这个世上有人在等我,尽管我不知道我在等谁。当因为这样,我每天都很快乐。' + '我从远方赶来,恰巧你们也在。', + 'Write the code, Change the world.', + '生活不止眼前的苟且,还应该有诗和远方的田野。', + '善恶终有报,天道好轮回。不信抬头看,苍天饶过谁。', + '年轻时最大的财富,不是你的青春,不是你的美貌,也不是你有充沛的精力,而是你有犯错误的机会。', + '闾阎扑地,钟鸣鼎食之家;舸舰迷津,青雀黄龙之舳。', + '鹤汀凫渚,穷岛屿之萦回;桂殿兰宫,即冈峦之体势。', + '螃蟹在剥我的壳,笔记本在写我。漫天的我落在枫叶上雪花上。而你在想我。', + '一个人至少拥有一个梦想,有一个理由去坚强。心若没有栖息的地方,到哪里都是在流浪。', +]; diff --git a/lib/data/res/url.dart b/lib/data/res/url.dart new file mode 100644 index 00000000..07986eb3 --- /dev/null +++ b/lib/data/res/url.dart @@ -0,0 +1,3 @@ +const backendUrl = 'https://v2.custed.lolli.tech'; +const baseUrl = backendUrl + '/res/toolbox'; +const joinQQGroupUrl = 'https://jq.qq.com/?_wv=1027&k=G0hUmPAq'; diff --git a/lib/data/service/app.dart b/lib/data/service/app.dart new file mode 100644 index 00000000..4744b9fc --- /dev/null +++ b/lib/data/service/app.dart @@ -0,0 +1,15 @@ +import 'package:dio/dio.dart'; +import 'package:toolbox/data/model/update.dart'; +import 'package:toolbox/data/res/url.dart'; + +class AppService { + Future getNotify() async { + final resp = await Dio().get('$baseUrl/notify.json'); + return resp.data; + } + + Future getUpdate() async { + final resp = await Dio().get('$baseUrl/update.json'); + return AppUpdate.fromJson(resp.data); + } +} diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart new file mode 100644 index 00000000..cd0ec0ca --- /dev/null +++ b/lib/data/store/setting.dart @@ -0,0 +1,6 @@ +import 'package:toolbox/core/persistant_store.dart'; + +class SettingStore extends PersistentStore { + StoreProperty get receiveNotification => + property('notify', defaultValue: true); +} diff --git a/lib/locator.dart b/lib/locator.dart new file mode 100644 index 00000000..8b009b83 --- /dev/null +++ b/lib/locator.dart @@ -0,0 +1,28 @@ +import 'package:get_it/get_it.dart'; +import 'package:toolbox/data/provider/app.dart'; +import 'package:toolbox/data/provider/debug.dart'; +import 'package:toolbox/data/service/app.dart'; +import 'package:toolbox/data/store/setting.dart'; + +GetIt locator = GetIt.instance; + +void setupLocatorForServices() { + locator.registerLazySingleton(() => AppService()); +} + +void setupLocatorForProviders() { + locator.registerSingleton(AppProvider()); + locator.registerSingleton(DebugProvider()); +} + +Future setupLocatorForStores() async { + final setting = SettingStore(); + await setting.init(boxName: 'setting'); + locator.registerSingleton(setting); +} + +Future setupLocator() async { + await setupLocatorForStores(); + setupLocatorForProviders(); + setupLocatorForServices(); +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 00000000..058c559b --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/app.dart'; + +void main() { + runApp(const MyApp()); +} diff --git a/lib/page/home.dart b/lib/page/home.dart new file mode 100644 index 00000000..f7cbd7ae --- /dev/null +++ b/lib/page/home.dart @@ -0,0 +1,141 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + late TextEditingController _textEditingController; + late TextEditingController _textEditingControllerResult; + late MediaQueryData _media; + late ThemeData _theme; + + static const List _typeOption = [ + 'base64 decode', + 'base64 encode', + 'URL encode', + 'URL decode' + ]; + int _typeOptionIndex = 0; + + @override + void initState() { + super.initState(); + _textEditingController = TextEditingController(); + _textEditingControllerResult = TextEditingController(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _media = MediaQuery.of(context); + _theme = Theme.of(context); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: SingleChildScrollView( + child: Column( + children: [ + _buildInputTop(), + _buildTypeOption(), + _buildResult(), + ], + )), + floatingActionButton: FloatingActionButton( + onPressed: () { + _textEditingControllerResult.text = doConvert(); + }, + tooltip: 'convert', + child: const Icon(Icons.send), + ), + ); + } + + String doConvert() { + final text = _textEditingController.text; + switch (_typeOptionIndex) { + case 0: + return utf8.decode(base64.decode(text)); + case 1: + return base64.encode(utf8.encode(text)); + case 2: + return Uri.encodeFull(text); + case 3: + return Uri.decodeFull(text); + default: + return '未知编码'; + } + } + + Widget _buildInputTop() { + return SizedBox( + height: _media.size.height * 0.33, + child: _buildInput(_textEditingController), + ); + } + + Widget _buildTypeOption() { + return Card( + child: ExpansionTile( + title: Text( + _typeOption[_typeOptionIndex], + style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.w500), + ), + children: _typeOption + .map((e) => ListTile( + title: Text( + e, + style: TextStyle( + color: + _theme.textTheme.bodyText2!.color!.withAlpha(177)), + ), + trailing: _buildRadio(_typeOption.indexOf(e)), + )) + .toList(), + ), + ); + } + + Widget _buildResult() { + return SizedBox( + height: _media.size.height * 0.33, + child: _buildInput(_textEditingControllerResult), + ); + } + + Widget _buildInput(TextEditingController controller) { + return Card( + child: TextField( + maxLines: 20, + decoration: InputDecoration( + fillColor: Theme.of(context).cardColor, + filled: true, + border: InputBorder.none), + controller: controller, + ), + ); + } + + Radio _buildRadio(int index) { + return Radio( + value: index, + groupValue: _typeOptionIndex, + onChanged: (int? value) { + setState(() { + _typeOptionIndex = value!; + }); + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 00000000..7fcd91f1 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,420 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + after_layout: + dependency: "direct main" + description: + name: after_layout + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.8.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.15.0" + countly_flutter: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: dcb1f6084242a7b4b8e4edd4b180a42ad7f595ca + url: "https://github.com/Countly/countly-sdk-flutter-bridge.git" + source: git + version: "20.11.4" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.0" + extended_image: + dependency: "direct main" + description: + name: extended_image + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.2.1" + extended_image_library: + dependency: transitive + description: + name: extended_image_library + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.3" + flutter_staggered_animations: + dependency: "direct main" + description: + name: flutter_staggered_animations + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.2.0" + hive: + dependency: "direct main" + description: + name: hive + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.4" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.13.3" + http_client_helper: + dependency: transitive + description: + name: http_client_helper + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.3" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.7.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.8.0" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.3" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.3" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.11.1" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" + process: + dependency: transitive + description: + name: process + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.2.3" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.10" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.4" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.4" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.9" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.0" +sdks: + dart: ">=2.13.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..0702dbea --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,99 @@ +name: toolbox +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.12.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + provider: ^6.0.0 + get_it: ^7.2.0 + hive: ^2.0.0 + hive_flutter: ^1.0.0 + dio: ^4.0.0 + after_layout: ^1.1.0 + flutter_staggered_animations: ^1.0.0 + extended_image: ^4.0.0 + url_launcher: ^6.0.9 + countly_flutter: + git: + url: https://github.com/Countly/countly-sdk-flutter-bridge.git + ref: master + + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 00000000..89e400ad --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,29 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:toolbox/app.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 00000000..8aaa46ac Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 00000000..b749bfef Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 00000000..88cfd48d Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..eb9b4d76 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..d69c5669 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..39484c06 --- /dev/null +++ b/web/index.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + toolbox + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 00000000..5fbd2500 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "toolbox", + "short_name": "toolbox", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}