diff --git a/android/build.gradle b/android/build.gradle index 4080d4f4..27077342 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,9 +8,9 @@ buildscript { dependencies { classpath 'io.fabric.tools:gradle:1.+' - classpath 'com.android.tools.build:gradle:3.3.1' + classpath 'com.android.tools.build:gradle:3.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:3.2.1' // Google Services plugin + classpath 'com.google.gms:google-services:4.3.0' // Google Services plugin } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 2819f022..9b6616cb 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/assets/images/section_first1.webp b/assets/images/section_first1.webp new file mode 100644 index 00000000..28166fab Binary files /dev/null and b/assets/images/section_first1.webp differ diff --git a/assets/images/section_first2.webp b/assets/images/section_first2.webp new file mode 100644 index 00000000..f4a049be Binary files /dev/null and b/assets/images/section_first2.webp differ diff --git a/assets/images/section_jiangong.webp b/assets/images/section_jiangong.webp new file mode 100644 index 00000000..6cc56de8 Binary files /dev/null and b/assets/images/section_jiangong.webp differ diff --git a/assets/images/section_nanzi.webp b/assets/images/section_nanzi.webp new file mode 100644 index 00000000..9dae5bd8 Binary files /dev/null and b/assets/images/section_nanzi.webp differ diff --git a/assets/images/section_qijin.webp b/assets/images/section_qijin.webp new file mode 100644 index 00000000..134ca720 Binary files /dev/null and b/assets/images/section_qijin.webp differ diff --git a/assets/images/section_yanchao.webp b/assets/images/section_yanchao.webp new file mode 100644 index 00000000..0fbd0246 Binary files /dev/null and b/assets/images/section_yanchao.webp differ diff --git a/desktop_plugin/shared_preferences_fde/.gitignore b/desktop_plugin/shared_preferences_fde/.gitignore new file mode 100755 index 00000000..53e92cc4 --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/.gitignore @@ -0,0 +1,3 @@ +.packages +.flutter-plugins +pubspec.lock diff --git a/desktop_plugin/shared_preferences_fde/LICENSE b/desktop_plugin/shared_preferences_fde/LICENSE new file mode 100755 index 00000000..d6456956 --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/desktop_plugin/shared_preferences_fde/macos/Classes/SharedPreferencesPlugin.swift b/desktop_plugin/shared_preferences_fde/macos/Classes/SharedPreferencesPlugin.swift new file mode 100755 index 00000000..f6ac715a --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/macos/Classes/SharedPreferencesPlugin.swift @@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FlutterMacOS +import Foundation + +public class SharedPreferencesPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/shared_preferences", + binaryMessenger: registrar.messenger) + let instance = SharedPreferencesPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + let method = call.method + if method == "getAll" { + result(getAllPrefs()) + } else if method == "setBool" || + method == "setInt" || + method == "setInt" || + method == "setDouble" || + method == "setString" || + method == "setStringList" { + let arguments = call.arguments as! [String : Any] + let key = arguments["key"] as! String + UserDefaults.standard.set(arguments["value"], forKey: key) + result(true) + } else if (method == "commit") { + // UserDefaults does not need to be synchronized. + result(true) + } else if (method == "remove") { + let arguments = call.arguments as! [String : Any] + let key = arguments["key"] as! String + UserDefaults.standard.removeObject(forKey: key) + result(true) + } else if (method == "clear") { + let defaults = UserDefaults.standard + for (key, _) in getAllPrefs() { + defaults.removeObject(forKey: key) + } + result(true) + } else { + result(FlutterMethodNotImplemented) + } + } +} + +private func getAllPrefs() -> [String : Any] { + var filteredPrefs: [String : Any] = [:] + if let appDomain = Bundle.main.bundleIdentifier, + let prefs = UserDefaults.standard.persistentDomain(forName: appDomain) { + for (key, value) in prefs { + if key.hasPrefix("flutter.") { + filteredPrefs[key] = value + } + } + } + return filteredPrefs +} diff --git a/desktop_plugin/shared_preferences_fde/macos/Flutter/GeneratedPluginRegistrant.swift b/desktop_plugin/shared_preferences_fde/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..876cb18d --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// +import Foundation +import FlutterMacOS + +import shared_preferences_fde + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) +} diff --git a/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 00000000..76855356 --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,7 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/rainvisitor/development/flutter +FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/desktop_plugin/shared_preferences_fde +FLUTTER_BUILD_DIR=build +FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64 +FLUTTER_BUILD_NAME=0.0.1 +FLUTTER_BUILD_NUMBER=0.0.1 diff --git a/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/flutter_export_environment.sh b/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100755 index 00000000..451604a2 --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/rainvisitor/development/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/desktop_plugin/shared_preferences_fde" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64" +export "FLUTTER_BUILD_NAME=0.0.1" +export "FLUTTER_BUILD_NUMBER=0.0.1" diff --git a/desktop_plugin/shared_preferences_fde/macos/shared_preferences_fde.podspec b/desktop_plugin/shared_preferences_fde/macos/shared_preferences_fde.podspec new file mode 100755 index 00000000..19e3597e --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/macos/shared_preferences_fde.podspec @@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'shared_preferences_fde' + s.version = '0.0.1' + s.summary = 'Flutter plugin for reading and writing simple key-value pairs.' + s.description = <<-DESC + A temporary macOS implmentation of the shared_preferences plugin from flutter/plugins. + DESC + s.homepage = 'https://github.com/google/flutter-desktop-embedding/tree/master/plugins/flutter_plugins/shared_preferences_fde' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Desktop Embedding Developers' => 'flutter-desktop-embedding-dev@googlegroups.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx + s.osx.deployment_target = '10.12' +end + diff --git a/desktop_plugin/shared_preferences_fde/pubspec.yaml b/desktop_plugin/shared_preferences_fde/pubspec.yaml new file mode 100755 index 00000000..5f5ba2c3 --- /dev/null +++ b/desktop_plugin/shared_preferences_fde/pubspec.yaml @@ -0,0 +1,17 @@ +name: shared_preferences_fde +description: Temporary desktop implmentations of shared_preferences from flutter/plugins +version: 0.0.1 +author: Flutter Desktop Embedding Developers +homepage: https://github.com/google/flutter-desktop-embedding/tree/master/plugins/flutter_plugins/shared_preferences_fde + +flutter: + plugin: + macosPrefix: FLE + pluginClass: SharedPreferencesPlugin + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter diff --git a/desktop_plugin/url_launcher_fde/.gitignore b/desktop_plugin/url_launcher_fde/.gitignore new file mode 100644 index 00000000..53e92cc4 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/.gitignore @@ -0,0 +1,3 @@ +.packages +.flutter-plugins +pubspec.lock diff --git a/desktop_plugin/url_launcher_fde/LICENSE b/desktop_plugin/url_launcher_fde/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/desktop_plugin/url_launcher_fde/linux/Makefile b/desktop_plugin/url_launcher_fde/linux/Makefile new file mode 100644 index 00000000..25161043 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/linux/Makefile @@ -0,0 +1,135 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The name of the plugin. +# The primary source file is assumed to be $(PLUGIN_NAME).cc, with +# $(PLUGIN_NAME).h as the public header meant for inclusion by the application. +PLUGIN_NAME=url_launcher_fde_plugin +# Any files other than the plugin class files that need to be compiled. +EXTRA_SOURCES= +# Extra flags (e.g., for library dependencies). +SYSTEM_LIBRARIES= +EXTRA_CXXFLAGS= +EXTRA_CPPFLAGS= +EXTRA_LDFLAGS= + +# Default build type. For a release build, set BUILD=release. +# Currently this only sets NDEBUG, which is used to control the flags passed +# to the Flutter engine in the example shell, and not the complation settings +# (e.g., optimization level) of the C++ code. +BUILD=debug + +# Dependency locations +# Default to building in the plugin directory. +OUT_DIR=$(CURDIR)/../build/linux +# Sharing an OUT_DIR will be common, so use a subdirectory for intermediates. +PLUGIN_OUT_DIR=$(OUT_DIR)/$(PLUGIN_NAME) +OBJ_DIR=$(PLUGIN_OUT_DIR)/obj +FLUTTER_CACHE_DIR=$(PLUGIN_OUT_DIR)/flutter + +# Flutter library +FLUTTER_LIB_NAME=flutter_linux +FLUTTER_LIB=$(FLUTTER_CACHE_DIR)/lib$(FLUTTER_LIB_NAME).so + +# If FLUTTER_ROOT is set, use that version of Flutter. Otherwise, assume that +# the flutter tool is in the path. +ifeq ($(strip $(FLUTTER_ROOT)),) +FLUTTER_BIN=flutter +else +FLUTTER_BIN=$(FLUTTER_ROOT)/bin/flutter +endif + +# Unpack configuration. +# TODO: Revisit how this is managed, either by having plugins builds always use +# an existing cache provided to the build, or moving logic into the tool. +FLUTTER_UNPACK_ARGS=--target-platform=linux-x64 \ + --cache-dir="$(FLUTTER_CACHE_DIR)" +ifneq ($(strip $(LOCAL_ENGINE)),) +FLUTTER_UNPACK_ARGS+= --local-engine="$(LOCAL_ENGINE)" +endif +ifneq ($(strip $(FLUTTER_ENGINE)),) +FLUTTER_UNPACK_ARGS+= --local-engine-src-path="$(FLUTTER_ENGINE)" +endif + +# Add relevant code from the wrapper library, which is intended to be statically +# built into the plugin. +# Use abspath for the wrapper root, which can contain relative paths; the +# intermediate build files will be based on the source path, which will cause +# issues if they start with one or more '../'s. +WRAPPER_ROOT=$(abspath $(FLUTTER_CACHE_DIR)/cpp_client_wrapper) +# TODO: Once JSON codec files are merged, make a PLUGIN_CODEC variable in the +# top section. For now, using JSON codec would require changes here. +WRAPPER_SOURCES= \ + $(WRAPPER_ROOT)/engine_method_result.cc \ + $(WRAPPER_ROOT)/plugin_registrar.cc \ + $(WRAPPER_ROOT)/standard_codec.cc + +# Use abspath for extra sources, which may also contain relative paths (see +# note above about WRAPPER_ROOT). +SOURCES=$(PLUGIN_NAME).cc $(WRAPPER_SOURCES) $(abspath $(EXTRA_SOURCES)) +PUBLIC_HEADER=$(PLUGIN_NAME).h + +WRAPPER_INCLUDE_DIR=$(WRAPPER_ROOT)/include +INCLUDE_DIRS=$(FLUTTER_CACHE_DIR) $(WRAPPER_INCLUDE_DIR) + +# Build settings +CXX=clang++ +CXXFLAGS.release=-DNDEBUG +CXXFLAGS=-std=c++14 -Wall -Werror -fPIC -fvisibility=hidden \ + -DFLUTTER_PLUGIN_IMPL $(CXXFLAGS.$(BUILD)) $(EXTRA_CXXFLAGS) +CPPFLAGS=$(patsubst %,-I%,$(INCLUDE_DIRS)) $(EXTRA_CPPFLAGS) +LDFLAGS=-shared -L$(FLUTTER_CACHE_DIR) -l$(FLUTTER_LIB_NAME) $(EXTRA_LDFLAGS) + +# Final output files that will be used by applications. +LIBRARY_OUT=$(OUT_DIR)/lib$(PLUGIN_NAME).so +HEADER_OUT=$(OUT_DIR)/include/$(PUBLIC_HEADER) + +# Intermediate files. +OBJ_FILES=$(SOURCES:%.cc=$(OBJ_DIR)/%.o) +DEPENDENCY_FILES=$(OBJ_FILES:%.o=%.d) + +# Targets + +.PHONY: all +all: $(PLUGIN_NAME) + +.PHONY: $(PLUGIN_NAME) +$(PLUGIN_NAME) : $(LIBRARY_OUT) $(HEADER_OUT) + +$(LIBRARY_OUT): $(OBJ_FILES) + mkdir -p $(@D) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ $(LDFLAGS) -o $@ + +$(HEADER_OUT): $(PUBLIC_HEADER) + mkdir -p $(@D) + cp $< $@ + +$(WRAPPER_SOURCES) $(FLUTTER_LIB): | sync + +# This is a phony target because the flutter tool cannot describe +# its inputs and outputs yet. +.PHONY: sync +sync: + $(FLUTTER_BIN) unpack $(FLUTTER_UNPACK_ARGS) + +-include $(DEPENDENCY_FILES) + +$(OBJ_DIR)/%.o : %.cc | sync + mkdir -p $(@D) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -c $< -o $@ + +.PHONY: clean +clean: + rm -f $(LIBRARY_OUT) $(HEADER_OUT) + rm -rf $(PLUGINOUT_DIR) diff --git a/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.cc b/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.cc new file mode 100644 index 00000000..16b3b485 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.cc @@ -0,0 +1,113 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "url_launcher_fde_plugin.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; + +class UrlLauncherPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar); + + virtual ~UrlLauncherPlugin(); + + private: + UrlLauncherPlugin(); + + // Called when a method is called on the plugin's channel; + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +// static +void UrlLauncherPlugin::RegisterWithRegistrar( + flutter::PluginRegistrar *registrar) { + auto channel = std::make_unique>( + registrar->messenger(), "plugins.flutter.io/url_launcher", + &flutter::StandardMethodCodec::GetInstance()); + + // Uses new instead of make_unique due to private constructor. + std::unique_ptr plugin(new UrlLauncherPlugin()); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +UrlLauncherPlugin::UrlLauncherPlugin() = default; + +UrlLauncherPlugin::~UrlLauncherPlugin() = default; + +void UrlLauncherPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("launch") == 0) { + std::string url; + if (method_call.arguments() && method_call.arguments()->IsMap()) { + const EncodableMap &arguments = method_call.arguments()->MapValue(); + auto url_it = arguments.find(EncodableValue("url")); + if (url_it != arguments.end()) { + url = url_it->second.StringValue(); + } + } + if (url.empty()) { + result->Error("argument_error", "No URL provided"); + return; + } + + pid_t pid = fork(); + if (pid == 0) { + execl("/usr/bin/xdg-open", "xdg-open", url.c_str(), nullptr); + exit(1); + } + int status = 0; + waitpid(pid, &status, 0); + if (status != 0) { + std::ostringstream error_message; + error_message << "Failed to open " << url << ": error " << status; + result->Error("open_error", error_message.str()); + return; + } + result->Success(); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void UrlLauncherRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + // The plugin registrar owns the plugin, registered callbacks, etc., so must + // remain valid for the life of the application. + static auto *plugin_registrar = new flutter::PluginRegistrar(registrar); + UrlLauncherPlugin::RegisterWithRegistrar(plugin_registrar); +} diff --git a/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.h b/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.h new file mode 100644 index 00000000..2200c1af --- /dev/null +++ b/desktop_plugin/url_launcher_fde/linux/url_launcher_fde_plugin.h @@ -0,0 +1,38 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef PLUGINS_FLUTTER_PLUGINS_URL_LAUNCHER_FDE_LINUX_URL_LAUNCHER_FDE_PLUGIN_H_ +#define PLUGINS_FLUTTER_PLUGINS_URL_LAUNCHER_FDE_LINUX_URL_LAUNCHER_FDE_PLUGIN_H_ + +// A plugin to control a native menubar. + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void UrlLauncherRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // PLUGINS_FLUTTER_PLUGINS_URL_LAUNCHER_FDE_LINUX_URL_LAUNCHER_FDE_PLUGIN_H_ diff --git a/desktop_plugin/url_launcher_fde/macos/Classes/UrlLauncherPlugin.swift b/desktop_plugin/url_launcher_fde/macos/Classes/UrlLauncherPlugin.swift new file mode 100644 index 00000000..4eda3a67 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/macos/Classes/UrlLauncherPlugin.swift @@ -0,0 +1,55 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import FlutterMacOS +import Foundation + +public class UrlLauncherPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/url_launcher", + binaryMessenger: registrar.messenger) + let instance = UrlLauncherPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + let method = call.method + let urlString: String? = (call.arguments as? Dictionary)?["url"] as? String + if method == "canLaunch" { + guard let unwrappedURLString = urlString, + let url = URL.init(string: unwrappedURLString) else { + result(invalidURLError(urlString)) + return + } + result(NSWorkspace.shared.urlForApplication(toOpen: url) != nil) + } else if method == "launch" { + guard let unwrappedURLString = urlString, + let url = URL.init(string: unwrappedURLString) else { + result(invalidURLError(urlString)) + return + } + result(NSWorkspace.shared.open(url)) + } else { + result(FlutterMethodNotImplemented) + } + } +} + +private func invalidURLError(_ url: String?) -> FlutterError { + return FlutterError( + code:"argument_error", + message: "Unable to parse URL", + details: "Provided URL: \(String(describing: url))") +} diff --git a/desktop_plugin/url_launcher_fde/macos/Flutter/GeneratedPluginRegistrant.swift b/desktop_plugin/url_launcher_fde/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..21db83d9 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// +import Foundation +import FlutterMacOS + +import url_launcher_fde + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) +} diff --git a/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 00000000..1264c990 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,7 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/rainvisitor/development/flutter +FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/desktop_plugin/url_launcher_fde +FLUTTER_BUILD_DIR=build +FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64 +FLUTTER_BUILD_NAME=0.0.1 +FLUTTER_BUILD_NUMBER=0.0.1 diff --git a/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/flutter_export_environment.sh b/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100755 index 00000000..c6086150 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/rainvisitor/development/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/desktop_plugin/url_launcher_fde" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64" +export "FLUTTER_BUILD_NAME=0.0.1" +export "FLUTTER_BUILD_NUMBER=0.0.1" diff --git a/desktop_plugin/url_launcher_fde/macos/url_launcher_fde.podspec b/desktop_plugin/url_launcher_fde/macos/url_launcher_fde.podspec new file mode 100644 index 00000000..d2371a71 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/macos/url_launcher_fde.podspec @@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'url_launcher_fde' + s.version = '0.0.1' + s.summary = 'Flutter plugin for launching a URL.' + s.description = <<-DESC + A temporary macOS implmentation of the url_launcher plugin from flutter/plugins. + DESC + s.homepage = 'https://github.com/google/flutter-desktop-embedding/tree/master/plugins/flutter_plugins/url_launcher_fde' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Desktop Embedding Developers' => 'flutter-desktop-embedding-dev@googlegroups.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx + s.osx.deployment_target = '10.12' +end + diff --git a/desktop_plugin/url_launcher_fde/pubspec.yaml b/desktop_plugin/url_launcher_fde/pubspec.yaml new file mode 100644 index 00000000..d31c9771 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/pubspec.yaml @@ -0,0 +1,17 @@ +name: url_launcher_fde +description: Temporary desktop implmentations of url_launcher from flutter/plugins +version: 0.0.1 +author: Flutter Desktop Embedding Developers +homepage: https://github.com/google/flutter-desktop-embedding/tree/master/plugins/flutter_plugins/url_launcher_fde + +flutter: + plugin: + macosPrefix: FLE + pluginClass: UrlLauncherPlugin + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter diff --git a/desktop_plugin/url_launcher_fde/windows/.gitignore b/desktop_plugin/url_launcher_fde/windows/.gitignore new file mode 100644 index 00000000..48c5a4e9 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/.gitignore @@ -0,0 +1,6 @@ +flutter/ + +# Visual Studio files +*.user +*.suo +.vs \ No newline at end of file diff --git a/desktop_plugin/url_launcher_fde/windows/scripts/cache_flutter.bat b/desktop_plugin/url_launcher_fde/windows/scripts/cache_flutter.bat new file mode 100644 index 00000000..b2f9e9c7 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/scripts/cache_flutter.bat @@ -0,0 +1,27 @@ +:: Copyright 2019 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +@echo off + +:: Caches the Flutter artifacts. This unfortunately reproduces logic from +:: Flutter's tool_backend.dart, since currently that step combines building +:: the bundle and running unpack. Future tool changes should simplify this. + +set CACHE_DIR=%~1 +:: Currently unused, but present now to avoid project changes when unpack needs it later. +set BUILD_MODE=%~2 + +if defined LOCAL_ENGINE set ENGINE_PARAM=--local-engine=%LOCAL_ENGINE% +if defined FLUTTER_ENGINE set ENGINE_SOURCE_PARAM=--local-engine-src-path=%FLUTTER_ENGINE% + +"%FLUTTER_ROOT%\bin\flutter" unpack --target-platform=windows-x64 --cache-dir="%CACHE_DIR%" %ENGINE_PARAM% %ENGINE_SOURCE_PARAM% diff --git a/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.cpp b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.cpp new file mode 100644 index 00000000..54274db2 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.cpp @@ -0,0 +1,121 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "url_launcher_fde.h" + +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; + +class UrlLauncherPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar); + + virtual ~UrlLauncherPlugin(); + + private: + UrlLauncherPlugin(); + + // Called when a method is called on plugin channel; + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +// static +void UrlLauncherPlugin::RegisterWithRegistrar( + flutter::PluginRegistrar *registrar) { + auto channel = std::make_unique>( + registrar->messenger(), "plugins.flutter.io/url_launcher", + &flutter::StandardMethodCodec::GetInstance()); + + // Uses new instead of make_unique due to private constructor. + std::unique_ptr plugin(new UrlLauncherPlugin()); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +UrlLauncherPlugin::UrlLauncherPlugin() = default; + +UrlLauncherPlugin::~UrlLauncherPlugin() = default; + +void UrlLauncherPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("launch") == 0) { + std::string url; + if (method_call.arguments() && method_call.arguments()->IsMap()) { + const EncodableMap &arguments = method_call.arguments()->MapValue(); + auto url_it = arguments.find(EncodableValue("url")); + if (url_it != arguments.end()) { + url = url_it->second.StringValue(); + } + } + if (url.empty()) { + result->Error("argument_error", "No URL provided"); + return; + } + + // launch a URL on Windows + size_t size = url.length() + 1; + std::wstring wurl; + wurl.reserve(size); + size_t outSize; + mbstowcs_s(&outSize, &wurl[0], size, url.c_str(), size - 1); + +#pragma warning(push) +#pragma warning(disable : 4311) // warning C4311: 'type cast': pointer + // truncation from 'HINSTANCE' to 'int' +#pragma warning(disable : 4302) // warning C4302: 'type cast': truncation from + // 'HINSTANCE' to 'int' + int status = + reinterpret_cast(ShellExecute(NULL, TEXT("open"), wurl.c_str(), NULL, NULL, SW_SHOWNORMAL)); +#pragma warning(pop) + + if (status <= 32) { + std::ostringstream error_message; + error_message << "Failed to open " << url << ": ShellExecute error code " + << status; + result->Error("open_error", error_message.str()); + return; + } + result->Success(); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void UrlLauncherRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + // The plugin registrar owns the plugin, registered callbacks, etc., so must + // remain valid for the life of the application. + static auto *plugin_registrar = new flutter::PluginRegistrar(registrar); + UrlLauncherPlugin::RegisterWithRegistrar(plugin_registrar); +} diff --git a/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.h b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.h new file mode 100644 index 00000000..7f35a745 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.h @@ -0,0 +1,36 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef PLUGINS_URL_LAUNCHER_PLUGIN_WINDOWS_H_ +#define PLUGINS_URL_LAUNCHER_PLUGIN_WINDOWS_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void UrlLauncherRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // PLUGINS_EXAMPLE_EXAMPLE_PLUGIN_WINDOWS_H_ diff --git a/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj new file mode 100644 index 00000000..62f1f8f1 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {18F565CD-7BD5-459C-9FEB-2E2379E88105} + Win32Proj + UrlLauncherPlugin + 10.0.17763.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)flutter\cpp_client_wrapper\include;$(ProjectDir)flutter + $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;$(ProjectDir)flutter + $(SolutionDir)..\build\windows\$(Platform)\$(Configuration)\Plugins\ + $(SolutionDir)..\build\windows\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(ProjectDir)flutter\cpp_client_wrapper\include;$(ProjectDir)flutter + $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64;$(ProjectDir)flutter + $(SolutionDir)..\build\windows\$(Platform)\$(Configuration)\Plugins\ + $(SolutionDir)..\build\windows\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;FLUTTER_PLUGIN_IMPL;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + flutter_windows.dll.lib;%(AdditionalDependencies) + + + "$(ProjectDir)scripts\cache_flutter" "$(ProjectDir)flutter" debug + Cache Flutter artifacts + + + + + + + + + + + + + xcopy /q /i /s /y "$(ProjectDir)url_launcher_fde.h" "$(OutDir)" + + + Publishing header to output directory + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;FLUTTER_PLUGIN_IMPL;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + flutter_windows.dll.lib;%(AdditionalDependencies) + + + "$(ProjectDir)scripts\cache_flutter" "$(ProjectDir)flutter" release + Cache Flutter artifacts + + + + + + + + + + + + + xcopy /q /i /s /y "$(ProjectDir)url_launcher_fde.h" "$(OutDir)" + + + Publishing header to output directory + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj.filters b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj.filters new file mode 100644 index 00000000..4dcd8a16 --- /dev/null +++ b/desktop_plugin/url_launcher_fde/windows/url_launcher_fde.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {dbe2dac9-4a21-4849-bef5-2069d695609d} + + + + + Header Files + + + Header Files + + + Source Files + + + + + Source Files\cpp_client_wrapper + + + Source Files\cpp_client_wrapper + + + Source Files\cpp_client_wrapper + + + Source Files + + + \ No newline at end of file diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 00000000..552c4bdb --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/rainvisitor/development/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter" +export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_BUILD_DIR=build" +export "SYMROOT=${SOURCE_ROOT}/../build/ios" +export "FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/ios" +export "FLUTTER_BUILD_NAME=3.2.0" +export "FLUTTER_BUILD_NUMBER=30200" diff --git a/lib/main.dart b/lib/main.dart index 31a7cd8d..0a44beaa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,8 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' + show debugDefaultTargetPlatformOverride; import 'package:flutter/material.dart'; import 'package:flutter_crashlytics/flutter_crashlytics.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -20,43 +21,49 @@ import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/utils/utils.dart'; import 'package:nkust_ap/widgets/share_data_widget.dart'; +import 'models/user_info.dart'; + void main() async { bool isInDebugMode = Constants.isInDebugMode; - String themeCode = AppTheme.LIGHT; - if (Platform.isIOS || Platform.isAndroid) { - await Preferences.init(); - AppIcon.code = - Preferences.getString(Constants.PREF_ICON_STYLE_CODE, AppIcon.OUTLINED); - themeCode = - Preferences.getString(Constants.PREF_THEME_CODE, AppTheme.LIGHT); - } - AppTheme.code = themeCode; + await Preferences.init(); + AppIcon.code = + Preferences.getString(Constants.PREF_ICON_STYLE_CODE, AppIcon.OUTLINED); + AppTheme.code = + Preferences.getString(Constants.PREF_THEME_CODE, AppTheme.LIGHT); if (Platform.isIOS || Platform.isAndroid) { - FlutterError.onError = (FlutterErrorDetails details) { - if (isInDebugMode) { - // In development mode simply print to console. - FlutterError.dumpErrorToConsole(details); - } else { - // In production mode report to the application zone to report to - // Crashlytics. - Zone.current.handleUncaughtError(details.exception, details.stack); - } - }; + if (!Constants.isInDebugMode) { + FlutterError.onError = (FlutterErrorDetails details) { + if (isInDebugMode) { + // In development mode simply print to console. + FlutterError.dumpErrorToConsole(details); + } else { + // In production mode report to the application zone to report to + // Crashlytics. + Zone.current.handleUncaughtError(details.exception, details.stack); + } + }; - await FlutterCrashlytics().initialize(); + await FlutterCrashlytics().initialize(); - runZoned>(() async { + runZoned>(() async { + runApp( + MyApp( + themeData: AppTheme.data, + ), + ); + }, onError: (error, stackTrace) async { + // Whenever an error occurs, call the `reportCrash` function. This will send + // Dart errors to our dev console or Crashlytics depending on the environment. + await FlutterCrashlytics() + .reportCrash(error, stackTrace, forceCrash: false); + }); + } else { runApp( MyApp( themeData: AppTheme.data, ), ); - }, onError: (error, stackTrace) async { - // Whenever an error occurs, call the `reportCrash` function. This will send - // Dart errors to our dev console or Crashlytics depending on the environment. - await FlutterCrashlytics() - .reportCrash(error, stackTrace, forceCrash: false); - }); + } } else { // See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; @@ -80,8 +87,10 @@ class MyApp extends StatefulWidget { class MyAppState extends State { FirebaseAnalytics analytics; - FirebaseMessaging _firebaseMessaging; + FirebaseMessaging firebaseMessaging; ThemeData themeData; + UserInfo userInfo; + bool isLogin = false, offlineLogin = false; setThemeData(ThemeData themeData) { setState(() { @@ -94,7 +103,7 @@ class MyAppState extends State { themeData = widget.themeData; if (Platform.isAndroid || Platform.isIOS) { analytics = FirebaseAnalytics(); - _firebaseMessaging = FirebaseMessaging(); + firebaseMessaging = FirebaseMessaging(); _initFCM(); FA.analytics = analytics; Preferences.init(); @@ -153,8 +162,8 @@ class MyAppState extends State { } void _initFCM() { - _firebaseMessaging.requestNotificationPermissions(); - _firebaseMessaging.configure( + firebaseMessaging.requestNotificationPermissions(); + firebaseMessaging.configure( onMessage: (Map message) async { if (Constants.isInDebugMode) print("onMessage: $message"); Utils.showFCMNotification( @@ -170,20 +179,25 @@ class MyAppState extends State { if (Constants.isInDebugMode) print("onResume: $message"); }, ); - _firebaseMessaging.requestNotificationPermissions( - const IosNotificationSettings(sound: true, badge: true, alert: true)); - _firebaseMessaging.onIosSettingsRegistered + firebaseMessaging.requestNotificationPermissions( + const IosNotificationSettings( + sound: true, + badge: true, + alert: true, + ), + ); + firebaseMessaging.onIosSettingsRegistered .listen((IosNotificationSettings settings) { print("Settings registered: $settings"); }); - _firebaseMessaging.getToken().then((String token) { + firebaseMessaging.getToken().then((String token) { if (token == null) return; if (Constants.isInDebugMode) { print("Push Messaging token: $token"); } if (Platform.isAndroid) - _firebaseMessaging.subscribeToTopic("Android"); - else if (Platform.isIOS) _firebaseMessaging.subscribeToTopic("IOS"); + firebaseMessaging.subscribeToTopic("Android"); + else if (Platform.isIOS) firebaseMessaging.subscribeToTopic("IOS"); }); } } diff --git a/lib/models/score_data.dart b/lib/models/score_data.dart index 17c1f426..461110be 100644 --- a/lib/models/score_data.dart +++ b/lib/models/score_data.dart @@ -13,8 +13,7 @@ class ScoreData { return ScoreData( status: json['status'], messages: json['messages'], - content: - json['scores'].length != 0 ? Content.fromJson(json['scores']) : null, + content: json['scores'] != null ? Content.fromJson(json['scores']) : null, ); } @@ -37,7 +36,7 @@ class Content { static Content fromJson(Map json) { return Content( scores: Score.toList(json['scores']), - detail: Detail.fromJson(json['detail']), + detail: json['detail'] == null ? null : Detail.fromJson(json['detail']), ); } diff --git a/lib/pages/home/about/about_us_page.dart b/lib/pages/home/about/about_us_page.dart index 0aea92db..f2935520 100644 --- a/lib/pages/home/about/about_us_page.dart +++ b/lib/pages/home/about/about_us_page.dart @@ -1,37 +1,27 @@ import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/assets.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; - -class AboutUsPageRoute extends MaterialPageRoute { - AboutUsPageRoute() - : super(builder: (BuildContext context) => new AboutUsPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new AboutUsPage()); - } -} +import 'package:nkust_ap/widgets/share_data_widget.dart'; class AboutUsPage extends StatefulWidget { static const String routerName = "/aboutUs"; @override - AboutUsPageState createState() => new AboutUsPageState(); + AboutUsPageState createState() => AboutUsPageState(); } -class AboutUsPageState extends State - with SingleTickerProviderStateMixin { +class AboutUsPageState extends State { AppLocalizations app; @override void initState() { - super.initState(); FA.setCurrentScreen("AboutUsPage", "about_us_page.dart"); + super.initState(); } @override @@ -39,11 +29,31 @@ class AboutUsPageState extends State super.dispose(); } + String get sectionImage { + final department = ShareDataWidget.of(context).data.userInfo.department; + Random random = Random(); + bool halfSnapFingerChance = random.nextInt(2000) % 2 == 0; + if (department.contains('建工') || department.contains('燕巢')) + return halfSnapFingerChance + ? ImageAssets.sectionJiangong + : ImageAssets.sectionYanchao; + else if (department.contains('第一')) + return halfSnapFingerChance + ? ImageAssets.sectionFirst1 + : ImageAssets.sectionFirst2; + else if (department.contains('旗津') || department.contains('楠梓')) + return halfSnapFingerChance + ? ImageAssets.sectionQijin + : ImageAssets.sectionNanzi; + else + return ImageAssets.kuasap2; + } + @override Widget build(BuildContext context) { app = AppLocalizations.of(context); var expandedHeight = MediaQuery.of(context).size.height * 0.25; - return new Scaffold( + return Scaffold( body: NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ @@ -51,7 +61,7 @@ class AboutUsPageState extends State expandedHeight: expandedHeight, floating: false, pinned: true, - title: new Text(app.about), + title: Text(app.about), actions: [ IconButton( icon: Icon(AppIcon.codeIcon), @@ -64,7 +74,7 @@ class AboutUsPageState extends State backgroundColor: Resource.Colors.blue, flexibleSpace: FlexibleSpaceBar( background: Image.asset( - ImageAssets.kuasap2, + sectionImage, fit: BoxFit.cover, ), ), diff --git a/lib/pages/home/about/open_source_page.dart b/lib/pages/home/about/open_source_page.dart index 645663cc..327f5793 100644 --- a/lib/pages/home/about/open_source_page.dart +++ b/lib/pages/home/about/open_source_page.dart @@ -2,32 +2,323 @@ import 'package:flutter/material.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -class OpenSourcePageRoute extends MaterialPageRoute { - OpenSourcePageRoute() - : super(builder: (BuildContext context) => new OpenSourcePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new OpenSourcePage()); - } -} - class OpenSourcePage extends StatefulWidget { static const String routerName = "/openSource"; @override - OpenSourcePageState createState() => new OpenSourcePageState(); + OpenSourcePageState createState() => OpenSourcePageState(); } -class OpenSourcePageState extends State - with SingleTickerProviderStateMixin { +class OpenSourcePageState extends State { + final List<_Item> items = [ + _Item( + "KUAS-AP-Material", + "The MIT License (MIT)\n" + + "\n" + + "Copyright (c) 2015 HearSilent\n" + + "\n" + + "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + + "of this software and associated documentation files (the \"Software\"), to deal\n" + + "in the Software without restriction, including without limitation the rights\n" + + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + + "copies of the Software, and to permit persons to whom the Software is\n" + + "furnished to do so, subject to the following conditions:\n" + + "\n" + + "The above copyright notice and this permission notice shall be included in all\n" + + "copies or substantial portions of the Software.\n" + + "\n" + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + + "SOFTWARE.\n"), + _Item( + "flutter", + "Copyright 2014 The Chromium Authors. All rights reserved.\n\n" + "Redistribution and use in source and binary forms, with or without" + "modification, are permitted provided that the following conditions are" + "met:\n" + "* Redistributions of source code must retain the above copyright" + " notice, this list of conditions and the following disclaimer.\n" + "* Redistributions in binary form must reproduce the above" + " copyright notice, this list of conditions and the following" + " disclaimer in the documentation and/or other materials provided" + " with the distribution.\n" + "* Neither the name of Google Inc. nor the names of its" + " contributors may be used to endorse or promote products derived" + " from this software without specific prior written permission.\n\n" + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS" + "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT" + "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR" + "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT" + "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL," + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT" + "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE," + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY" + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), + _Item( + "flutter plugin", + " Copyright 2017 The Chromium Authors. All rights reserved.\n\n" + " Redistribution and use in source and binary forms, with or without" + " modification, are permitted provided that the following conditions are" + " met:\n\n" + " * Redistributions of source code must retain the above copyright" + " notice, this list of conditions and the following disclaimer.\n" + " * Redistributions in binary form must reproduce the above" + " copyright notice, this list of conditions and the following disclaimer" + " in the documentation and/or other materials provided with the" + " distribution.\n" + " * Neither the name of Google Inc. nor the names of its" + " contributors may be used to endorse or promote products derived from" + " this software without specific prior written permission.\n\n" + " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS" + " \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT" + " LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR" + " A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT" + " OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL," + " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT" + " LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE," + " DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY" + " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + " (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + " OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), + _Item( + "dio", + "MIT License\n\n" + "Copyright (c) 2018 wendux\n\n" + "Permission is hereby granted, free of charge, to any person obtaining a copy" + "of this software and associated documentation files (the \"Software\"), to deal" + "in the Software without restriction, including without limitation the rights" + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell" + "copies of the Software, and to permit persons to whom the Software is" + "furnished to do so, subject to the following conditions:\n\n" + "The above copyright notice and this permission notice shall be included in all" + "copies or substantial portions of the Software.\n\n" + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR" + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY," + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE" + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER" + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM," + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE" + "SOFTWARE."), + _Item( + "FlutterToast", + "MIT License\n" + + "\n" + + "Copyright (c) 2018 YM\n" + + "\n" + + "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + + "of this software and associated documentation files (the \"Software\"), to deal\n" + + "in the Software without restriction, including without limitation the rights\n" + + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + + "copies of the Software, and to permit persons to whom the Software is\n" + + "furnished to do so, subject to the following conditions:\n" + + "\n" + + "The above copyright notice and this permission notice shall be included in all\n" + + "copies or substantial portions of the Software.\n" + + "\n" + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + + "SOFTWARE.", + ), + _Item( + "flutter_calendar", + "Copyright 2018 AppTree SoftWare\n" + + "\n" + + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n" + + "\n" + + "Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n" + + "\n" + + "Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n" + + "\n" + + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), + _Item( + "flutter_carousel_slider", + "MIT License\n" + + "\n" + + "Copyright (c) 2017 serenader\n" + + "\n" + + "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + + "of this software and associated documentation files (the \"Software\"), to deal\n" + + "in the Software without restriction, including without limitation the rights\n" + + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + + "copies of the Software, and to permit persons to whom the Software is\n" + + "furnished to do so, subject to the following conditions:\n" + + "\n" + + "The above copyright notice and this permission notice shall be included in all\n" + + "copies or substantial portions of the Software.\n" + + "\n" + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + + "SOFTWARE."), + _Item( + "flutter_crashlytics", + "BSD 2-Clause License\n" + + "\n" + + "Copyright (c) 2018, KiWi\n" + + "All rights reserved.\n" + + "\n" + + "Redistribution and use in source and binary forms, with or without\n" + + "modification, are permitted provided that the following conditions are met:\n" + + "\n" + + "* Redistributions of source code must retain the above copyright notice, this\n" + + " list of conditions and the following disclaimer.\n" + + "\n" + + "* Redistributions in binary form must reproduce the above copyright notice,\n" + + " this list of conditions and the following disclaimer in the documentation\n" + + " and/or other materials provided with the distribution.\n" + + "\n" + + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" + + "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" + + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" + + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n" + + "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" + + "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n" + + "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n" + + "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" + + "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"), + _Item( + "encrypt", + "BSD 3-Clause License\n" + "Copyright (c) 2018, Leo Cavalcante\n" + "All rights reserved.\n" + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions are met:\n" + "\n" + "* Redistributions of source code must retain the above copyright notice, this\n" + "list of conditions and the following disclaimer.\n" + "\n" + "* Redistributions in binary form must reproduce the above copyright notice,\n" + "this list of conditions and the following disclaimer in the documentation\n" + "and/or other materials provided with the distribution.\n" + "\n" + "* Neither the name of the copyright holder nor the names of its\n" + "contributors may be used to endorse or promote products derived from\n" + "this software without specific prior written permission.\n" + "\n" + " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" + "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n" + "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" + "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR" + " SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n" + "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" + "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + ")\n"), + _Item( + 'flutter_local_notifications', + "// Copyright 2018 Michael Bui. All rights reserved.\n" + + "//\n" + + "// Redistribution and use in source and binary forms, with or without\n" + + "// modification, are permitted provided that the following conditions are\n" + + "// met:\n" + + "//\n" + + "// * Redistributions of source code must retain the above copyright\n" + + "// notice, this list of conditions and the following disclaimer.\n" + + "// * Redistributions in binary form must reproduce the above\n" + + "// copyright notice, this list of conditions and the following disclaimer\n" + + "// in the documentation and/or other materials provided with the\n" + + "// distribution.\n" + + "// * Neither the name of the copyright holder nor the names of its\n" + + "// contributors may be used to endorse or promote products derived from\n" + + "// this software without specific prior written permission.\n" + + "//\n" + + "// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" + + "// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" + + "// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" + + "// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" + + "// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" + + "// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" + + "// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" + + "// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + + "// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" + + "// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + + "// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), + _Item( + 'sprintf', + "Copyright (c) 2012, Richard Eames All rights reserved.\n" + + "\n" + + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n" + + "\n" + + "Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n" + + "Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n" + + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), + _Item('add_2_calendar', + 'MIT License\n\nCopyright (c) 2018 Javi Hurtado\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.'), + _Item( + 'flutter_cached_network_image', + "The MIT License (MIT)\n" + + "\n" + + "Copyright (c) 2018 Rene Floor\n" + + "\n" + + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n" + + "\n" + + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n" + + "\n" + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."), + _Item( + 'app_review', + "MIT License\n" + + "\n" + + "Copyright (c) 2018 Rody Davis\n" + + "\n" + + "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + + "of this software and associated documentation files (the \"Software\"), to deal\n" + + "in the Software without restriction, including without limitation the rights\n" + + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + + "copies of the Software, and to permit persons to whom the Software is\n" + + "furnished to do so, subject to the following conditions:\n" + + "\n" + + "The above copyright notice and this permission notice shall be included in all\n" + + "copies or substantial portions of the Software.\n" + + "\n" + + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + + "SOFTWARE."), + _Item( + 'outline_material_icons', + "Copyright 2018 Lucas Levin\n" + + "\n" + + "Licensed under the Apache License, Version 2.0 (the \"License\");\n" + + "you may not use this file except in compliance with the License.\n" + + "You may obtain a copy of the License at\n" + + "\n" + + " http://www.apache.org/licenses/LICENSE-2.0\n" + + "\n" + + "Unless required by applicable law or agreed to in writing, software\n" + + "distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + "See the License for the specific language governing permissions and\n" + + "limitations under the License.", + ) + ]; + AppLocalizations app; @override void initState() { - super.initState(); FA.setCurrentScreen("OpenSourcePage", "open_source_page.dart"); + super.initState(); } @override @@ -38,344 +329,52 @@ class OpenSourcePageState extends State @override Widget build(BuildContext context) { app = AppLocalizations.of(context); - return new Scaffold( - appBar: new AppBar( - title: new Text(app.aboutOpenSourceTitle), + return Scaffold( + appBar: AppBar( + title: Text(app.aboutOpenSourceTitle), backgroundColor: Resource.Colors.blue, ), - body: ListView( - children: [ - _item( - "KUAS-AP-Material", - "The MIT License (MIT)\n" + - "\n" + - "Copyright (c) 2015 HearSilent\n" + - "\n" + - "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + - "of this software and associated documentation files (the \"Software\"), to deal\n" + - "in the Software without restriction, including without limitation the rights\n" + - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + - "copies of the Software, and to permit persons to whom the Software is\n" + - "furnished to do so, subject to the following conditions:\n" + - "\n" + - "The above copyright notice and this permission notice shall be included in all\n" + - "copies or substantial portions of the Software.\n" + - "\n" + - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + - "SOFTWARE.\n"), - _item( - "flutter", - "Copyright 2014 The Chromium Authors. All rights reserved.\n\n" - "Redistribution and use in source and binary forms, with or without" - "modification, are permitted provided that the following conditions are" - "met:\n" - "* Redistributions of source code must retain the above copyright" - " notice, this list of conditions and the following disclaimer.\n" - "* Redistributions in binary form must reproduce the above" - " copyright notice, this list of conditions and the following" - " disclaimer in the documentation and/or other materials provided" - " with the distribution.\n" - "* Neither the name of Google Inc. nor the names of its" - " contributors may be used to endorse or promote products derived" - " from this software without specific prior written permission.\n\n" - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS" - "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT" - "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR" - "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT" - "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL," - "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT" - "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE," - "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY" - "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" - "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" - "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), - _item( - "flutter plugin", - " Copyright 2017 The Chromium Authors. All rights reserved.\n\n" - " Redistribution and use in source and binary forms, with or without" - " modification, are permitted provided that the following conditions are" - " met:\n\n" - " * Redistributions of source code must retain the above copyright" - " notice, this list of conditions and the following disclaimer.\n" - " * Redistributions in binary form must reproduce the above" - " copyright notice, this list of conditions and the following disclaimer" - " in the documentation and/or other materials provided with the" - " distribution.\n" - " * Neither the name of Google Inc. nor the names of its" - " contributors may be used to endorse or promote products derived from" - " this software without specific prior written permission.\n\n" - " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS" - " \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT" - " LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR" - " A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT" - " OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL," - " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT" - " LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE," - " DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY" - " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" - " (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" - " OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), - _item( - "dio", - "MIT License\n\n" - "Copyright (c) 2018 wendux\n\n" - "Permission is hereby granted, free of charge, to any person obtaining a copy" - "of this software and associated documentation files (the \"Software\"), to deal" - "in the Software without restriction, including without limitation the rights" - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell" - "copies of the Software, and to permit persons to whom the Software is" - "furnished to do so, subject to the following conditions:\n\n" - "The above copyright notice and this permission notice shall be included in all" - "copies or substantial portions of the Software.\n\n" - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR" - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY," - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE" - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER" - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM," - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE" - "SOFTWARE."), - _item( - "FlutterToast", - "MIT License\n" + - "\n" + - "Copyright (c) 2018 YM\n" + - "\n" + - "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + - "of this software and associated documentation files (the \"Software\"), to deal\n" + - "in the Software without restriction, including without limitation the rights\n" + - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + - "copies of the Software, and to permit persons to whom the Software is\n" + - "furnished to do so, subject to the following conditions:\n" + - "\n" + - "The above copyright notice and this permission notice shall be included in all\n" + - "copies or substantial portions of the Software.\n" + - "\n" + - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + - "SOFTWARE.", - ), - _item( - "flutter_calendar", - "Copyright 2018 AppTree SoftWare\n" + - "\n" + - "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n" + - "\n" + - "Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n" + - "\n" + - "Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n" + - "\n" + - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), - _item( - "flutter_carousel_slider", - "MIT License\n" + - "\n" + - "Copyright (c) 2017 serenader\n" + - "\n" + - "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + - "of this software and associated documentation files (the \"Software\"), to deal\n" + - "in the Software without restriction, including without limitation the rights\n" + - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + - "copies of the Software, and to permit persons to whom the Software is\n" + - "furnished to do so, subject to the following conditions:\n" + - "\n" + - "The above copyright notice and this permission notice shall be included in all\n" + - "copies or substantial portions of the Software.\n" + - "\n" + - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + - "SOFTWARE."), - _item( - "flutter_crashlytics", - "BSD 2-Clause License\n" + - "\n" + - "Copyright (c) 2018, KiWi\n" + - "All rights reserved.\n" + - "\n" + - "Redistribution and use in source and binary forms, with or without\n" + - "modification, are permitted provided that the following conditions are met:\n" + - "\n" + - "* Redistributions of source code must retain the above copyright notice, this\n" + - " list of conditions and the following disclaimer.\n" + - "\n" + - "* Redistributions in binary form must reproduce the above copyright notice,\n" + - " this list of conditions and the following disclaimer in the documentation\n" + - " and/or other materials provided with the distribution.\n" + - "\n" + - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" + - "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" + - "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" + - "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n" + - "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" + - "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n" + - "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n" + - "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" + - "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + - "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"), - _item( - "encrypt", - "BSD 3-Clause License\n" - "Copyright (c) 2018, Leo Cavalcante\n" - "All rights reserved.\n" - "\n" - "Redistribution and use in source and binary forms, with or without\n" - "modification, are permitted provided that the following conditions are met:\n" - "\n" - "* Redistributions of source code must retain the above copyright notice, this\n" - "list of conditions and the following disclaimer.\n" - "\n" - "* Redistributions in binary form must reproduce the above copyright notice,\n" - "this list of conditions and the following disclaimer in the documentation\n" - "and/or other materials provided with the distribution.\n" - "\n" - "* Neither the name of the copyright holder nor the names of its\n" - "contributors may be used to endorse or promote products derived from\n" - "this software without specific prior written permission.\n" - "\n" - " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" - "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" - "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" - "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n" - "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" - "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR" - " SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n" - "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" - "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" - "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - ")\n"), - _item( - 'flutter_local_notifications', - "// Copyright 2018 Michael Bui. All rights reserved.\n" + - "//\n" + - "// Redistribution and use in source and binary forms, with or without\n" + - "// modification, are permitted provided that the following conditions are\n" + - "// met:\n" + - "//\n" + - "// * Redistributions of source code must retain the above copyright\n" + - "// notice, this list of conditions and the following disclaimer.\n" + - "// * Redistributions in binary form must reproduce the above\n" + - "// copyright notice, this list of conditions and the following disclaimer\n" + - "// in the documentation and/or other materials provided with the\n" + - "// distribution.\n" + - "// * Neither the name of the copyright holder nor the names of its\n" + - "// contributors may be used to endorse or promote products derived from\n" + - "// this software without specific prior written permission.\n" + - "//\n" + - "// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" + - "// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" + - "// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" + - "// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" + - "// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" + - "// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" + - "// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" + - "// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + - "// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" + - "// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + - "// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), - _item( - 'sprintf', - "Copyright (c) 2012, Richard Eames All rights reserved.\n" + - "\n" + - "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n" + - "\n" + - "Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n" + - "Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n" + - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."), - _item('add_2_calendar', - 'MIT License\n\nCopyright (c) 2018 Javi Hurtado\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.'), - _item( - 'flutter_cached_network_image', - "The MIT License (MIT)\n" + - "\n" + - "Copyright (c) 2018 Rene Floor\n" + - "\n" + - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n" + - "\n" + - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n" + - "\n" + - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."), - _item( - 'app_review', - "MIT License\n" + - "\n" + - "Copyright (c) 2018 Rody Davis\n" + - "\n" + - "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + - "of this software and associated documentation files (the \"Software\"), to deal\n" + - "in the Software without restriction, including without limitation the rights\n" + - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + - "copies of the Software, and to permit persons to whom the Software is\n" + - "furnished to do so, subject to the following conditions:\n" + - "\n" + - "The above copyright notice and this permission notice shall be included in all\n" + - "copies or substantial portions of the Software.\n" + - "\n" + - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + - "SOFTWARE."), - _item( - 'outline_material_icons', - "Copyright 2018 Lucas Levin\n" + - "\n" + - "Licensed under the Apache License, Version 2.0 (the \"License\");\n" + - "you may not use this file except in compliance with the License.\n" + - "You may obtain a copy of the License at\n" + - "\n" + - " http://www.apache.org/licenses/LICENSE-2.0\n" + - "\n" + - "Unless required by applicable law or agreed to in writing, software\n" + - "distributed under the License is distributed on an \"AS IS\" BASIS,\n" + - "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + - "See the License for the specific language governing permissions and\n" + - "limitations under the License.", - ) - ], + body: ListView.builder( + itemCount: items.length, + itemBuilder: (_, index) { + return Card( + margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + elevation: 4.0, + child: Container( + padding: EdgeInsets.only( + top: 24.0, left: 16.0, bottom: 16.0, right: 16.0), + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + items[index].text, + style: TextStyle(fontSize: 18.0), + ), + SizedBox( + height: 4.0, + ), + RichText( + text: TextSpan( + style: TextStyle( + fontSize: 14.0, color: Resource.Colors.grey), + text: items[index].subText, + ), + ), + ], + ), + ), + ); + }, ), ); } +} - _item(String text, String subText) => Card( - margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), - elevation: 4.0, - child: Container( - padding: - EdgeInsets.only(top: 24.0, left: 16.0, bottom: 16.0, right: 16.0), - width: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - text, - style: TextStyle(fontSize: 18.0), - ), - SizedBox( - height: 4.0, - ), - RichText( - text: TextSpan( - style: TextStyle(fontSize: 14.0, color: Resource.Colors.grey), - text: subText, - ), - ), - ], - ), - ), - ); +class _Item { + final String text; + final String subText; + + _Item(this.text, this.subText); } diff --git a/lib/pages/home/bus/bus_reservations_page.dart b/lib/pages/home/bus/bus_reservations_page.dart index 3d65c07b..6e60e651 100644 --- a/lib/pages/home/bus/bus_reservations_page.dart +++ b/lib/pages/home/bus/bus_reservations_page.dart @@ -5,26 +5,14 @@ import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/cache_utils.dart'; import 'package:nkust_ap/utils/global.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/default_dialog.dart'; import 'package:nkust_ap/widgets/hint_content.dart'; import 'package:nkust_ap/widgets/progress_dialog.dart'; import 'package:nkust_ap/widgets/yes_no_dialog.dart'; -import 'package:shared_preferences/shared_preferences.dart'; enum _State { loading, finish, error, empty, offlineEmpty } -class BusReservationsPageRoute extends MaterialPageRoute { - BusReservationsPageRoute() - : super(builder: (BuildContext context) => new BusReservationsPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new BusReservationsPage()); - } -} - class BusReservationsPage extends StatefulWidget { static const String routerName = "/bus/reservations"; @@ -47,9 +35,9 @@ class BusReservationsPageState extends State @override void initState() { - super.initState(); FA.setCurrentScreen("BusReservationsPage", "bus_reservations_page.dart"); _getBusReservations(); + super.initState(); } @override @@ -59,6 +47,7 @@ class BusReservationsPageState extends State @override Widget build(BuildContext context) { + super.build(context); app = AppLocalizations.of(context); return Column( children: [ @@ -207,8 +196,7 @@ class BusReservationsPageState extends State ); _getBusReservations() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN)) { + if (Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false)) { busReservationsData = await CacheUtils.loadBusReservationsData(); if (mounted) { setState(() { diff --git a/lib/pages/home/bus/bus_reserve_page.dart b/lib/pages/home/bus/bus_reserve_page.dart index 73e41f2d..74c26148 100644 --- a/lib/pages/home/bus/bus_reserve_page.dart +++ b/lib/pages/home/bus/bus_reserve_page.dart @@ -5,27 +5,16 @@ import 'package:nkust_ap/models/models.dart'; import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/default_dialog.dart'; import 'package:nkust_ap/widgets/flutter_calendar.dart'; import 'package:nkust_ap/widgets/hint_content.dart'; import 'package:nkust_ap/widgets/progress_dialog.dart'; import 'package:nkust_ap/widgets/yes_no_dialog.dart'; -import 'package:shared_preferences/shared_preferences.dart'; enum _State { loading, finish, error, empty, offline } enum Station { janGong, yanchao } -class BusReservePageRoute extends MaterialPageRoute { - BusReservePageRoute() - : super(builder: (BuildContext context) => BusReservePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: BusReservePage()); - } -} - class BusReservePage extends StatefulWidget { static const String routerName = "/bus/reserve"; @@ -38,21 +27,22 @@ class BusReservePageState extends State @override bool get wantKeepAlive => true; - double top = 0.0; + AppLocalizations app; _State state = _State.loading; - BusData busData; Station selectStartStation = Station.janGong; DateTime dateTime = DateTime.now(); - AppLocalizations app; + BusData busData; + + double top = 0.0; @override void initState() { - super.initState(); FA.setCurrentScreen("BusReservePage", "bus_reserve_page.dart"); _getBusTimeTables(); + super.initState(); } @override @@ -62,6 +52,7 @@ class BusReservePageState extends State @override Widget build(BuildContext context) { + super.build(context); app = AppLocalizations.of(context); return Scaffold( body: OrientationBuilder(builder: (_, orientation) { @@ -179,9 +170,10 @@ class BusReservePageState extends State ); default: return RefreshIndicator( - onRefresh: () { - _getBusTimeTables(); + onRefresh: () async { + await _getBusTimeTables(); FA.logAction('refresh', 'swipe'); + return null; }, child: ListView( physics: const NeverScrollableScrollPhysics(), @@ -341,8 +333,7 @@ class BusReservePageState extends State ); _getBusTimeTables() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN)) { + if (Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false)) { setState(() { state = _State.offline; }); diff --git a/lib/pages/home/bus/bus_rule_page.dart b/lib/pages/home/bus/bus_rule_page.dart index 44b6c2b6..b7bf27b6 100644 --- a/lib/pages/home/bus/bus_rule_page.dart +++ b/lib/pages/home/bus/bus_rule_page.dart @@ -3,17 +3,6 @@ import 'package:flutter/material.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -class BusRulePageRoute extends MaterialPageRoute { - BusRulePageRoute() - : super(builder: (BuildContext context) => new BusRulePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new BusRulePage()); - } -} - class BusRulePage extends StatefulWidget { static const String routerName = "/bus/rule"; @@ -23,14 +12,13 @@ class BusRulePage extends StatefulWidget { BusRulePageState createState() => new BusRulePageState(); } -class BusRulePageState extends State - with SingleTickerProviderStateMixin { +class BusRulePageState extends State { AppLocalizations app; @override void initState() { - super.initState(); FA.setCurrentScreen("BusRulePage", "bus_rule_page.dart"); + super.initState(); } @override diff --git a/lib/pages/home/bus_page.dart b/lib/pages/home/bus_page.dart index 0bf9724f..21bc1b8b 100644 --- a/lib/pages/home/bus_page.dart +++ b/lib/pages/home/bus_page.dart @@ -4,22 +4,6 @@ import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -class BusPageRoute extends MaterialPageRoute { - BusPageRoute({this.initIndex = 0}) - : super( - builder: (BuildContext context) => - new BusPage(initIndex: initIndex)); - - final int initIndex; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new BusPage(initIndex: initIndex)); - } -} - class BusPage extends StatefulWidget { static const String routerName = "/bus"; final List _children = [ @@ -31,25 +15,21 @@ class BusPage extends StatefulWidget { BusPage({this.initIndex = 0}); @override - BusPageState createState() => new BusPageState(_children, initIndex); + BusPageState createState() => new BusPageState(); } class BusPageState extends State with SingleTickerProviderStateMixin { - final List _children; - final int initIndex; - int _currentIndex = 0; AppLocalizations app; TabController controller; - BusPageState(this._children, this.initIndex) { - _currentIndex = initIndex; - } + int _currentIndex = 0; @override void initState() { + controller = + TabController(length: 2, initialIndex: widget.initIndex, vsync: this); super.initState(); - controller = TabController(length: 2, initialIndex: initIndex, vsync: this); } @override @@ -72,12 +52,12 @@ class BusPageState extends State with SingleTickerProviderStateMixin { color: Colors.white, ), onPressed: () { - Navigator.of(context).push(BusRulePageRoute()); + Utils.pushCupertinoStyle(context, BusRulePage()); }) ], ), body: TabBarView( - children: _children, + children: widget._children, controller: controller, physics: NeverScrollableScrollPhysics()), bottomNavigationBar: BottomNavigationBar( diff --git a/lib/pages/home/calculate_units_page.dart b/lib/pages/home/calculate_units_page.dart index 21a3aef4..43cd5f30 100644 --- a/lib/pages/home/calculate_units_page.dart +++ b/lib/pages/home/calculate_units_page.dart @@ -9,18 +9,6 @@ import 'package:shared_preferences/shared_preferences.dart'; enum _State { ready, loading, finish, error, empty, offline } -class CalculateUnitsPageRoute extends MaterialPageRoute { - CalculateUnitsPageRoute() - : super(builder: (BuildContext context) => new CalculateUnitsPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new CalculateUnitsPage()); - } -} - class CalculateUnitsPage extends StatefulWidget { static const String routerName = "/calculateUnits"; diff --git a/lib/pages/home/course_page.dart b/lib/pages/home/course_page.dart index b41570e3..4549e5a8 100644 --- a/lib/pages/home/course_page.dart +++ b/lib/pages/home/course_page.dart @@ -12,16 +12,6 @@ import 'package:nkust_ap/widgets/semester_picker.dart'; enum _State { loading, finish, error, empty, offlineEmpty } -class CoursePageRoute extends MaterialPageRoute { - CoursePageRoute() : super(builder: (BuildContext context) => CoursePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: CoursePage()); - } -} - class CoursePage extends StatefulWidget { static const String routerName = '/course'; diff --git a/lib/pages/home/info/notification_page.dart b/lib/pages/home/info/notification_page.dart index 7a7afa5b..f02857d6 100644 --- a/lib/pages/home/info/notification_page.dart +++ b/lib/pages/home/info/notification_page.dart @@ -4,22 +4,11 @@ import 'package:nkust_ap/models/models.dart'; import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/hint_content.dart'; -import 'package:shared_preferences/shared_preferences.dart'; enum _State { loading, finish, loadingMore, error, empty, offline } -class NotificationPageRoute extends MaterialPageRoute { - NotificationPageRoute() - : super(builder: (BuildContext context) => NotificationPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: NotificationPage()); - } -} - class NotificationPage extends StatefulWidget { static const String routerName = "/info/notification"; @@ -32,20 +21,31 @@ class NotificationPageState extends State @override bool get wantKeepAlive => true; + _State state = _State.loading; + + AppLocalizations app; + ScrollController controller; List notificationList = []; + int page = 1; - _State state = _State.loading; + TextStyle get _textStyle => TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ); - AppLocalizations app; + TextStyle get _textGreyStyle => TextStyle( + color: Resource.Colors.grey, + fontSize: 14.0, + ); @override void initState() { - super.initState(); FA.setCurrentScreen("NotificationPage", "notification_page.dart"); controller = ScrollController()..addListener(_scrollListener); _getNotifications(); + super.initState(); } @override @@ -54,14 +54,6 @@ class NotificationPageState extends State controller.removeListener(_scrollListener); } - _textGreyStyle() { - return TextStyle(color: Resource.Colors.grey, fontSize: 14.0); - } - - _textStyle() { - return TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold); - } - Widget _notificationItem(NotificationModel notification) { return GestureDetector( onLongPress: () { @@ -88,8 +80,8 @@ class NotificationPageState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - notification.info.title ?? "", - style: _textStyle(), + notification.info.title ?? '', + style: _textStyle, textAlign: TextAlign.left, ), SizedBox(height: 8.0), @@ -97,15 +89,15 @@ class NotificationPageState extends State children: [ Expanded( child: Text( - notification.info.department ?? "", - style: _textGreyStyle(), + notification.info.department ?? '', + style: _textGreyStyle, textAlign: TextAlign.left, ), ), Expanded( child: Text( - notification.info.date ?? "", - style: _textGreyStyle(), + notification.info.date ?? '', + style: _textGreyStyle, textAlign: TextAlign.right, ), ), @@ -120,6 +112,7 @@ class NotificationPageState extends State @override Widget build(BuildContext context) { + super.build(context); app = AppLocalizations.of(context); return _body(); } @@ -149,13 +142,14 @@ class NotificationPageState extends State ); default: return RefreshIndicator( - onRefresh: () { + onRefresh: () async { setState(() { state = _State.loading; }); notificationList.clear(); - _getNotifications(); + await _getNotifications(); FA.logAction('refresh', 'swipe'); + return null; }, child: ListView.builder( controller: controller, @@ -181,8 +175,7 @@ class NotificationPageState extends State } _getNotifications() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN)) { + if (Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false)) { setState(() { state = _State.offline; }); diff --git a/lib/pages/home/info/phone_page.dart b/lib/pages/home/info/phone_page.dart index 0376b494..58d0ee17 100644 --- a/lib/pages/home/info/phone_page.dart +++ b/lib/pages/home/info/phone_page.dart @@ -7,16 +7,6 @@ import 'package:nkust_ap/widgets/hint_content.dart'; enum _State { loading, finish, error } -class PhonePageRoute extends MaterialPageRoute { - PhonePageRoute() : super(builder: (BuildContext context) => PhonePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: PhonePage()); - } -} - class PhonePage extends StatefulWidget { static const String routerName = "/info/phone"; @@ -29,19 +19,33 @@ class PhonePageState extends State @override bool get wantKeepAlive => true; + _State state = _State.loading; + + AppLocalizations app; + List phoneModelList = []; - _State state = _State.loading; + TextStyle get _textStyle => TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ); - int page = 1; + TextStyle get _textBlueStyle => TextStyle( + color: Resource.Colors.blueText, + fontSize: 18.0, + fontWeight: FontWeight.bold, + ); - AppLocalizations app; + TextStyle get _textGreyStyle => TextStyle( + color: Resource.Colors.grey, + fontSize: 14.0, + ); @override void initState() { - super.initState(); FA.setCurrentScreen("PhonePage", "phone_page.dart"); _getPhones(); + super.initState(); } @override @@ -49,19 +53,45 @@ class PhonePageState extends State super.dispose(); } - _textBlueStyle() { - return TextStyle( - color: Resource.Colors.blueText, - fontSize: 18.0, - fontWeight: FontWeight.bold); - } - - _textGreyStyle() { - return TextStyle(color: Resource.Colors.grey, fontSize: 14.0); + @override + Widget build(BuildContext context) { + super.build(context); + app = AppLocalizations.of(context); + return _body(); } - _textStyle() { - return TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold); + Widget _body() { + switch (state) { + case _State.loading: + return Center( + child: CircularProgressIndicator(), + ); + case _State.error: + return HintContent( + icon: AppIcon.assignment, + content: app.clickToRetry, + ); + default: + return ListView.builder( + itemCount: phoneModelList.length, + itemBuilder: (context, index) { + if (phoneModelList[index].number.isEmpty) { + return Padding( + padding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + child: Text( + phoneModelList[index].name, + style: _textBlueStyle, + textAlign: TextAlign.left, + ), + ); + } else + return _phoneItem(phoneModelList[index]); + }, + ); + } } Widget _phoneItem(PhoneModel phone) { @@ -80,13 +110,13 @@ class PhonePageState extends State children: [ Text( phone.name ?? '', - style: _textStyle(), + style: _textStyle, textAlign: TextAlign.left, ), SizedBox(height: 8.0), Text( phone.number ?? '', - style: _textGreyStyle(), + style: _textGreyStyle, textAlign: TextAlign.left, ) ], @@ -104,42 +134,6 @@ class PhonePageState extends State ); } - @override - Widget build(BuildContext context) { - app = AppLocalizations.of(context); - return _body(); - } - - Widget _body() { - switch (state) { - case _State.loading: - return Container( - child: CircularProgressIndicator(), alignment: Alignment.center); - case _State.error: - return HintContent( - icon: AppIcon.assignment, - content: app.clickToRetry, - ); - default: - return ListView.builder( - itemCount: phoneModelList.length, - itemBuilder: (context, index) { - if (phoneModelList[index].number.isEmpty) { - return Padding( - padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), - child: Text( - phoneModelList[index].name, - style: _textBlueStyle(), - textAlign: TextAlign.left, - ), - ); - } else - return _phoneItem(phoneModelList[index]); - }, - ); - } - } - _getPhones() async { setState(() { state = _State.loading; diff --git a/lib/pages/home/info/schedule_page.dart b/lib/pages/home/info/schedule_page.dart index 9cb4dffe..ef4f60db 100644 --- a/lib/pages/home/info/schedule_page.dart +++ b/lib/pages/home/info/schedule_page.dart @@ -15,17 +15,6 @@ import 'package:sprintf/sprintf.dart'; enum _State { loading, finish, error, empty } -class SchedulePageRoute extends MaterialPageRoute { - SchedulePageRoute() - : super(builder: (BuildContext context) => new SchedulePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new SchedulePage()); - } -} - class SchedulePage extends StatefulWidget { static const String routerName = "/info/schedule"; @@ -38,7 +27,7 @@ class SchedulePageState extends State @override bool get wantKeepAlive => true; - List scheduleWeights = []; + AppLocalizations app; List scheduleDataList = []; @@ -46,13 +35,21 @@ class SchedulePageState extends State int page = 1; - AppLocalizations app; + TextStyle get _textBlueStyle => TextStyle( + color: Resource.Colors.blueText, + fontSize: 18.0, + fontWeight: FontWeight.bold, + ); + + TextStyle get _textStyle => TextStyle( + fontSize: 16.0, + ); @override void initState() { - super.initState(); FA.setCurrentScreen("SchedulePage", "schedule_page.dart"); _getSchedules(); + super.initState(); } @override @@ -62,6 +59,7 @@ class SchedulePageState extends State @override Widget build(BuildContext context) { + super.build(context); app = AppLocalizations.of(context); return _body(); } @@ -82,7 +80,9 @@ class SchedulePageState extends State ); default: return CustomScrollView( - slivers: scheduleWeights, + slivers: [ + for (var value in scheduleDataList) ..._scheduleItem(value), + ], ); } } @@ -119,8 +119,6 @@ class SchedulePageState extends State } else { var jsonArray = jsonDecode(data); scheduleDataList = ScheduleData.toList(jsonArray); - scheduleWeights.clear(); - for (var i in scheduleDataList) scheduleWeights.addAll(_scheduleItem(i)); if (mounted) { setState(() { if (scheduleDataList.length == 0) @@ -133,17 +131,6 @@ class SchedulePageState extends State } } - _textBlueStyle() { - return TextStyle( - color: Resource.Colors.blueText, - fontSize: 18.0, - fontWeight: FontWeight.bold); - } - - _textStyle() { - return TextStyle(fontSize: 16.0); - } - List _scheduleItem(ScheduleData schedule) { List events = []; for (var i in schedule.events) { @@ -153,7 +140,7 @@ class SchedulePageState extends State padding: EdgeInsets.symmetric(horizontal: 12.0), child: Text( i, - style: _textStyle(), + style: _textStyle, textAlign: TextAlign.left, ), ), @@ -173,7 +160,7 @@ class SchedulePageState extends State padding: EdgeInsets.symmetric(horizontal: 12.0), child: Text( schedule.week ?? "", - style: _textBlueStyle(), + style: _textBlueStyle, textAlign: TextAlign.left, ), ), @@ -224,7 +211,7 @@ class SchedulePageState extends State alignment: Alignment.centerLeft, child: Text( schedule.events[index], - style: _textStyle(), + style: _textStyle, textAlign: TextAlign.left, ), ), diff --git a/lib/pages/home/leave_page.dart b/lib/pages/home/leave_page.dart index 81b545a9..a81331fd 100644 --- a/lib/pages/home/leave_page.dart +++ b/lib/pages/home/leave_page.dart @@ -5,22 +5,6 @@ import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -class LeavePageRoute extends MaterialPageRoute { - LeavePageRoute({this.initIndex = 0}) - : super( - builder: (BuildContext context) => - new LeavePage(initIndex: initIndex)); - - final int initIndex; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new LeavePage(initIndex: initIndex)); - } -} - class LeavePage extends StatefulWidget { static const String routerName = "/leave"; final List _children = [ @@ -32,26 +16,22 @@ class LeavePage extends StatefulWidget { LeavePage({this.initIndex = 0}); @override - LeavePageState createState() => new LeavePageState(_children, initIndex); + LeavePageState createState() => LeavePageState(); } class LeavePageState extends State with SingleTickerProviderStateMixin { - final List _children; - final int initIndex; - int _currentIndex = 0; AppLocalizations app; TabController controller; - LeavePageState(this._children, this.initIndex) { - _currentIndex = initIndex; - } + int _currentIndex = 0; @override void initState() { super.initState(); - controller = TabController(length: 2, initialIndex: initIndex, vsync: this); + controller = + TabController(length: 2, initialIndex: widget.initIndex, vsync: this); } @override @@ -69,7 +49,7 @@ class LeavePageState extends State backgroundColor: Resource.Colors.blue, ), body: TabBarView( - children: _children, + children: widget._children, controller: controller, physics: NeverScrollableScrollPhysics()), bottomNavigationBar: BottomNavigationBar( diff --git a/lib/pages/home/leaves/leave_apply_page.dart b/lib/pages/home/leaves/leave_apply_page.dart index 81c896fb..aa2c7794 100644 --- a/lib/pages/home/leaves/leave_apply_page.dart +++ b/lib/pages/home/leaves/leave_apply_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:nkust_ap/models/models.dart'; import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; @@ -9,17 +8,6 @@ import 'package:nkust_ap/widgets/hint_content.dart'; enum _State { loading, finish, error, empty } enum Leave { normal, sick, official, funeral, maternity } -class LeaveApplyPageRoute extends MaterialPageRoute { - LeaveApplyPageRoute() - : super(builder: (BuildContext context) => new LeaveApplyPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new LeaveApplyPage()); - } -} - class LeaveApplyPage extends StatefulWidget { static const String routerName = "/leave/apply"; @@ -32,19 +20,17 @@ class LeaveApplyPageState extends State @override bool get wantKeepAlive => true; - _State state = _State.empty; - BusReservationsData busReservationsData; - List busReservationWeights = []; - DateTime dateTime = DateTime.now(); - AppLocalizations app; + _State state = _State.empty; + + DateTime dateTime = DateTime.now(); Leave _leave = Leave.normal; @override void initState() { - super.initState(); FA.setCurrentScreen("LeaveApplyPage", "leave_apply_page.dart"); + super.initState(); } @override diff --git a/lib/pages/home/leaves/leave_record_page.dart b/lib/pages/home/leaves/leave_record_page.dart index 9c419549..79df7240 100644 --- a/lib/pages/home/leaves/leave_record_page.dart +++ b/lib/pages/home/leaves/leave_record_page.dart @@ -13,17 +13,6 @@ import 'package:nkust_ap/widgets/semester_picker.dart'; enum _State { loading, finish, error, empty, offlineEmpty } -class LeaveRecordPageRoute extends MaterialPageRoute { - LeaveRecordPageRoute() - : super(builder: (BuildContext context) => LeaveRecordPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: LeaveRecordPage()); - } -} - class LeaveRecordPage extends StatefulWidget { static const String routerName = '/leave/record'; diff --git a/lib/pages/home/news_content_page.dart b/lib/pages/home/news_content_page.dart index dab40057..7d474d69 100644 --- a/lib/pages/home/news_content_page.dart +++ b/lib/pages/home/news_content_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:nkust_ap/models/models.dart'; @@ -7,20 +9,6 @@ import 'package:nkust_ap/utils/global.dart'; enum _Status { loading, finish, error, empty } -class NewsContentPageRoute extends MaterialPageRoute { - NewsContentPageRoute(this.news) - : super(builder: (BuildContext context) => new NewsContentPage(news)); - - final News news; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new NewsContentPage(news)); - } -} - class NewsContentPage extends StatefulWidget { static const String routerName = "/news/content"; @@ -29,22 +17,17 @@ class NewsContentPage extends StatefulWidget { NewsContentPage(this.news); @override - NewsContentPageState createState() => new NewsContentPageState(news); + NewsContentPageState createState() => NewsContentPageState(); } -class NewsContentPageState extends State - with SingleTickerProviderStateMixin { +class NewsContentPageState extends State { _Status state = _Status.finish; AppLocalizations app; - final News news; - - NewsContentPageState(this.news); - @override void initState() { + FA.setCurrentScreen("NewsContentPage", "widget.s_content_page.dart"); super.initState(); - FA.setCurrentScreen("NewsContentPage", "news_content_page.dart"); } @override @@ -52,74 +35,68 @@ class NewsContentPageState extends State super.dispose(); } - Widget _homebody(Orientation orientation) { + @override + Widget build(BuildContext context) { + app = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(app.news), + backgroundColor: Resource.Colors.blue, + ), + body: _homebody(), + ); + } + + Widget _homebody() { switch (state) { case _Status.loading: return Center( child: CircularProgressIndicator(), ); case _Status.finish: - return OrientationBuilder(builder: (_, orientation) { - if (orientation == Orientation.portrait) - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: _renderContent(orientation), - ); - else - return Row( + return OrientationBuilder( + builder: (_, orientation) { + return Flex( + direction: orientation == Orientation.portrait + ? Axis.vertical + : Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: _renderContent(orientation), ); - }); + }, + ); default: return Container(); } } - @override - Widget build(BuildContext context) { - app = AppLocalizations.of(context); - return Scaffold( - appBar: new AppBar( - title: new Text(app.news), - backgroundColor: Resource.Colors.blue, - ), - body: OrientationBuilder(builder: (_, orientation) { - return _homebody(orientation); - }), - ); - } - _renderContent(Orientation orientation) { - List list = [ - AspectRatio( - aspectRatio: orientation == Orientation.portrait ? 4 / 3 : 9 / 16, - child: Hero( - tag: news.hashCode, - child: CachedNetworkImage( - imageUrl: news.image, - errorWidget: (context, url, error) => Icon(AppIcon.error), - ), - ), + final Widget image = AspectRatio( + aspectRatio: orientation == Orientation.portrait ? 4 / 3 : 9 / 16, + child: Hero( + tag: widget.news.hashCode, + child: (Platform.isIOS || Platform.isAndroid) + ? CachedNetworkImage( + imageUrl: widget.news.image, + errorWidget: (context, url, error) => Icon(Icons.error), + ) + : Image.network(widget.news.image), ), - SizedBox( - height: orientation == Orientation.portrait ? 16.0 : 0.0, - width: orientation == Orientation.portrait ? 0.0 : 32.0), - ]; - List listB = [ + ); + final List newsContent = [ Hero( tag: Constants.TAG_NEWS_TITLE, child: Material( color: Colors.transparent, child: Text( - news.title, + widget.news.title, textAlign: TextAlign.center, style: TextStyle( - fontSize: 20.0, - color: Resource.Colors.greyText, - fontWeight: FontWeight.w500), + fontSize: 20.0, + color: Resource.Colors.greyText, + fontWeight: FontWeight.w500, + ), ), ), ), @@ -131,36 +108,55 @@ class NewsContentPageState extends State padding: EdgeInsets.symmetric( horizontal: orientation == Orientation.portrait ? 16.0 : 0.0), child: Text( - news.content, + widget.news.content, textAlign: TextAlign.center, - style: TextStyle(fontSize: 16.0, color: Resource.Colors.greyText), + style: TextStyle( + fontSize: 16.0, + color: Resource.Colors.greyText, + ), ), ), - SizedBox(height: 16.0), - RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(30.0), + if (widget.news.url.isNotEmpty) ...[ + SizedBox(height: 16.0), + RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(30.0), + ), + ), + onPressed: () { + Utils.launchUrl(widget.news.url); + String message = widget.news.content.length > 12 + ? widget.news.content + : widget.news.content.substring(0, 12); + FA.logAction('news_link', 'click', message: message); + }, + padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 0.0), + color: Resource.Colors.yellow, + child: Icon( + AppIcon.exitToApp, + color: Colors.white, ), ), - onPressed: () { - if (news.url.isNotEmpty) Utils.launchUrl(news.url); - String message = news.content.length > 12 - ? news.content - : news.content.substring(0, 12); - FA.logAction('news_link', 'click', message: message); - }, - padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 0.0), - color: Resource.Colors.yellow, - child: Icon(AppIcon.exitToApp, color: Colors.white), - ) + ], ]; if (orientation == Orientation.portrait) { - list.addAll(listB); + return [ + image, + SizedBox(height: 16.0), + ...newsContent, + ]; } else { - list.add( - Column(mainAxisAlignment: MainAxisAlignment.center, children: listB)); + return [ + Expanded(child: image), + SizedBox(width: 32.0), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: newsContent, + ), + ), + ]; } - return list; } } diff --git a/lib/pages/home/score_page.dart b/lib/pages/home/score_page.dart index 35bbdd13..a1d79c35 100644 --- a/lib/pages/home/score_page.dart +++ b/lib/pages/home/score_page.dart @@ -11,16 +11,6 @@ import 'package:nkust_ap/widgets/semester_picker.dart'; enum _State { loading, finish, error, empty, offlineEmpty } -class ScorePageRoute extends MaterialPageRoute { - ScorePageRoute() : super(builder: (BuildContext context) => ScorePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition(opacity: animation, child: ScorePage()); - } -} - class ScorePage extends StatefulWidget { static const String routerName = '/score'; @@ -306,8 +296,10 @@ class ScorePageState extends State { state = _State.offlineEmpty; else if (scoreData.status == 204) state = _State.empty; - else { + else if (scoreData.status == 200) { state = _State.finish; + } else { + state = _State.error; } }); } diff --git a/lib/pages/home/setting_page.dart b/lib/pages/home/setting_page.dart index e0b2549c..5a014ad9 100644 --- a/lib/pages/home/setting_page.dart +++ b/lib/pages/home/setting_page.dart @@ -18,22 +18,6 @@ import 'package:nkust_ap/widgets/progress_dialog.dart'; import 'package:nkust_ap/widgets/share_data_widget.dart'; import 'package:package_info/package_info.dart'; -class SettingPageRoute extends MaterialPageRoute { - SettingPageRoute() - : super( - builder: (BuildContext context) => SettingPage(), - ); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return FadeTransition( - opacity: animation, - child: SettingPage(), - ); - } -} - class SettingPage extends StatefulWidget { static const String routerName = "/setting"; @@ -172,10 +156,8 @@ class SettingPageState extends State { setState(() { AppLocalizations.languageCode = item.value; }); - if (Platform.isAndroid || Platform.isIOS) { - Preferences.setString( - Constants.PREF_LANGUAGE_CODE, item.value); - } + Preferences.setString( + Constants.PREF_LANGUAGE_CODE, item.value); Navigator.pop(context); }), ]), @@ -210,10 +192,8 @@ class SettingPageState extends State { setState(() { AppIcon.code = item.value; }); - if (Platform.isAndroid || Platform.isIOS) - Preferences.setString( - Constants.PREF_ICON_STYLE_CODE, - item.value); + Preferences.setString( + Constants.PREF_ICON_STYLE_CODE, item.value); Navigator.pop(context); }), ]), @@ -251,9 +231,8 @@ class SettingPageState extends State { .data .setThemeData(AppTheme.data); }); - if (Platform.isAndroid || Platform.isIOS) - Preferences.setString( - Constants.PREF_THEME_CODE, item.value); + Preferences.setString( + Constants.PREF_THEME_CODE, item.value); Navigator.pop(context); }), ]), @@ -306,10 +285,12 @@ class SettingPageState extends State { } _getPreference() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); + PackageInfo packageInfo; + if (Platform.isAndroid || Platform.isIOS) + packageInfo = await PackageInfo.fromPlatform(); setState(() { isOffline = Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false); - appVersion = packageInfo.version; + appVersion = packageInfo?.version ?? '1.0.0'; courseNotify = Preferences.getBool(Constants.PREF_COURSE_NOTIFY, false); displayPicture = Preferences.getBool(Constants.PREF_DISPLAY_PICTURE, true); diff --git a/lib/pages/home/shcool_info_page.dart b/lib/pages/home/shcool_info_page.dart index b0424f77..4f813e89 100644 --- a/lib/pages/home/shcool_info_page.dart +++ b/lib/pages/home/shcool_info_page.dart @@ -3,64 +3,61 @@ import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -class SchoolInfoPageRoute extends MaterialPageRoute { - SchoolInfoPageRoute() - : super(builder: (BuildContext context) => new SchoolInfoPage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new SchoolInfoPage()); - } -} - class SchoolInfoPage extends StatefulWidget { static const String routerName = "/ShcoolInfo"; @override - SchoolInfoPageState createState() => new SchoolInfoPageState(); + SchoolInfoPageState createState() => SchoolInfoPageState(); } class SchoolInfoPageState extends State with SingleTickerProviderStateMixin { - int _currentIndex = 0; - TabController controller; final List _children = [ - new NotificationPage(), - new PhonePage(), - new SchedulePage() + NotificationPage(), + PhonePage(), + SchedulePage() ]; AppLocalizations app; + TabController controller; + + int _currentIndex = 0; + @override void initState() { - super.initState(); FA.setCurrentScreen("SchoolInfoPage", "school_info_page.dart"); controller = TabController(length: 3, vsync: this); + super.initState(); } @override void dispose() { - super.dispose(); controller.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { app = AppLocalizations.of(context); - return new Scaffold( - appBar: new AppBar( - title: new Text(app.schoolInfo), + return Scaffold( + appBar: AppBar( + title: Text(app.schoolInfo), backgroundColor: Resource.Colors.blue, ), body: TabBarView( - children: _children, - controller: controller, - physics: new NeverScrollableScrollPhysics()), - bottomNavigationBar: new BottomNavigationBar( + children: _children, + controller: controller, + physics: NeverScrollableScrollPhysics(), + ), + bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, - onTap: onTabTapped, + onTap: (index) { + setState(() { + _currentIndex = index; + controller.animateTo(_currentIndex); + }); + }, fixedColor: Resource.Colors.yellow, items: [ BottomNavigationBarItem( @@ -79,11 +76,4 @@ class SchoolInfoPageState extends State ), ); } - - void onTabTapped(int index) { - setState(() { - _currentIndex = index; - controller.animateTo(_currentIndex); - }); - } } diff --git a/lib/pages/home/user_info_page.dart b/lib/pages/home/user_info_page.dart index 177ede09..1ae0f16a 100644 --- a/lib/pages/home/user_info_page.dart +++ b/lib/pages/home/user_info_page.dart @@ -5,27 +5,11 @@ import 'package:nkust_ap/models/user_info.dart'; import 'package:nkust_ap/res/colors.dart' as Resource; import 'package:nkust_ap/utils/cache_utils.dart'; import 'package:nkust_ap/utils/global.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/drawer_body.dart'; -import 'package:shared_preferences/shared_preferences.dart'; enum _Status { loading, finish, error, empty } -class UserInfoPageRoute extends MaterialPageRoute { - final UserInfo userInfo; - - UserInfoPageRoute(this.userInfo) - : super( - builder: (BuildContext context) => - new UserInfoPage(userInfo: userInfo)); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition( - opacity: animation, child: new UserInfoPage(userInfo: userInfo)); - } -} - class UserInfoPage extends StatefulWidget { static const String routerName = "/widget.userInfo"; final UserInfo userInfo; @@ -138,8 +122,8 @@ class UserInfoPageState extends State } _getUserPicture() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - bool isOffline = prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN) ?? false; + bool isOffline = + Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false); if (!isOffline) { Helper.instance.getUsersPicture().then((url) async { if (this.mounted) { diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 42bfe514..250907c1 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -8,28 +8,19 @@ import 'package:nkust_ap/res/app_icon.dart'; import 'package:nkust_ap/res/colors.dart' as Resource; import 'package:nkust_ap/utils/cache_utils.dart'; import 'package:nkust_ap/utils/global.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/drawer_body.dart'; import 'package:nkust_ap/widgets/hint_content.dart'; +import 'package:nkust_ap/widgets/share_data_widget.dart'; import 'package:nkust_ap/widgets/yes_no_dialog.dart'; -import 'package:shared_preferences/shared_preferences.dart'; enum _State { loading, finish, error, empty, offline } -class HomePageRoute extends MaterialPageRoute { - HomePageRoute() : super(builder: (BuildContext context) => new HomePage()); - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new FadeTransition(opacity: animation, child: new HomePage()); - } -} - class HomePage extends StatefulWidget { static const String routerName = "/home"; @override - HomePageState createState() => new HomePageState(); + HomePageState createState() => HomePageState(); } class HomePageState extends State { @@ -37,17 +28,18 @@ class HomePageState extends State { AppLocalizations app; UserInfo userInfo = UserInfo(); - int _currentTabIndex = 0; int _currentNewsIndex = 0; List newsList = []; @override void initState() { - super.initState(); FA.setCurrentScreen("HomePage", "home_page.dart"); - _getAllNews(); + _getNewsAll(); _getUserInfo(); + if (Preferences.getBool(Constants.PREF_AUTO_LOGIN, false)) + Utils.checkUpdate(context); + super.initState(); } @override @@ -55,6 +47,63 @@ class HomePageState extends State { super.dispose(); } + @override + Widget build(BuildContext context) { + app = AppLocalizations.of(context); + if (state != _State.offline) _setupBusNotify(context); + return WillPopScope( + child: Scaffold( + appBar: AppBar( + title: Text(app.appName), + backgroundColor: Resource.Colors.blue, + actions: [ + IconButton( + icon: Icon(AppIcon.info), + onPressed: _showInformationDialog, + ) + ], + ), + drawer: DrawerBody(userInfo: userInfo), + body: OrientationBuilder(builder: (_, orientation) { + return Container( + padding: EdgeInsets.symmetric( + vertical: orientation == Orientation.portrait ? 32.0 : 4.0), + child: Center( + child: _homebody(orientation), + ), + ); + }), + bottomNavigationBar: BottomNavigationBar( + elevation: 12.0, + fixedColor: Resource.Colors.bottomNavigationSelect, + unselectedItemColor: Resource.Colors.bottomNavigationSelect, + type: BottomNavigationBarType.fixed, + selectedFontSize: 12.0, + unselectedFontSize: 12.0, + selectedIconTheme: IconThemeData(size: 24.0), + onTap: onTabTapped, + items: [ + BottomNavigationBarItem( + icon: Icon(AppIcon.directionsBus), + title: Text(app.bus), + ), + BottomNavigationBarItem( + icon: Icon(AppIcon.classIcon), + title: Text(app.course), + ), + BottomNavigationBarItem( + icon: Icon(AppIcon.assignment), + title: Text(app.score), + ), + ], + ), + ), + onWillPop: () async { + if (Platform.isAndroid) _showLogoutDialog(); + return false; + }); + } + Widget _newsImage(News news, Orientation orientation, bool active) { return AnimatedContainer( duration: Duration(milliseconds: 500), @@ -64,7 +113,10 @@ class HomePageState extends State { horizontal: MediaQuery.of(context).size.width * 0.02), child: GestureDetector( onTap: () { - Navigator.of(context).push(NewsContentPageRoute(news)); + Utils.pushCupertinoStyle( + context, + NewsContentPage(news), + ); String message = news.content.length > 12 ? news.content : news.content.substring(0, 12); @@ -72,10 +124,12 @@ class HomePageState extends State { }, child: Hero( tag: news.hashCode, - child: CachedNetworkImage( - imageUrl: news.image, - errorWidget: (context, url, error) => Icon(Icons.error), - ), + child: (Platform.isIOS || Platform.isAndroid) + ? CachedNetworkImage( + imageUrl: news.image, + errorWidget: (context, url, error) => Icon(Icons.error), + ) + : Image.network(news.image), ), ), ); @@ -128,27 +182,29 @@ class HomePageState extends State { ), Expanded( child: PageView.builder( - controller: pageController, - itemCount: newsList.length, - itemBuilder: (context, int currentIndex) { - bool active = (currentIndex == _currentNewsIndex); - return _newsImage( - newsList[currentIndex], orientation, active); - }), + controller: pageController, + itemCount: newsList.length, + itemBuilder: (context, int currentIndex) { + bool active = (currentIndex == _currentNewsIndex); + return _newsImage( + newsList[currentIndex], orientation, active); + }, + ), ), SizedBox(height: orientation == Orientation.portrait ? 16.0 : 4.0), RichText( textAlign: TextAlign.center, text: TextSpan( - style: TextStyle(color: Resource.Colors.grey, fontSize: 24.0), - children: [ - TextSpan( - text: - '${newsList.length >= 10 && _currentNewsIndex < 9 ? '0' : ''}' - '${_currentNewsIndex + 1}', - style: TextStyle(color: Resource.Colors.red)), - TextSpan(text: ' / ${newsList.length}'), - ]), + style: TextStyle(color: Resource.Colors.grey, fontSize: 24.0), + children: [ + TextSpan( + text: + '${newsList.length >= 10 && _currentNewsIndex < 9 ? '0' : ''}' + '${_currentNewsIndex + 1}', + style: TextStyle(color: Resource.Colors.red)), + TextSpan(text: ' / ${newsList.length}'), + ], + ), ), ], ); @@ -162,124 +218,59 @@ class HomePageState extends State { } } - @override - Widget build(BuildContext context) { - app = AppLocalizations.of(context); - if (state == _State.offline) _setupBusNotify(context); - return WillPopScope( - child: Scaffold( - appBar: AppBar( - title: Text(app.appName), - backgroundColor: Resource.Colors.blue, - actions: [ - IconButton( - icon: Icon(AppIcon.info), - onPressed: _showInformationDialog, - ) - ], - ), - drawer: DrawerBody(userInfo: userInfo), - body: OrientationBuilder(builder: (_, orientation) { - return Container( - padding: EdgeInsets.symmetric( - vertical: orientation == Orientation.portrait ? 32.0 : 4.0), - child: Center( - child: _homebody(orientation), - ), - ); - }), - bottomNavigationBar: BottomNavigationBar( - fixedColor: Resource.Colors.bottomNavigationSelect, - unselectedItemColor: Resource.Colors.bottomNavigationSelect, - type: BottomNavigationBarType.fixed, - selectedFontSize: 12.0, - unselectedFontSize: 12.0, - selectedIconTheme: IconThemeData(size: 24.0), - currentIndex: _currentTabIndex, - onTap: onTabTapped, - items: [ - BottomNavigationBarItem( - icon: Icon(AppIcon.directionsBus), - title: Text(app.bus), - ), - BottomNavigationBarItem( - icon: Icon(AppIcon.classIcon), - title: Text(app.course), - ), - BottomNavigationBarItem( - icon: Icon(AppIcon.assignment), - title: Text(app.score), - ), - ], - ), - ), - onWillPop: () async { - if (Platform.isAndroid) _showLogoutDialog(); - return false; - }); - } - void onTabTapped(int index) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - bool bus = prefs.getBool(Constants.PREF_BUS_ENABLE) ?? true; - setState(() { - _currentTabIndex = index; - switch (_currentTabIndex) { - case 0: - if (bus) - Navigator.of(context).push(BusPageRoute()); - else - Utils.showToast(context, app.canNotUseFeature); - break; - case 1: - Navigator.of(context).push(CoursePageRoute()); - break; - case 2: - Navigator.of(context).push(ScorePageRoute()); - break; - } - }); + bool bus = Preferences.getBool(Constants.PREF_BUS_ENABLE, true); + switch (index) { + case 0: + if (bus) + Utils.pushCupertinoStyle(context, BusPage()); + else + Utils.showToast(context, app.canNotUseFeature); + break; + case 1: + Utils.pushCupertinoStyle(context, CoursePage()); + break; + case 2: + Utils.pushCupertinoStyle(context, ScorePage()); + break; + } } - _getAllNews() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN)) { + _getNewsAll() async { + if (Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false)) { setState(() { state = _State.offline; }); - return; - } - state = _State.loading; - Helper.instance.getAllNews().then((newsList) { - this.newsList = newsList; - this.newsList.sort((a, b) { - return b.weight.compareTo(a.weight); - }); - setState(() { - state = newsList.length == 0 ? _State.empty : _State.finish; - }); - }).catchError((e) { - if (e is DioError) { - switch (e.type) { - case DioErrorType.RESPONSE: - Utils.handleResponseError(context, 'getAllNews', mounted, e); - break; - case DioErrorType.CANCEL: - break; - default: - state = _State.error; - Utils.handleDioError(context, e); - break; + } else + Helper.instance.getAllNews().then((newsList) { + this.newsList = newsList; + this.newsList.sort((a, b) { + return b.weight.compareTo(a.weight); + }); + setState(() { + state = newsList.length == 0 ? _State.empty : _State.finish; + }); + }).catchError((e) { + if (e is DioError) { + switch (e.type) { + case DioErrorType.RESPONSE: + Utils.handleResponseError(context, 'getAllNews', mounted, e); + break; + case DioErrorType.CANCEL: + break; + default: + state = _State.error; + Utils.handleDioError(context, e); + break; + } + } else { + throw e; } - } else { - throw e; - } - }); + }); } _setupBusNotify(BuildContext context) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_BUS_NOTIFY) ?? false) + if (Preferences.getBool(Constants.PREF_BUS_NOTIFY, false)) Helper.instance .getBusReservations() .then((BusReservationsData response) async { @@ -303,38 +294,37 @@ class HomePageState extends State { } _getUserInfo() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - if (prefs.getBool(Constants.PREF_IS_OFFLINE_LOGIN)) { + if (Preferences.getBool(Constants.PREF_IS_OFFLINE_LOGIN, false)) { userInfo = await CacheUtils.loadUserInfo(); setState(() { state = _State.offline; }); - return; - } - Helper.instance.getUsersInfo().then((response) { - if (this.mounted) { - setState(() { - userInfo = response; - }); - FA.setUserProperty('department', userInfo.department); - FA.setUserProperty('student_id', userInfo.studentId); - FA.setUserId(userInfo.studentId); - FA.logUserInfo(userInfo.department); - CacheUtils.saveUserInfo(userInfo); - } - }).catchError((e) { - if (e is DioError) { - switch (e.type) { - case DioErrorType.RESPONSE: - Utils.handleResponseError(context, 'getUserInfo', mounted, e); - break; - default: - break; + } else + Helper.instance.getUsersInfo().then((userInfo) { + if (this.mounted) { + setState(() { + this.userInfo = userInfo; + }); + FA.setUserProperty('department', userInfo.department); + FA.setUserProperty('student_id', userInfo.studentId); + FA.setUserId(userInfo.studentId); + FA.logUserInfo(userInfo.department); + ShareDataWidget.of(context).data.userInfo = userInfo; + CacheUtils.saveUserInfo(userInfo); } - } else { - throw e; - } - }); + }).catchError((e) { + if (e is DioError) { + switch (e.type) { + case DioErrorType.RESPONSE: + Utils.handleResponseError(context, 'getUserInfo', mounted, e); + break; + default: + break; + } + } else { + throw e; + } + }); } void _showLogoutDialog() { @@ -349,7 +339,9 @@ class HomePageState extends State { rightActionText: app.ok, rightActionFunction: () { Navigator.popUntil( - context, ModalRoute.withName(Navigator.defaultRouteName)); + context, + ModalRoute.withName(Navigator.defaultRouteName), + ); }, ), ); diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 338b5638..249b40f6 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -1,53 +1,50 @@ -import 'dart:io'; - import 'package:dio/dio.dart'; -import 'package:encrypt/encrypt.dart'; -import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:nkust_ap/models/api/login_response.dart'; +import 'package:nkust_ap/pages/search_student_id_page.dart'; import 'package:nkust_ap/res/assets.dart'; import 'package:nkust_ap/res/colors.dart' as Resource; import 'package:nkust_ap/utils/global.dart'; -import 'package:nkust_ap/widgets/default_dialog.dart'; +import 'package:nkust_ap/utils/preferences.dart'; import 'package:nkust_ap/widgets/drawer_body.dart'; import 'package:nkust_ap/widgets/progress_dialog.dart'; -import 'package:nkust_ap/widgets/yes_no_dialog.dart'; -import 'package:package_info/package_info.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:nkust_ap/widgets/share_data_widget.dart'; class LoginPage extends StatefulWidget { static const String routerName = "/login"; @override - LoginPageState createState() => new LoginPageState(); + LoginPageState createState() => LoginPageState(); } class LoginPageState extends State with SingleTickerProviderStateMixin { AppLocalizations app; - SharedPreferences prefs; - final TextEditingController _username = new TextEditingController(); - final TextEditingController _password = new TextEditingController(); + final TextEditingController _username = TextEditingController(); + final TextEditingController _password = TextEditingController(); var isRememberPassword = true; var isAutoLogin = false; FocusNode usernameFocusNode; FocusNode passwordFocusNode; - final encrypter = - Encrypter(AES(Constants.key, Constants.iv, mode: AESMode.cbc)); + TextStyle get _editTextStyle => TextStyle( + color: Colors.white, + fontSize: 18.0, + decorationColor: Colors.white, + ); @override void initState() { - super.initState(); FA.setCurrentScreen("LoginPage", "login_page.dart"); usernameFocusNode = FocusNode(); passwordFocusNode = FocusNode(); - if (Platform.isAndroid || Platform.isIOS) { - _getPreference(); - _checkUpdate(); - } + _getPreference(); + if (!Preferences.getBool(Constants.PREF_AUTO_LOGIN, false)) + Utils.checkUpdate(context); + super.initState(); } @override @@ -55,38 +52,37 @@ class LoginPageState extends State super.dispose(); } - _editTextStyle() => new TextStyle( - color: Colors.white, fontSize: 18.0, decorationColor: Colors.white); - @override Widget build(BuildContext context) { app = AppLocalizations.of(context); - return OrientationBuilder(builder: (_, orientation) { - return Scaffold( - resizeToAvoidBottomPadding: orientation == Orientation.portrait, - backgroundColor: Resource.Colors.blue, - body: Center( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 30.0), - child: orientation == Orientation.portrait - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.min, - children: _renderContent(orientation), - ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: _renderContent(orientation), - ), + return OrientationBuilder( + builder: (_, orientation) { + return Scaffold( + resizeToAvoidBottomPadding: orientation == Orientation.portrait, + backgroundColor: Resource.Colors.blue, + body: Center( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: orientation == Orientation.portrait + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: _renderContent(orientation), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _renderContent(orientation), + ), + ), ), - ), - ); - }); + ); + }, + ); } _renderContent(Orientation orientation) { - List list = orientation == Orientation.portrait + List section = orientation == Orientation.portrait ? [ Center( child: Image.asset( @@ -107,7 +103,7 @@ class LoginPageState extends State ), SizedBox(height: orientation == Orientation.portrait ? 30.0 : 0.0), ]; - List listB = [ + List sectionInput = [ TextField( maxLines: 1, controller: _username, @@ -120,7 +116,7 @@ class LoginPageState extends State decoration: InputDecoration( labelText: app.username, ), - style: _editTextStyle(), + style: _editTextStyle, ), TextField( obscureText: true, @@ -135,7 +131,7 @@ class LoginPageState extends State decoration: InputDecoration( labelText: app.password, ), - style: _editTextStyle(), + style: _editTextStyle, ), SizedBox(height: 8.0), Row( @@ -156,7 +152,10 @@ class LoginPageState extends State onChanged: _onAutoLoginChanged, ), ), - Text(app.autoLogin, style: TextStyle(color: Colors.white)) + Text( + app.autoLogin, + style: TextStyle(color: Colors.white), + ) ], ), onTap: () => _onAutoLoginChanged(!isAutoLogin), @@ -176,7 +175,10 @@ class LoginPageState extends State onChanged: _onRememberPasswordChanged, ), ), - Text(app.remember, style: TextStyle(color: Colors.white)) + Text( + app.remember, + style: TextStyle(color: Colors.white), + ) ], ), onTap: () => _onRememberPasswordChanged(!isRememberPassword), @@ -206,6 +208,7 @@ class LoginPageState extends State ), Center( child: FlatButton( + padding: EdgeInsets.all(0.0), onPressed: () { _offlineLogin(); }, @@ -214,141 +217,52 @@ class LoginPageState extends State style: TextStyle(color: Colors.white, fontSize: 16.0), ), ), - ) + ), + Center( + child: FlatButton( + padding: EdgeInsets.all(0.0), + onPressed: () async { + var username = await Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => SearchStudentIdPage(), + ), + ); + if (username != null && username is String) { + setState(() { + _username.text = username; + }); + Utils.showToast(context, app.firstLoginHint); + } + }, + child: Text( + app.searchUsername, + style: TextStyle(color: Colors.white, fontSize: 16.0), + ), + ), + ), ]; if (orientation == Orientation.portrait) { - list.addAll(listB); + section.addAll(sectionInput); } else { - list.add(Expanded( + section.add( + Expanded( child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: listB))); - } - return list; - } - - _checkUpdate() async { - prefs = await SharedPreferences.getInstance(); - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - await Future.delayed(Duration(milliseconds: 50)); - var currentVersion = prefs.getString(Constants.PREF_CURRENT_VERSION) ?? ""; - if (currentVersion != packageInfo.buildNumber) { - showDialog( - context: context, - builder: (BuildContext context) => DefaultDialog( - title: app.updateNoteTitle, - contentWidget: Text( - "v${packageInfo.version}\n" - "${app.updateNoteContent}", - textAlign: TextAlign.center, - style: TextStyle(color: Resource.Colors.grey), + mainAxisAlignment: MainAxisAlignment.center, + children: sectionInput, ), - actionText: app.iKnow, - actionFunction: () => - Navigator.of(context, rootNavigator: true).pop('dialog'), ), ); - prefs.setString(Constants.PREF_CURRENT_VERSION, packageInfo.buildNumber); - } - if (!Constants.isInDebugMode) { - final RemoteConfig remoteConfig = await RemoteConfig.instance; - try { - await remoteConfig.fetch(expiration: const Duration(seconds: 10)); - await remoteConfig.activateFetched(); - } on FetchThrottledException catch (exception) {} catch (exception) {} - String url = ""; - int versionDiff = 0, newVersion; - if (Platform.isAndroid) { - url = "market://details?id=${packageInfo.packageName}"; - newVersion = remoteConfig.getInt(Constants.ANDROID_APP_VERSION); - } else if (Platform.isIOS) { - url = - "itms-apps://itunes.apple.com/tw/app/apple-store/id1439751462?mt=8"; - newVersion = remoteConfig.getInt(Constants.IOS_APP_VERSION); - } else { - url = "https://www.facebook.com/NKUST.ITC/"; - newVersion = remoteConfig.getInt(Constants.APP_VERSION); - } - versionDiff = newVersion - int.parse(packageInfo.buildNumber); - String versionContent = - "\nv${newVersion ~/ 10000}.${newVersion % 1000 ~/ 100}.${newVersion % 100}\n"; - switch (AppLocalizations.locale.languageCode) { - case 'zh': - versionContent += - remoteConfig.getString(Constants.NEW_VERSION_CONTENT_ZH); - break; - default: - versionContent += - remoteConfig.getString(Constants.NEW_VERSION_CONTENT_EN); - break; - } - if (versionDiff < 5 && versionDiff > 0) { - showDialog( - context: context, - builder: (BuildContext context) => YesNoDialog( - title: app.updateTitle, - contentWidget: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: TextStyle( - color: Resource.Colors.grey, height: 1.3, fontSize: 16.0), - children: [ - TextSpan( - text: - '${Utils.getPlatformUpdateContent(app)}\n${versionContent.replaceAll('\\n', '\n')}', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ]), - ), - leftActionText: app.skip, - rightActionText: app.update, - leftActionFunction: null, - rightActionFunction: () { - Utils.launchUrl(url); - }, - ), - ); - } else if (versionDiff >= 5) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) => WillPopScope( - child: DefaultDialog( - title: app.updateTitle, - actionText: app.update, - contentWidget: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: TextStyle( - color: Resource.Colors.grey, - height: 1.3, - fontSize: 16.0), - children: [ - TextSpan( - text: - '${Utils.getPlatformUpdateContent(app)}\n${versionContent.replaceAll('\\n', '\n')}', - style: TextStyle(fontWeight: FontWeight.bold)), - ]), - ), - actionFunction: () { - Utils.launchUrl(url); - }), - onWillPop: () async { - return false; - }, - ), - ); - } } + return section; } _onRememberPasswordChanged(bool value) async { setState(() { isRememberPassword = value; if (!isRememberPassword) isAutoLogin = false; - if (Platform.isAndroid || Platform.isIOS) { - prefs.setBool(Constants.PREF_AUTO_LOGIN, isAutoLogin); - prefs.setBool(Constants.PREF_REMEMBER_PASSWORD, isRememberPassword); - } + Preferences.setBool(Constants.PREF_AUTO_LOGIN, isAutoLogin); + Preferences.setBool(Constants.PREF_REMEMBER_PASSWORD, isRememberPassword); }); } @@ -356,36 +270,20 @@ class LoginPageState extends State setState(() { isAutoLogin = value; isRememberPassword = isAutoLogin; - if (Platform.isAndroid || Platform.isIOS) { - prefs.setBool(Constants.PREF_AUTO_LOGIN, isAutoLogin); - prefs.setBool(Constants.PREF_REMEMBER_PASSWORD, isRememberPassword); - } + Preferences.setBool(Constants.PREF_AUTO_LOGIN, isAutoLogin); + Preferences.setBool(Constants.PREF_REMEMBER_PASSWORD, isRememberPassword); }); } _getPreference() async { - prefs = await SharedPreferences.getInstance(); isRememberPassword = - prefs.getBool(Constants.PREF_REMEMBER_PASSWORD) ?? true; - isAutoLogin = prefs.getBool(Constants.PREF_AUTO_LOGIN) ?? false; - var username = prefs.getString(Constants.PREF_USERNAME) ?? ""; - var password = ""; - if (isRememberPassword) { - var encryptPassword = prefs.getString(Constants.PREF_PASSWORD) ?? ""; - if (encryptPassword != "") { - try { - password = encrypter.decrypt64(encryptPassword); - } catch (e) { - FA.logAESErrorEvent(encryptPassword); - password = encryptPassword; - await prefs.setString(Constants.PREF_PASSWORD, - encrypter.encrypt(encryptPassword).base64); - } - } - } + Preferences.getBool(Constants.PREF_REMEMBER_PASSWORD, true); + isAutoLogin = Preferences.getBool(Constants.PREF_AUTO_LOGIN, false); setState(() { - _username.text = username; - _password.text = password; + _username.text = Preferences.getString(Constants.PREF_USERNAME, ''); + _password.text = isRememberPassword + ? Preferences.getStringSecurity(Constants.PREF_PASSWORD, '') + : ''; }); await Future.delayed(Duration(microseconds: 50)); if (isAutoLogin) { @@ -398,37 +296,37 @@ class LoginPageState extends State Utils.showToast(context, app.doNotEmpty); } else { showDialog( - context: context, - builder: (BuildContext context) => WillPopScope( - child: ProgressDialog(app.logining), - onWillPop: () async { - return false; - }), - barrierDismissible: false); - - if (Platform.isAndroid || Platform.isIOS) - prefs.setString(Constants.PREF_USERNAME, _username.text); + context: context, + builder: (BuildContext context) => WillPopScope( + child: ProgressDialog(app.logining), + onWillPop: () async { + return false; + }), + barrierDismissible: false, + ); + Preferences.setString(Constants.PREF_USERNAME, _username.text); Helper.instance .login(_username.text, _password.text) .then((LoginResponse response) async { - if (Navigator.canPop(context)) Navigator.pop(context, 'dialog'); - - if (Platform.isAndroid || Platform.isIOS) { - if (response.isLogin != null) { - prefs.setBool(Constants.PREF_AP_ENABLE, response.isLogin.ap); - prefs.setBool(Constants.PREF_BUS_ENABLE, response.isLogin.bus); - prefs.setBool(Constants.PREF_LEAVE_ENABLE, response.isLogin.leave); - } - prefs.setString(Constants.PREF_USERNAME, _username.text); - if (isRememberPassword) { - await prefs.setString(Constants.PREF_PASSWORD, - encrypter.encrypt(_password.text).base64); - } - prefs.setBool(Constants.PREF_IS_OFFLINE_LOGIN, false); + if (Navigator.canPop(context)) + Navigator.of(context, rootNavigator: true).pop(); + if (response.isLogin != null) { + Preferences.setBool(Constants.PREF_AP_ENABLE, response.isLogin.ap); + Preferences.setBool(Constants.PREF_BUS_ENABLE, response.isLogin.bus); + Preferences.setBool( + Constants.PREF_LEAVE_ENABLE, response.isLogin.leave); } + Preferences.setString(Constants.PREF_USERNAME, _username.text); + if (isRememberPassword) { + Preferences.setStringSecurity( + Constants.PREF_PASSWORD, _password.text); + } + Preferences.setBool(Constants.PREF_IS_OFFLINE_LOGIN, false); + ShareDataWidget.of(context).data.isLogin = true; _navigateToFilterObject(context); }).catchError((e) { - if (Navigator.canPop(context)) Navigator.pop(context, 'dialog'); + if (Navigator.canPop(context)) + Navigator.of(context, rootNavigator: true).pop(); if (e is DioError) { switch (e.type) { case DioErrorType.RESPONSE: @@ -450,24 +348,16 @@ class LoginPageState extends State } _offlineLogin() async { - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - String encryptPassword = prefs.getString(Constants.PREF_PASSWORD) ?? ''; - if (username.isEmpty || encryptPassword.isEmpty) { + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + String password = + Preferences.getStringSecurity(Constants.PREF_PASSWORD, ''); + if (username.isEmpty || password.isEmpty) { Utils.showToast(context, app.noOfflineLoginData); } else { - String password = ''; - try { - password = encrypter.decrypt64(encryptPassword); - } catch (e) { - FA.logAESErrorEvent(encryptPassword); - password = encryptPassword; - await prefs.setString( - Constants.PREF_PASSWORD, encrypter.encrypt(encryptPassword).base64); - } if (username != _username.text || password != _password.text) Utils.showToast(context, app.offlineLoginPasswordError); else { - prefs.setBool(Constants.PREF_IS_OFFLINE_LOGIN, true); + Preferences.setBool(Constants.PREF_IS_OFFLINE_LOGIN, true); Utils.showToast(context, app.loadOfflineData); _navigateToFilterObject(context); } @@ -476,14 +366,17 @@ class LoginPageState extends State _navigateToFilterObject(BuildContext context) async { final result = await Navigator.push( - context, MaterialPageRoute(builder: (context) => HomePage())); + context, + MaterialPageRoute( + builder: (context) => HomePage(), + ), + ); print(result); clearSetting(); } void clearSetting() async { - var prefs = await SharedPreferences.getInstance(); - prefs.setBool(Constants.PREF_AUTO_LOGIN, false); + Preferences.setBool(Constants.PREF_AUTO_LOGIN, false); setState(() { isAutoLogin = false; pictureBytes = null; diff --git a/lib/pages/search_student_id_page.dart b/lib/pages/search_student_id_page.dart new file mode 100644 index 00000000..6daa982f --- /dev/null +++ b/lib/pages/search_student_id_page.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:nkust_ap/models/user_info.dart'; +import 'package:nkust_ap/res/assets.dart'; +import 'package:nkust_ap/res/colors.dart' as Resource; +import 'package:nkust_ap/utils/app_localizations.dart'; +import 'package:nkust_ap/utils/firebase_analytics_utils.dart'; +import 'package:nkust_ap/utils/nkust_helper.dart'; +import 'package:nkust_ap/utils/utils.dart'; +import 'package:nkust_ap/widgets/default_dialog.dart'; +import 'package:sprintf/sprintf.dart'; + +class SearchStudentIdPage extends StatefulWidget { + static const String routerName = "/searchUsername"; + + @override + SearchStudentIdPageState createState() => SearchStudentIdPageState(); +} + +class SearchStudentIdPageState extends State { + final TextEditingController _id = TextEditingController(); + + AppLocalizations app; + + FocusNode idFocusNode; + bool isAutoFill = true; + + @override + void initState() { + super.initState(); + FA.setCurrentScreen( + "SearchUsernamePagePage", "search_student_id_page.dart"); + idFocusNode = FocusNode(); + } + + @override + void dispose() { + super.dispose(); + } + + _editTextStyle() => TextStyle( + color: Colors.white, fontSize: 18.0, decorationColor: Colors.white); + + @override + Widget build(BuildContext context) { + app = AppLocalizations.of(context); + return OrientationBuilder( + builder: (_, orientation) { + return Scaffold( + backgroundColor: Resource.Colors.blue, + resizeToAvoidBottomPadding: orientation == Orientation.portrait, + body: Container( + alignment: Alignment(0, 0), + padding: EdgeInsets.symmetric(horizontal: 30.0), + child: orientation == Orientation.portrait + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.min, + children: _renderContent(orientation), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _renderContent(orientation), + ), + ), + ); + }, + ); + } + + _renderContent(Orientation orientation) { + List list = orientation == Orientation.portrait + ? [ + Center( + child: Image.asset( + ImageAssets.K, + width: 120.0, + height: 120.0, + ), + ), + SizedBox(height: orientation == Orientation.portrait ? 30.0 : 0.0), + ] + : [ + Expanded( + child: Image.asset( + ImageAssets.K, + width: 120.0, + height: 120.0, + ), + ), + SizedBox(height: orientation == Orientation.portrait ? 30.0 : 0.0), + ]; + List listB = [ + TextField( + maxLines: 1, + textInputAction: TextInputAction.send, + controller: _id, + focusNode: idFocusNode, + onSubmitted: (text) { + idFocusNode.unfocus(); + _search(); + }, + decoration: InputDecoration( + labelText: app.id, + ), + style: _editTextStyle(), + ), + SizedBox(height: 8.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Theme( + data: ThemeData( + unselectedWidgetColor: Colors.white, + ), + child: Checkbox( + activeColor: Colors.white, + checkColor: Color(0xff2574ff), + value: isAutoFill, + onChanged: _onAutoFillChanged, + ), + ), + Text( + app.autoFill, + style: TextStyle(color: Colors.white), + ) + ], + ), + onTap: () => _onAutoFillChanged(!isAutoFill), + ), + ], + ), + SizedBox(height: 8.0), + Container( + width: double.infinity, + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(30.0), + ), + ), + padding: EdgeInsets.all(14.0), + onPressed: () { + FA.logAction('search_username', 'click'); + _search(); + }, + color: Colors.white, + child: Text( + app.search, + style: TextStyle(color: Resource.Colors.blue, fontSize: 18.0), + ), + ), + ), + ]; + if (orientation == Orientation.portrait) { + list.addAll(listB); + } else { + list.add( + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: listB, + ), + ), + ); + } + return list; + } + + _onAutoFillChanged(bool value) async { + setState(() { + isAutoFill = value; + }); + } + + _search() async { + if (_id.text.isEmpty) { + Utils.showToast(context, app.doNotEmpty); + } else { + UserInfo result = await NKUSTHelper.instance.getUsername(_id.text); + if (result != null && isAutoFill) { + Navigator.pop(context, result.studentId); + } else { + showDialog( + context: context, + builder: (BuildContext context) => DefaultDialog( + title: app.searchResult, + actionText: app.iKnow, + actionFunction: () => + Navigator.of(context, rootNavigator: true).pop('dialog'), + contentWidget: RichText( + text: TextSpan( + style: TextStyle( + color: Resource.Colors.grey, + height: 1.3, + fontSize: 16.0, + ), + children: [ + TextSpan( + text: result == null + ? app.searchStudentIdError + : sprintf( + app.searchStudentIdFormat, + [ + result.studentNameCht, + result.studentId, + ], + ), + style: TextStyle(fontWeight: FontWeight.bold), + ), + if (result != null) + TextSpan( + text: '\n${app.firstLoginHint}', + ), + ], + ), + ), + ), + ); + } + } + } +} diff --git a/lib/res/assets.dart b/lib/res/assets.dart index 9cacb54c..82483614 100644 --- a/lib/res/assets.dart +++ b/lib/res/assets.dart @@ -20,6 +20,13 @@ class ImageAssets { static const String dashLineLight = '$basePath/dash_line.webp'; static const String dashLineDarkTheme = '$basePath/dash_line_dark_theme.webp'; + static String sectionJiangong = '$basePath/section_jiangong.webp'; + static String sectionYanchao = '$basePath/section_yanchao.webp'; + static String sectionFirst1 = '$basePath/section_first1.webp'; + static String sectionFirst2 = '$basePath/section_first2.webp'; + static String sectionNanzi = '$basePath/section_nanzi.webp'; + static String sectionQijin = '$basePath/section_qijin.webp'; + static String get dashLine { switch (AppTheme.code) { case AppTheme.DARK: diff --git a/lib/utils/app_localizations.dart b/lib/utils/app_localizations.dart index 9a310d8f..52f549e2 100644 --- a/lib/utils/app_localizations.dart +++ b/lib/utils/app_localizations.dart @@ -64,7 +64,7 @@ class AppLocalizations { 'app_name': 'NKUST AP', 'update_note_title': 'Update Notes', 'update_note_content': - '1.Add dark theme(switch in setting).\n2.Add new icon style(switch in setting).\n3.Imporve pick semester dialog UI.\n4.Fix some error.', + '1.Add search student id page.\n2.Page can swipe to back(Android).\n3.Add other section picture\n4.Fix some error.', 'splash_content': '我們全都包了\n只剩下學校不包我們', 'share': 'Share', 'teacher_confirm_title': 'Are you a teacher?', @@ -351,12 +351,22 @@ class AppLocalizations { 'iconStyle': 'Icon Style', 'filled': 'Filled', 'outlined': 'Outlined', + 'searchUsername': 'Search Student ID', + 'search': 'Search', + 'name': 'Name', + 'id': 'id', + 'searchResult': 'Result', + 'autoFill': 'Auto Fill', + 'firstLoginHint': + 'For first-time login, please fill in the last four number of your ID as your password', + 'searchStudentIdFormat': 'Name:%s\nStudent ID:%s\n', + 'searchStudentIdError': 'Search no data.', }, 'zh': { 'app_name': '高科校務通', 'update_note_title': '更新日誌', 'update_note_content': - '1.新增深色主題(可至設定切換)\n2.推出新風格圖案(可至設定切換)\n3.優化選擇學期列表對話框\n4.修正部分錯誤', + '1.新增查詢學號頁面\n2.可滑動返回上一頁(Android)\n3.新增其他校區照片\n4.修正部分錯誤', 'splash_content': '我們全都包了\n只剩下學校不包我們', 'share': '分享', 'teacher_confirm_title': '您是老師嗎?', @@ -625,6 +635,15 @@ class AppLocalizations { 'iconStyle': '圖案風格', 'filled': '填充', 'outlined': '輪廓', + 'searchUsername': '學號查詢', + 'search': '查詢', + 'name': '名字', + 'id': '身分證字號', + 'searchResult': '查詢結果', + 'autoFill': '自動填入', + 'firstLoginHint': '首次登入密碼預設為身分證末四碼', + 'searchStudentIdFormat': '姓名:%s\n學號:%s\n', + 'searchStudentIdError': '查無資料', }, }; @@ -909,7 +928,14 @@ class AppLocalizations { String get appVersion => _vocabularies['app_version']; - String get updateContent => _vocabularies['update_content']; + String get updateContent { + if (Platform.isAndroid) + return updateAndroidContent; + else if (Platform.isIOS) + return updateIOSContent; + else + return _vocabularies['update_content']; + } String get updateAndroidContent => _vocabularies['update_android_content']; @@ -1099,6 +1125,24 @@ class AppLocalizations { String get filled => _vocabularies['filled']; String get outlined => _vocabularies['outlined']; + + String get searchUsername => _vocabularies['searchUsername']; + + String get search => _vocabularies['search']; + + String get name => _vocabularies['name']; + + String get id => _vocabularies['id']; + + String get searchResult => _vocabularies['searchResult']; + + String get autoFill => _vocabularies['autoFill']; + + String get firstLoginHint => _vocabularies['firstLoginHint']; + + String get searchStudentIdFormat => _vocabularies['searchStudentIdFormat']; + + String get searchStudentIdError => _vocabularies['searchError']; } class AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/utils/cache_utils.dart b/lib/utils/cache_utils.dart index af83af70..8e99d61e 100644 --- a/lib/utils/cache_utils.dart +++ b/lib/utils/cache_utils.dart @@ -10,105 +10,94 @@ import 'package:nkust_ap/models/schedule_data.dart'; import 'package:nkust_ap/models/score_data.dart'; import 'package:nkust_ap/models/semester_data.dart'; import 'package:nkust_ap/models/user_info.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:nkust_ap/utils/preferences.dart'; class CacheUtils { static void saveSemesterData(SemesterData semesterData) async { if (semesterData == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setString( + Preferences.setString( Constants.PREF_SEMESTER_DATA, jsonEncode(semesterData)); } static Future loadSemesterData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String json = prefs.getString(Constants.PREF_SEMESTER_DATA) ?? ""; - if (json == "") return null; + String json = Preferences.getString(Constants.PREF_SEMESTER_DATA, ''); + if (json == '') return null; SemesterData semesterData = SemesterData.fromJson(jsonDecode(json)); return semesterData; } static void saveCourseData(String value, CourseData courseData) async { if (courseData == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString('${Constants.PREF_COURSE_DATA}_${username}_$value', + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( + '${Constants.PREF_COURSE_DATA}_${username}_$value', jsonEncode(courseData)); } static Future loadCourseData(String value) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - String json = - prefs.getString('${Constants.PREF_COURSE_DATA}_${username}_$value') ?? - ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + String json = Preferences.getString( + '${Constants.PREF_COURSE_DATA}_${username}_$value', ''); if (json == '') return null; return CourseData.fromJson(jsonDecode(json)); } static void saveScoreData(String value, ScoreData scoreData) async { if (scoreData == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString('${Constants.PREF_SCORE_DATA}_${username}_$value', + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( + '${Constants.PREF_SCORE_DATA}_${username}_$value', jsonEncode(scoreData)); } static Future loadScoreData(String value) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - String json = - prefs.getString('${Constants.PREF_SCORE_DATA}_${username}_$value') ?? - ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + String json = Preferences.getString( + '${Constants.PREF_SCORE_DATA}_${username}_$value', ''); if (json == '') return null; return ScoreData.fromJson(jsonDecode(json)); } static void saveScheduleDataList(List scheduleDataList) async { if (scheduleDataList == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setString( + await Preferences.setString( Constants.PREF_SCHEDULE_DATA, jsonEncode(scheduleDataList)); } static Future> loadScheduleDataList() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String json = prefs.getString(Constants.PREF_SCHEDULE_DATA) ?? ''; + String json = Preferences.getString(Constants.PREF_SCHEDULE_DATA, ''); if (json == '') return null; return ScheduleData.toList(jsonDecode(json)); } static void saveUserInfo(UserInfo userInfo) async { if (userInfo == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString( + + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( '${Constants.PREF_USER_INFO}_$username', jsonEncode(userInfo)); } static Future loadUserInfo() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); String json = - prefs.getString('${Constants.PREF_USER_INFO}_$username') ?? ''; + Preferences.getString('${Constants.PREF_USER_INFO}_$username', ''); if (json == '') return null; return UserInfo.fromJson(jsonDecode(json)); } static void saveLeaveData(String value, LeaveResponse leaveData) async { if (leaveData == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString('${Constants.PREF_LEAVE_DATA}_${username}_$value', + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( + '${Constants.PREF_LEAVE_DATA}_${username}_$value', jsonEncode(leaveData)); } static Future loadLeaveData(String value) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - String json = - prefs.getString('${Constants.PREF_LEAVE_DATA}_${username}_$value') ?? - ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + String json = Preferences.getString( + '${Constants.PREF_LEAVE_DATA}_${username}_$value', ''); if (json == '') return null; return LeaveResponse.fromJson(jsonDecode(json)); } @@ -116,35 +105,32 @@ class CacheUtils { static void saveBusReservationsData( BusReservationsData busReservationsData) async { if (busReservationsData == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString('${Constants.PREF_BUS_RESERVATIONS_DATA}_$username', + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( + '${Constants.PREF_BUS_RESERVATIONS_DATA}_$username', jsonEncode(busReservationsData)); } static Future loadBusReservationsData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - String json = - prefs.getString('${Constants.PREF_BUS_RESERVATIONS_DATA}_$username') ?? - ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + String json = Preferences.getString( + '${Constants.PREF_BUS_RESERVATIONS_DATA}_$username', ''); if (json == '') return null; return BusReservationsData.fromJson(jsonDecode(json)); } static void savePictureData(Uint8List bytes) async { if (bytes == null) return; - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; - await prefs.setString( + + String username = Preferences.getString(Constants.PREF_USERNAME, ''); + await Preferences.setString( '${Constants.PREF_PICTURE_DATA}_$username', base64.encode(bytes)); } static Future loadPictureData() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String username = prefs.getString(Constants.PREF_USERNAME) ?? ''; + String username = Preferences.getString(Constants.PREF_USERNAME, ''); String base64String = - prefs.getString('${Constants.PREF_PICTURE_DATA}_$username') ?? ''; + Preferences.getString('${Constants.PREF_PICTURE_DATA}_$username', ''); if (base64String == '') return null; return base64.decode(base64String); } diff --git a/lib/utils/firebase_analytics_utils.dart b/lib/utils/firebase_analytics_utils.dart index 9d001e7e..645459fd 100644 --- a/lib/utils/firebase_analytics_utils.dart +++ b/lib/utils/firebase_analytics_utils.dart @@ -88,8 +88,8 @@ class FA { static Future logAction(String name, String action, {String message = ''}) async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - if (Platform.isIOS || Platform.isAndroid) + if (Platform.isIOS || Platform.isAndroid) { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); await analytics.logEvent( name: name ?? '', parameters: { @@ -99,5 +99,6 @@ class FA { 'platform': Platform.operatingSystem, }, ); + } } } diff --git a/lib/utils/nkust_helper.dart b/lib/utils/nkust_helper.dart new file mode 100644 index 00000000..ea6dd975 --- /dev/null +++ b/lib/utils/nkust_helper.dart @@ -0,0 +1,40 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:html/parser.dart'; +import 'package:nkust_ap/models/user_info.dart'; + +class NKUSTHelper { + static NKUSTHelper _instance; + + static NKUSTHelper get instance { + if (_instance == null) { + _instance = NKUSTHelper(); + } + return _instance; + } + + Future getUsername(String rocId) async { + final client = HttpClient(); + List bodyBytes = utf8.encode("uid=$rocId"); // utf8 encode + final request = await client.postUrl( + Uri.parse('https://webap.nkust.edu.tw/nkust/system/getuid_1.jsp'), + ); + request.headers.add('Connection', 'keep-alive'); + request.headers.add('Content-Length', "uid=$rocId".length.toString()); + request.headers.add('Content-Type', 'application/x-www-form-urlencoded'); + request.add(bodyBytes); + final response = await request.close(); + var text = await utf8.decoder.bind(response).first; + var document = parse(text); + var elements = document.getElementsByTagName('b'); + if (elements.length >= 4) + return UserInfo( + studentId: elements[4].text.replaceAll(' ', ''), + studentNameCht: elements[2].text, + ); + else + return null; + } +} diff --git a/lib/utils/preferences.dart b/lib/utils/preferences.dart index e2177996..f069f8be 100644 --- a/lib/utils/preferences.dart +++ b/lib/utils/preferences.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:encrypt/encrypt.dart'; import 'package:nkust_ap/config/constants.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -14,15 +16,16 @@ class Preferences { static SharedPreferences prefs; static init() async { - prefs = await SharedPreferences.getInstance(); + if (Platform.isIOS || Platform.isAndroid) + prefs = await SharedPreferences.getInstance(); } static Future setStringSecurity(String key, String data) async { - await prefs.setString(key, encrypter.encrypt(data).base64); + await prefs?.setString(key, encrypter.encrypt(data).base64); } static String getStringSecurity(String key, String defaultValue) { - String data = prefs.getString(key) ?? ''; + String data = prefs?.getString(key) ?? ''; if (data == '') return defaultValue; else @@ -30,42 +33,42 @@ class Preferences { } static Future setString(String key, String data) async { - await prefs.setString(key, data); + await prefs?.setString(key, data); } static String getString(String key, String defaultValue) { - return prefs.getString(key) ?? defaultValue; + return prefs?.getString(key) ?? defaultValue; } static Future setInt(String key, int data) async { - await prefs.setInt(key, data); + await prefs?.setInt(key, data); } static int getInt(String key, int defaultValue) { - return prefs.getInt(key) ?? defaultValue; + return prefs?.getInt(key) ?? defaultValue; } static Future setDouble(String key, double data) async { - await prefs.setDouble(key, data); + await prefs?.setDouble(key, data); } static double getDouble(String key, double defaultValue) { - return prefs.getDouble(key) ?? defaultValue; + return prefs?.getDouble(key) ?? defaultValue; } static Future setBool(String key, bool data) async { - await prefs.setBool(key, data); + await prefs?.setBool(key, data); } static bool getBool(String key, bool defaultValue) { - return prefs.getBool(key) ?? defaultValue; + return prefs?.getBool(key) ?? defaultValue; } static Future setStringList(String key, List data) async { - await prefs.setStringList(key, data); + await prefs?.setStringList(key, data); } static List getStringList(String key, List defaultValue) { - return prefs.getStringList(key) ?? defaultValue; + return prefs?.getStringList(key) ?? defaultValue; } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 5ac53e46..c4cf259e 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -3,6 +3,8 @@ import 'dart:io'; import 'package:app_review/app_review.dart'; import 'package:connectivity/connectivity.dart'; import 'package:dio/dio.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:flutter/cupertino.dart' show CupertinoPageRoute; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:nkust_ap/config/constants.dart'; @@ -11,6 +13,8 @@ import 'package:nkust_ap/models/course_data.dart'; import 'package:nkust_ap/res/resource.dart' as Resource; import 'package:nkust_ap/utils/app_localizations.dart'; import 'package:nkust_ap/utils/firebase_analytics_utils.dart'; +import 'package:nkust_ap/utils/preferences.dart'; +import 'package:nkust_ap/widgets/default_dialog.dart'; import 'package:nkust_ap/widgets/yes_no_dialog.dart'; import 'package:package_info/package_info.dart'; import 'package:share/share.dart'; @@ -59,13 +63,13 @@ class Utils { ); } - static String getPlatformUpdateContent(AppLocalizations app) { + static String getPlatformUpdateContent(BuildContext context) { if (Platform.isAndroid) - return app.updateAndroidContent; + return AppLocalizations.of(context).updateAndroidContent; else if (Platform.isIOS) - return app.updateIOSContent; + return AppLocalizations.of(context).updateIOSContent; else - return app.updateContent; + return AppLocalizations.of(context).updateContent; } static void showSnackBarBar( @@ -393,4 +397,134 @@ class Utils { //TODO implement other platform system local notification } } + + static checkUpdate(BuildContext context) async { + await Future.delayed( + Duration(milliseconds: 50), + ); + if (!(Platform.isAndroid || Platform.isIOS)) return; + final app = AppLocalizations.of(context); + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + var currentVersion = + Preferences.getString(Constants.PREF_CURRENT_VERSION, ''); + if (currentVersion != packageInfo.buildNumber) { + showDialog( + context: context, + builder: (BuildContext context) => DefaultDialog( + title: app.updateNoteTitle, + contentWidget: Text( + "v${packageInfo.version}\n" + "${app.updateNoteContent}", + textAlign: TextAlign.center, + style: TextStyle(color: Resource.Colors.grey), + ), + actionText: app.iKnow, + actionFunction: () => + Navigator.of(context, rootNavigator: true).pop('dialog'), + ), + ); + Preferences.setString( + Constants.PREF_CURRENT_VERSION, packageInfo.buildNumber); + } + if (!Constants.isInDebugMode) { + final RemoteConfig remoteConfig = await RemoteConfig.instance; + try { + await remoteConfig.fetch( + expiration: const Duration(seconds: 10), + ); + await remoteConfig.activateFetched(); + } on FetchThrottledException catch (exception) {} catch (exception) {} + String url = ""; + int versionDiff = 0, newVersion; + if (Platform.isAndroid) { + url = "market://details?id=${packageInfo.packageName}"; + newVersion = remoteConfig.getInt(Constants.ANDROID_APP_VERSION); + } else if (Platform.isIOS) { + url = + "itms-apps://itunes.apple.com/tw/app/apple-store/id1439751462?mt=8"; + newVersion = remoteConfig.getInt(Constants.IOS_APP_VERSION); + } else { + url = "https://www.facebook.com/NKUST.ITC/"; + newVersion = remoteConfig.getInt(Constants.APP_VERSION); + } + versionDiff = newVersion - int.parse(packageInfo.buildNumber); + String versionContent = + "\nv${newVersion ~/ 10000}.${newVersion % 1000 ~/ 100}.${newVersion % 100}\n"; + switch (AppLocalizations.locale.languageCode) { + case 'zh': + versionContent += + remoteConfig.getString(Constants.NEW_VERSION_CONTENT_ZH); + break; + default: + versionContent += + remoteConfig.getString(Constants.NEW_VERSION_CONTENT_EN); + break; + } + if (versionDiff < 5 && versionDiff > 0) { + showDialog( + context: context, + builder: (BuildContext context) => YesNoDialog( + title: app.updateTitle, + contentWidget: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + color: Resource.Colors.grey, height: 1.3, fontSize: 16.0), + children: [ + TextSpan( + text: '${app.updateContent}\n' + '${versionContent.replaceAll('\\n', '\n')}', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ]), + ), + leftActionText: app.skip, + rightActionText: app.update, + leftActionFunction: null, + rightActionFunction: () { + Utils.launchUrl(url); + }, + ), + ); + } else if (versionDiff >= 5) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) => WillPopScope( + child: DefaultDialog( + title: app.updateTitle, + actionText: app.update, + contentWidget: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + color: Resource.Colors.grey, + height: 1.3, + fontSize: 16.0), + children: [ + TextSpan( + text: '${app.updateContent}\n' + '${versionContent.replaceAll('\\n', '\n')}', + style: TextStyle(fontWeight: FontWeight.bold)), + ]), + ), + actionFunction: () { + Utils.launchUrl(url); + }), + onWillPop: () async { + return false; + }, + ), + ); + } + } + } + + static pushCupertinoStyle(BuildContext context, Widget page) { + Navigator.of(context).push( + CupertinoPageRoute(builder: (BuildContext context) { + return page; + }), + ); + } } diff --git a/lib/widgets/drawer_body.dart b/lib/widgets/drawer_body.dart index 170fcfd2..d543e154 100644 --- a/lib/widgets/drawer_body.dart +++ b/lib/widgets/drawer_body.dart @@ -64,8 +64,10 @@ class DrawerBodyState extends State { ? 200 : widget.userInfo.status) == 200) - Navigator.of(context) - .push(UserInfoPageRoute(widget.userInfo)); + Utils.pushCupertinoStyle( + context, + UserInfoPage(userInfo: widget.userInfo), + ); else Utils.showToast(context, widget.userInfo.message); }, @@ -150,17 +152,17 @@ class DrawerBodyState extends State { _subItem( icon: AppIcon.classIcon, title: app.course, - route: CoursePageRoute(), + page: CoursePage(), ), _subItem( icon: AppIcon.assignment, title: app.score, - route: ScorePageRoute(), + page: ScorePage(), ), _subItem( icon: AppIcon.apps, title: app.calculateUnits, - route: CalculateUnitsPageRoute(), + page: CalculateUnitsPage(), ), ], ), @@ -181,12 +183,12 @@ class DrawerBodyState extends State { _subItem( icon: AppIcon.edit, title: app.leaveApply, - route: LeavePageRoute(initIndex: 0), + page: LeavePage(initIndex: 0), ), _subItem( icon: AppIcon.assignment, title: app.leaveRecords, - route: LeavePageRoute(initIndex: 1), + page: LeavePage(initIndex: 1), ), ], ), @@ -207,29 +209,29 @@ class DrawerBodyState extends State { _subItem( icon: AppIcon.dateRange, title: app.busReserve, - route: BusPageRoute(initIndex: 0), + page: BusPage(initIndex: 0), ), _subItem( icon: AppIcon.assignment, title: app.busReservations, - route: BusPageRoute(initIndex: 1), + page: BusPage(initIndex: 1), ), ], ), _item( icon: AppIcon.info, title: app.schoolInfo, - route: SchoolInfoPageRoute(), + page: SchoolInfoPage(), ), _item( icon: AppIcon.face, title: app.about, - route: AboutUsPageRoute(), + page: AboutUsPage(), ), _item( icon: AppIcon.settings, title: app.settings, - route: SettingPageRoute(), + page: SettingPage(), ), ListTile( leading: Icon( @@ -251,21 +253,21 @@ class DrawerBodyState extends State { _item({ @required IconData icon, @required String title, - @required MaterialPageRoute route, + @required Widget page, }) => ListTile( leading: Icon(icon, color: Resource.Colors.grey), title: Text(title, style: _defaultStyle), onTap: () { Navigator.pop(context); - Navigator.push(context, route); + Utils.pushCupertinoStyle(context, page); }, ); _subItem({ @required IconData icon, @required String title, - @required MaterialPageRoute route, + @required Widget page, }) => ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 72.0), @@ -273,13 +275,13 @@ class DrawerBodyState extends State { title: Text(title, style: _defaultStyle), onTap: () async { if (Platform.isAndroid || Platform.isIOS) { - if (route is BusPageRoute) { + if (page is BusPage) { bool bus = Preferences.getBool(Constants.PREF_BUS_ENABLE, true); if (!bus) { Utils.showToast(context, app.canNotUseFeature); return; } - } else if (route is LeavePageRoute) { + } else if (page is LeavePage) { bool leave = Preferences.getBool(Constants.PREF_BUS_ENABLE, true); if (!leave) { Utils.showToast(context, app.canNotUseFeature); @@ -288,7 +290,7 @@ class DrawerBodyState extends State { } } Navigator.of(context).pop(); - Navigator.of(context).push(route); + Utils.pushCupertinoStyle(context, page); }, ); diff --git a/lib/widgets/semester_picker.dart b/lib/widgets/semester_picker.dart index 699ab880..b4a0ee2c 100644 --- a/lib/widgets/semester_picker.dart +++ b/lib/widgets/semester_picker.dart @@ -65,9 +65,11 @@ class SemesterPickerState extends State { this.semesterData = await CacheUtils.loadSemesterData(); if (this.semesterData == null) return; widget.onSelect(semesterData.defaultSemester, semesterData.defaultIndex); - setState(() { - selectSemester = semesterData.defaultSemester; - }); + if (mounted) { + setState(() { + selectSemester = semesterData.defaultSemester; + }); + } } void _getSemester() async { diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..7c8c39de --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,6 @@ +xcuserdata + +# Generated by flutter tooling as needed. +Flutter/ephemeral/ +# Created by CocoaPods for plugins. +Pods/ diff --git a/macos/AppDelegate.swift b/macos/AppDelegate.swift new file mode 100644 index 00000000..bdcc151b --- /dev/null +++ b/macos/AppDelegate.swift @@ -0,0 +1,25 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Cocoa + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + @IBOutlet weak var window: NSWindow! + + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} + diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9d0ecc78 --- /dev/null +++ b/macos/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,199 @@ +{ + "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" : "57x57", + "idiom" : "iphone", + "filename" : "Icon-App-57x57@1x.png", + "scale" : "1x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon-App-57x57@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "ItunesArtwork@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-72x72@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-76x76@3x.png", + "scale" : "3x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-20x20@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-40x40@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-60x60@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-72x72@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-76x76@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-Small-50x50@1x.png", + "unassigned" : true, + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-76x76@2x.png", + "unassigned" : true, + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Icon-App-83.5x83.5@2x.png", + "unassigned" : true, + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Icon-Small-50x50@2x.png", + "unassigned" : true, + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..e9a1b63e Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..397afa26 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..5b1baf51 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..bfe03fc9 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..9f5e8bf2 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..8ecfbc8f Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..2b1859f0 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..5b1baf51 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..f95396df Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..46e2ab4c Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 00000000..3d588678 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 00000000..f5513778 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png new file mode 100644 index 00000000..bfe03fc9 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..46e2ab4c Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..cd1842e0 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 00000000..635da1ac Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 00000000..1dddbe1f Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..59f42cc2 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..58b2c263 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png new file mode 100644 index 00000000..fc6b1212 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..cba19f6d Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png new file mode 100644 index 00000000..019d52d6 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png new file mode 100644 index 00000000..0d911133 Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png differ diff --git a/macos/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/macos/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 00000000..e9a1b63e Binary files /dev/null and b/macos/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/macos/Base.lproj/MainMenu.xib b/macos/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..8a14279a --- /dev/null +++ b/macos/Base.lproj/MainMenu.xib @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Configs/Debug.xcconfig b/macos/Configs/Debug.xcconfig new file mode 100644 index 00000000..41b74956 --- /dev/null +++ b/macos/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Configs/Release.xcconfig b/macos/Configs/Release.xcconfig new file mode 100644 index 00000000..2a7dc01d --- /dev/null +++ b/macos/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Configs/Warnings.xcconfig b/macos/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/macos/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/ExampleWindow.swift b/macos/ExampleWindow.swift new file mode 100644 index 00000000..633dfc1d --- /dev/null +++ b/macos/ExampleWindow.swift @@ -0,0 +1,30 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Cocoa +import FlutterMacOS + +class ExampleWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FLEViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} + diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..785633d3 --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5fba960c --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Generated.xcconfig b/macos/Flutter/Generated.xcconfig new file mode 100644 index 00000000..b59a4a68 --- /dev/null +++ b/macos/Flutter/Generated.xcconfig @@ -0,0 +1,6 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/rainvisitor/development/flutter +FLUTTER_APPLICATION_PATH=/Users/rainvisitor/development/flutter-desktop-embedding/example +FLUTTER_BUILD_DIR=build +SYMROOT=/Users/rainvisitor/development/flutter-desktop-embedding/example/build/macos/Build/Products +FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64 diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..58425bba --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// +import Foundation +import FlutterMacOS + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/macos/Flutter/flutter_export_environment.sh b/macos/Flutter/flutter_export_environment.sh new file mode 100755 index 00000000..9e21f37e --- /dev/null +++ b/macos/Flutter/flutter_export_environment.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/rainvisitor/development/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter" +export "FLUTTER_TARGET=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/lib/main.dart" +export "FLUTTER_BUILD_DIR=/Users/rainvisitor/Documents/GitHub/NKUST-AP-Flutter/.dart_tool/flutter_build/25c1fbd5ee33c3b4cf3abf2baeaddc44" +export "FLUTTER_FRAMEWORK_DIR=/Users/rainvisitor/development/flutter/bin/cache/artifacts/engine/darwin-x64" +export "FLUTTER_BUILD_NAME=3.2.0" +export "FLUTTER_BUILD_NUMBER=30200" diff --git a/macos/Info.plist b/macos/Info.plist new file mode 100644 index 00000000..20ee72c5 --- /dev/null +++ b/macos/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.1 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2018 Google LLC. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..45f32620 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,84 @@ +platform :osx, '10.13' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + # TODO: Add Profile support to projects. + #'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +def pubspec_supports_macos(file) + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return false; + end + File.foreach(file_abs_path) { |line| + # TODO(stuartmorgan): Use formal platform declaration once it exists, + # see https://github.com/flutter/flutter/issues/33597. + return true if line =~ /^\s*macosPrefix:/ + } + return false +end + +target 'Runner' do + use_frameworks! + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + ephemeral_dir = File.join('Flutter', 'ephemeral') + symlink_dir = File.join(ephemeral_dir, '.symlinks') + symlink_plugins_dir = File.join(symlink_dir, 'plugins') + system("rm -rf #{symlink_dir}") + system("mkdir -p #{symlink_plugins_dir}") + + # Flutter Pods + generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) + if generated_xcconfig.empty? + puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcconfig.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join(symlink_dir, 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join(symlink_plugins_dir, p[:name]) + File.symlink(p[:path], symlink) + if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) + pod p[:name], :path => File.join(symlink, 'macos') + end + } +end + +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ccf1c18f --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,544 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Build Flutter Bundle" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Build Flutter Bundle"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 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 /* ExampleWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* ExampleWindow.swift */; }; + 33CC112F204626C80003C045 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC112C20461AD40003C045 /* flutter_assets */; }; + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3B2398DFB6ED470D0AE9ACC5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E976979D3EC1F83E826671AA /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 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 = ( + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Warnings.xcconfig; path = Configs/Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* NKUST AP.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "NKUST AP.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* ExampleWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWindow.swift; sourceTree = ""; }; + 33CC112C20461AD40003C045 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../build/flutter_assets; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; + 3791C254B5D15F61597672EC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configs/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configs/Debug.xcconfig; sourceTree = ""; }; + E976979D3EC1F83E826671AA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F6D1C7225F9B1694A8A75E31 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, + 3B2398DFB6ED470D0AE9ACC5 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 121BD0F826E3339928FD5815 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E976979D3EC1F83E826671AA /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + name = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* ExampleWindow.swift */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + 3C590732CA5578AFB3561924 /* Pods */, + 121BD0F826E3339928FD5815 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* NKUST AP.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + 33CC112C20461AD40003C045 /* flutter_assets */, + ); + name = Resources; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, + ); + path = Flutter; + sourceTree = ""; + }; + 3C590732CA5578AFB3561924 /* Pods */ = { + isa = PBXGroup; + children = ( + 3791C254B5D15F61597672EC /* Pods-Runner.debug.xcconfig */, + F6D1C7225F9B1694A8A75E31 /* Pods-Runner.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + F8478360E5B25ECFF8849625 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 45584FC44CA4E8AE5653DDB4 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = "NKUST AP"; + productReference = 33CC10ED2044A3C60003C045 /* NKUST AP.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "Google LLC"; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + 33CC112F204626C80003C045 /* flutter_assets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_build_flutter_assets.sh\n"; + }; + 45584FC44CA4E8AE5653DDB4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F8478360E5B25ECFF8849625 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* ExampleWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Build Flutter Bundle */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 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.13; + 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.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3791C254B5D15F61597672EC /* Pods-Runner.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = R7BC26UNC9; + FRAMEWORK_SEARCH_PATHS = ( + $PROJECT_DIR/Flutter/ephemeral, + "$(inherited)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nkust.ap.flutter; + PRODUCT_NAME = "NKUST AP"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F6D1C7225F9B1694A8A75E31 /* Pods-Runner.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = R7BC26UNC9; + FRAMEWORK_SEARCH_PATHS = ( + $PROJECT_DIR/Flutter/ephemeral, + "$(inherited)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nkust.ap.flutter; + PRODUCT_NAME = "NKUST AP"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + 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 */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Build Flutter Bundle" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..764c74b8 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..f75281fc --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/pubspec.yaml b/pubspec.yaml index d8e5599c..94ba362c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: nkust_ap description: A new Flutter application. -version: 3.2.0+30200 +version: 3.2.1+30201 environment: sdk: ">=2.2.2 <3.0.0" @@ -12,20 +12,20 @@ dependencies: flutter_localizations: sdk: flutter flutter_cupertino_localizations: ^1.0.1 - cupertino_icons: ^0.1.2 #official plugin - shared_preferences: ^0.5.3+1 - package_info: ^0.4.0+4 - url_launcher: ^5.0.3 - share: ^0.6.1+1 - connectivity: ^0.4.2 - firebase_core: ^0.4.0+3 - firebase_analytics: ^3.0.2 - firebase_remote_config: ^0.2.0+2 - firebase_messaging: ^5.0.2 + shared_preferences: ^0.5.3+4 + package_info: ^0.4.0+6 + url_launcher: ^5.1.2 + share: ^0.6.2+1 + connectivity: ^0.4.3+7 + firebase_core: ^0.4.0+8 + firebase_analytics: ^5.0.0 + firebase_remote_config: ^0.2.0+5 + firebase_messaging: ^5.1.3 http: ^0.12.0+2 + html: ^0.14.0+2 #third party plugin - dio: 2.1.0 + dio: 2.1.13 toast: ^0.1.4 tuple: ^1.0.1 flutter_crashlytics: diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..8056593c --- /dev/null +++ b/web/index.html @@ -0,0 +1,10 @@ + + + + + NKUST AP + + + + +