diff --git a/feather/macos/AppDelegate.swift b/feather/macos/AppDelegate.swift
new file mode 100644
index 00000000..a1f9c9bd
--- /dev/null
+++ b/feather/macos/AppDelegate.swift
@@ -0,0 +1,43 @@
+// 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
+ }
+
+
+ func applicationWillFinishLaunching(_ notification: Notification) {
+ NSLog("applicationWillFinishLaunching");
+ NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(AppDelegate.handleEvent(_:withReplyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL));
+ }
+
+// @objc - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+// {
+// NSString *param = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+// }
+ @objc func handleEvent(_ event: NSAppleEventDescriptor!, withReplyEvent: NSAppleEventDescriptor!) {
+ let param:String? = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue;
+ NSLog("Param: %@", param!);
+ let win:MainWindow = self.window as! MainWindow;
+ win.flutterViewController.receivedUriLaunch(param!);
+ }
+
+}
+
diff --git a/feather/macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/feather/macos/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..8a213cb7
--- /dev/null
+++ b/feather/macos/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,60 @@
+{
+ "images" : [
+ {
+ "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"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app-icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app-icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/feather/macos/Assets.xcassets/AppIcon.appiconset/app-icon_512.png b/feather/macos/Assets.xcassets/AppIcon.appiconset/app-icon_512.png
new file mode 100644
index 00000000..1f1353c7
Binary files /dev/null and b/feather/macos/Assets.xcassets/AppIcon.appiconset/app-icon_512.png differ
diff --git a/feather/macos/Base.lproj/MainMenu.xib b/feather/macos/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..28350547
--- /dev/null
+++ b/feather/macos/Base.lproj/MainMenu.xib
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/FeatherApp-Bridging-Header.h b/feather/macos/FeatherApp-Bridging-Header.h
new file mode 100644
index 00000000..f8f76606
--- /dev/null
+++ b/feather/macos/FeatherApp-Bridging-Header.h
@@ -0,0 +1,18 @@
+// 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
+#import
+#import
+#import
diff --git a/feather/macos/FeatherApp.xcodeproj/project.pbxproj b/feather/macos/FeatherApp.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..c34093fd
--- /dev/null
+++ b/feather/macos/FeatherApp.xcodeproj/project.pbxproj
@@ -0,0 +1,489 @@
+// !$*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 */
+ 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 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainWindow.swift */; };
+ 33CC112F204626C80003C045 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC112C20461AD40003C045 /* flutter_assets */; };
+ 4D277AAF21AE332C00D39356 /* FlutterEmbedderMenubar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D277AAB21AE332C00D39356 /* FlutterEmbedderMenubar.framework */; };
+ 4D277AB121AE332C00D39356 /* FlutterEmbedderColorPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D277AAD21AE332C00D39356 /* FlutterEmbedderColorPanel.framework */; };
+ 4D277AB221AE332C00D39356 /* FlutterEmbedderFileChooser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D277AAE21AE332C00D39356 /* FlutterEmbedderFileChooser.framework */; };
+ 4D277AB321AE333A00D39356 /* FlutterEmbedderColorPanel.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 4D277AAD21AE332C00D39356 /* FlutterEmbedderColorPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 4D277AB421AE333A00D39356 /* FlutterEmbedderFileChooser.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 4D277AAE21AE332C00D39356 /* FlutterEmbedderFileChooser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 4D277AB621AE333A00D39356 /* FlutterEmbedderMenubar.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 4D277AAB21AE332C00D39356 /* FlutterEmbedderMenubar.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 4DCE8BBE2162667F003A471F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4DCE8BBC2162667F003A471F /* InfoPlist.strings */; };
+ 4DD3B527213CB7EE009C4DBB /* SignInView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4DD3B526213CB7EE009C4DBB /* SignInView.xib */; };
+ 4DD3B529213CB8CC009C4DBB /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD3B528213CB8CC009C4DBB /* SignInView.swift */; };
+ 4DFA41C421AF86FE00052B11 /* FlutterEmbedderMacFeather.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DFA41C321AF86FE00052B11 /* FlutterEmbedderMacFeather.framework */; };
+ 4DFA41C621AF883700052B11 /* FlutterEmbedder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DFA41C521AF883700052B11 /* FlutterEmbedder.framework */; };
+ 4DFA41C721AF88D000052B11 /* FlutterEmbedder.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 4DFA41C521AF883700052B11 /* FlutterEmbedder.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 4DFA41C821AF88D000052B11 /* FlutterEmbedderMacFeather.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 4DFA41C321AF86FE00052B11 /* FlutterEmbedderMacFeather.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+/* 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 = (
+ 4DFA41C721AF88D000052B11 /* FlutterEmbedder.framework in Bundle Framework */,
+ 4DFA41C821AF88D000052B11 /* FlutterEmbedderMacFeather.framework in Bundle Framework */,
+ 4D277AB321AE333A00D39356 /* FlutterEmbedderColorPanel.framework in Bundle Framework */,
+ 4D277AB421AE333A00D39356 /* FlutterEmbedderFileChooser.framework in Bundle Framework */,
+ 4D277AB621AE333A00D39356 /* FlutterEmbedderMenubar.framework in Bundle Framework */,
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 33CC10ED2044A3C60003C045 /* FeatherApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = FeatherApp.app; path = "app_9vzIF7brk1rN.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 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = ""; };
+ 33CC11162044C3600003C045 /* FeatherApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FeatherApp-Bridging-Header.h"; sourceTree = ""; };
+ 33CC112C20461AD40003C045 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../../build/flutter_assets; sourceTree = ""; };
+ 4D277AAB21AE332C00D39356 /* FlutterEmbedderMenubar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FlutterEmbedderMenubar.framework; sourceTree = ""; };
+ 4D277AAD21AE332C00D39356 /* FlutterEmbedderColorPanel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FlutterEmbedderColorPanel.framework; sourceTree = ""; };
+ 4D277AAE21AE332C00D39356 /* FlutterEmbedderFileChooser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FlutterEmbedderFileChooser.framework; sourceTree = ""; };
+ 4DCE8BBB216265FE003A471F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; };
+ 4DCE8BBD2162667F003A471F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
+ 4DD3B526213CB7EE009C4DBB /* SignInView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SignInView.xib; sourceTree = ""; };
+ 4DD3B528213CB8CC009C4DBB /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = ""; };
+ 4DFA41C321AF86FE00052B11 /* FlutterEmbedderMacFeather.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FlutterEmbedderMacFeather.framework; sourceTree = ""; };
+ 4DFA41C521AF883700052B11 /* FlutterEmbedder.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FlutterEmbedder.framework; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4D277AAF21AE332C00D39356 /* FlutterEmbedderMenubar.framework in Frameworks */,
+ 4DFA41C421AF86FE00052B11 /* FlutterEmbedderMacFeather.framework in Frameworks */,
+ 4DFA41C621AF883700052B11 /* FlutterEmbedder.framework in Frameworks */,
+ 4D277AB221AE332C00D39356 /* FlutterEmbedderFileChooser.framework in Frameworks */,
+ 4D277AB121AE332C00D39356 /* FlutterEmbedderColorPanel.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 4DD3B526213CB7EE009C4DBB /* SignInView.xib */,
+ 4DD3B528213CB8CC009C4DBB /* SignInView.swift */,
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainWindow.swift */,
+ 33CC11162044C3600003C045 /* FeatherApp-Bridging-Header.h */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ 4DFA41C521AF883700052B11 /* FlutterEmbedder.framework */,
+ 4DFA41C321AF86FE00052B11 /* FlutterEmbedderMacFeather.framework */,
+ 4D277AAD21AE332C00D39356 /* FlutterEmbedderColorPanel.framework */,
+ 4D277AAE21AE332C00D39356 /* FlutterEmbedderFileChooser.framework */,
+ 4D277AAB21AE332C00D39356 /* FlutterEmbedderMenubar.framework */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* FeatherApp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 4DCE8BBC2162667F003A471F /* InfoPlist.strings */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ 33CC112C20461AD40003C045 /* flutter_assets */,
+ );
+ name = Resources;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* FeatherApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "FeatherApp" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = FeatherApp;
+ productName = FeatherApp;
+ productReference = 33CC10ED2044A3C60003C045 /* FeatherApp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 0920;
+ ProvisioningStyle = Manual;
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "FeatherApp" */;
+ compatibilityVersion = "Xcode 8.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ Base,
+ en,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* FeatherApp */,
+ 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 */,
+ 4DD3B527213CB7EE009C4DBB /* SignInView.xib in Resources */,
+ 33CC112F204626C80003C045 /* flutter_assets in Resources */,
+ 4DCE8BBE2162667F003A471F /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "cd \"$PROJECT_DIR\"/../..\n/Users/rainvisitor/flutter/bin/flutter build bundle";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4DD3B529213CB8CC009C4DBB /* SignInView.swift in Sources */,
+ 33CC11132044BFA00003C045 /* MainWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.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 */,
+ 4DCE8BBB216265FE003A471F /* en */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "";
+ };
+ 4DCE8BBC2162667F003A471F /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 4DCE8BBD2162667F003A471F /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ 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_COMMA = 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_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = 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_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ 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;
+ 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_COMMA = 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_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = 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_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "";
+ CODE_SIGN_STYLE = Manual;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.10.5;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.feather-apps.app-9vzIF7brk1rN";
+ PRODUCT_NAME = "app_9vzIF7brk1rN";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OBJC_BRIDGING_HEADER = "FeatherApp-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 4.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_IDENTITY = "";
+ CODE_SIGN_STYLE = Manual;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.10.5;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.feather-apps.app-9vzIF7brk1rN";
+ PRODUCT_NAME = "app_9vzIF7brk1rN";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OBJC_BRIDGING_HEADER = "FeatherApp-Bridging-Header.h";
+ 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 "FeatherApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "FeatherApp" */ = {
+ 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/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata.gz b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata.gz
new file mode 100644
index 00000000..2c8dfd23
Binary files /dev/null and b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata.gz differ
diff --git a/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 00000000..cb171f27
Binary files /dev/null and b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate.gz b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate.gz
new file mode 100644
index 00000000..3a3ee82a
Binary files /dev/null and b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate.gz differ
diff --git a/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/rainvisitor.xcuserdatad/UserInterfaceState.xcuserstate b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/rainvisitor.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 00000000..ecdd03c6
Binary files /dev/null and b/feather/macos/FeatherApp.xcodeproj/project.xcworkspace/xcuserdata/rainvisitor.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/feather/macos/FeatherApp.xcodeproj/xcuserdata/rainvisitor.xcuserdatad/xcschemes/xcschememanagement.plist b/feather/macos/FeatherApp.xcodeproj/xcuserdata/rainvisitor.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 00000000..dbe51319
--- /dev/null
+++ b/feather/macos/FeatherApp.xcodeproj/xcuserdata/rainvisitor.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ SchemeUserState
+
+ Build Flutter Bundle.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ FeatherApp.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+
+
diff --git a/feather/macos/FlutterEmbedder.framework/FlutterEmbedder b/feather/macos/FlutterEmbedder.framework/FlutterEmbedder
new file mode 120000
index 00000000..349bcbf6
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/FlutterEmbedder
@@ -0,0 +1 @@
+Versions/Current/FlutterEmbedder
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedder.framework/Headers b/feather/macos/FlutterEmbedder.framework/Headers
new file mode 120000
index 00000000..a177d2a6
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedder.framework/Modules b/feather/macos/FlutterEmbedder.framework/Modules
new file mode 120000
index 00000000..5736f318
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedder.framework/Resources b/feather/macos/FlutterEmbedder.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/A/FlutterEmbedder b/feather/macos/FlutterEmbedder.framework/Versions/A/FlutterEmbedder
new file mode 100755
index 00000000..20701e1e
Binary files /dev/null and b/feather/macos/FlutterEmbedder.framework/Versions/A/FlutterEmbedder differ
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/A/Headers/FlutterEmbedder.h b/feather/macos/FlutterEmbedder.framework/Versions/A/Headers/FlutterEmbedder.h
new file mode 100644
index 00000000..d1a997ae
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Versions/A/Headers/FlutterEmbedder.h
@@ -0,0 +1,335 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_EMBEDDER_H_
+#define FLUTTER_EMBEDDER_H_
+
+#include
+#include
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef FLUTTER_EXPORT
+#define FLUTTER_EXPORT
+#endif // FLUTTER_EXPORT
+
+#define FLUTTER_ENGINE_VERSION 1
+
+typedef enum {
+ kSuccess = 0,
+ kInvalidLibraryVersion,
+ kInvalidArguments,
+ kInternalInconsistency,
+} FlutterEngineResult;
+
+typedef enum {
+ kOpenGL,
+ kSoftware,
+} FlutterRendererType;
+
+typedef struct _FlutterEngine* FlutterEngine;
+
+typedef struct {
+ // horizontal scale factor
+ double scaleX;
+ // horizontal skew factor
+ double skewX;
+ // horizontal translation
+ double transX;
+ // vertical skew factor
+ double skewY;
+ // vertical scale factor
+ double scaleY;
+ // vertical translation
+ double transY;
+ // input x-axis perspective factor
+ double pers0;
+ // input y-axis perspective factor
+ double pers1;
+ // perspective scale factor
+ double pers2;
+} FlutterTransformation;
+
+typedef void (*VoidCallback)(void* /* user data */);
+
+typedef struct {
+ // Target texture of the active texture unit (example GL_TEXTURE_2D).
+ uint32_t target;
+ // The name of the texture.
+ uint32_t name;
+ // The texture format (example GL_RGBA8).
+ uint32_t format;
+ // User data to be returned on the invocation of the destruction callback.
+ void* user_data;
+ // Callback invoked (on an engine managed thread) that asks the embedder to
+ // collect the texture.
+ VoidCallback destruction_callback;
+} FlutterOpenGLTexture;
+
+typedef bool (*BoolCallback)(void* /* user data */);
+typedef FlutterTransformation (*TransformationCallback)(void* /* user data */);
+typedef uint32_t (*UIntCallback)(void* /* user data */);
+typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */,
+ const void* /* allocation */,
+ size_t /* row bytes */,
+ size_t /* height */);
+typedef void* (*ProcResolver)(void* /* user data */, const char* /* name */);
+typedef bool (*TextureFrameCallback)(void* /* user data */,
+ int64_t /* texture identifier */,
+ size_t /* width */,
+ size_t /* height */,
+ FlutterOpenGLTexture* /* texture out */);
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
+ size_t struct_size;
+ BoolCallback make_current;
+ BoolCallback clear_current;
+ BoolCallback present;
+ UIntCallback fbo_callback;
+ // This is an optional callback. Flutter will ask the emebdder to create a GL
+ // context current on a background thread. If the embedder is able to do so,
+ // Flutter will assume that this context is in the same sharegroup as the main
+ // rendering context and use this context for asynchronous texture uploads.
+ // Though optional, it is recommended that all embedders set this callback as
+ // it will lead to better performance in texture handling.
+ BoolCallback make_resource_current;
+ // By default, the renderer config assumes that the FBO does not change for
+ // the duration of the engine run. If this argument is true, the
+ // engine will ask the embedder for an updated FBO target (via an fbo_callback
+ // invocation) after a present call.
+ bool fbo_reset_after_present;
+ // The transformation to apply to the render target before any rendering
+ // operations. This callback is optional.
+ TransformationCallback surface_transformation;
+ ProcResolver gl_proc_resolver;
+ // When the embedder specifies that a texture has a frame available, the
+ // engine will call this method (on an internal engine managed thread) so that
+ // external texture details can be supplied to the engine for subsequent
+ // composition.
+ TextureFrameCallback gl_external_texture_frame_callback;
+} FlutterOpenGLRendererConfig;
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterSoftwareRendererConfig).
+ size_t struct_size;
+ // The callback presented to the embedder to present a fully populated buffer
+ // to the user. The pixel format of the buffer is the native 32-bit RGBA
+ // format. The buffer is owned by the Flutter engine and must be copied in
+ // this callback if needed.
+ SoftwareSurfacePresentCallback surface_present_callback;
+} FlutterSoftwareRendererConfig;
+
+typedef struct {
+ FlutterRendererType type;
+ union {
+ FlutterOpenGLRendererConfig open_gl;
+ FlutterSoftwareRendererConfig software;
+ };
+} FlutterRendererConfig;
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
+ size_t struct_size;
+ // Physical width of the window.
+ size_t width;
+ // Physical height of the window.
+ size_t height;
+ // Scale factor for the physical screen.
+ double pixel_ratio;
+} FlutterWindowMetricsEvent;
+
+typedef enum {
+ kCancel,
+ kUp,
+ kDown,
+ kMove,
+} FlutterPointerPhase;
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterPointerEvent).
+ size_t struct_size;
+ FlutterPointerPhase phase;
+ size_t timestamp; // in microseconds.
+ double x;
+ double y;
+ // An optional device identifier. If this is not specified, it is assumed that
+ // the embedder has no multitouch capability.
+ int32_t device;
+} FlutterPointerEvent;
+
+struct _FlutterPlatformMessageResponseHandle;
+typedef struct _FlutterPlatformMessageResponseHandle
+ FlutterPlatformMessageResponseHandle;
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterPlatformMessage).
+ size_t struct_size;
+ const char* channel;
+ const uint8_t* message;
+ const size_t message_size;
+ // The response handle on which to invoke
+ // |FlutterEngineSendPlatformMessageResponse| when the response is ready. This
+ // field is ignored for messages being sent from the embedder to the
+ // framework. If the embedder ever receives a message with a non-null response
+ // handle, that handle must always be used with a
+ // |FlutterEngineSendPlatformMessageResponse| call. If not, this is a memory
+ // leak. It is not safe to send multiple responses on a single response
+ // object.
+ const FlutterPlatformMessageResponseHandle* response_handle;
+} FlutterPlatformMessage;
+
+typedef void (*FlutterPlatformMessageCallback)(
+ const FlutterPlatformMessage* /* message*/,
+ void* /* user data */);
+
+typedef struct {
+ // The size of this struct. Must be sizeof(FlutterProjectArgs).
+ size_t struct_size;
+ // The path to the Flutter assets directory containing project assets. The
+ // string can be collected after the call to |FlutterEngineRun| returns. The
+ // string must be NULL terminated.
+ const char* assets_path;
+ // The path to the Dart file containing the |main| entry point.
+ // The string can be collected after the call to |FlutterEngineRun| returns.
+ // The string must be NULL terminated.
+ //
+ // \deprecated As of Dart 2, running from Dart source is no longer supported.
+ // Dart code should now be compiled to kernel form and will be loaded by from
+ // |kernel_blob.bin| in the assets directory. This struct member is retained
+ // for ABI stability.
+ const char* main_path__unused__;
+ // The path to the |.packages| for the project. The string can be collected
+ // after the call to |FlutterEngineRun| returns. The string must be NULL
+ // terminated.
+ //
+ // \deprecated As of Dart 2, running from Dart source is no longer supported.
+ // Dart code should now be compiled to kernel form and will be loaded by from
+ // |kernel_blob.bin| in the assets directory. This struct member is retained
+ // for ABI stability.
+ const char* packages_path__unused__;
+ // The path to the icudtl.dat file for the project. The string can be
+ // collected after the call to |FlutterEngineRun| returns. The string must
+ // be NULL terminated.
+ const char* icu_data_path;
+ // The command line argument count used to initialize the project.
+ int command_line_argc;
+ // The command line arguments used to initialize the project. The strings can
+ // be collected after the call to |FlutterEngineRun| returns. The strings must
+ // be NULL terminated.
+ // Note: The first item in the command line (if specificed at all) is
+ // interpreted as the executable name. So if an engine flag needs to be passed
+ // into the same, it needs to not be the very first item in the list. The set
+ // of engine flags are only meant to control unstable features in the engine.
+ // Deployed applications should not pass any command line arguments at all as
+ // they may affect engine stability at runtime in the presence of unsanitized
+ // input. The list of currently recognized engine flags and their descriptions
+ // can be retrieved from the |switches.h| engine source file.
+ const char* const* command_line_argv;
+ // The callback invoked by the engine in order to give the embedder the chance
+ // to respond to platform messages from the Dart application. The callback
+ // will be invoked on the thread on which the |FlutterEngineRun| call is made.
+ FlutterPlatformMessageCallback platform_message_callback;
+ // The VM snapshot data buffer used in AOT operation. This buffer must be
+ // mapped in as read-only. For more information refer to the documentation on
+ // the Wiki at
+ // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode
+ const uint8_t* vm_snapshot_data;
+ // The size of the VM snapshot data buffer.
+ size_t vm_snapshot_data_size;
+ // The VM snapshot instructions buffer used in AOT operation. This buffer must
+ // be mapped in as read-execute. For more information refer to the
+ // documentation on the Wiki at
+ // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode
+ const uint8_t* vm_snapshot_instructions;
+ // The size of the VM snapshot instructions buffer.
+ size_t vm_snapshot_instructions_size;
+ // The isolate snapshot data buffer used in AOT operation. This buffer must be
+ // mapped in as read-only. For more information refer to the documentation on
+ // the Wiki at
+ // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode
+ const uint8_t* isolate_snapshot_data;
+ // The size of the isolate snapshot data buffer.
+ size_t isolate_snapshot_data_size;
+ // The isolate snapshot instructions buffer used in AOT operation. This buffer
+ // must be mapped in as read-execute. For more information refer to the
+ // documentation on the Wiki at
+ // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode
+ const uint8_t* isolate_snapshot_instructions;
+ // The size of the isolate snapshot instructions buffer.
+ size_t isolate_snapshot_instructions_size;
+ // The callback invoked by the engine in root isolate scope. Called
+ // immediately after the root isolate has been created and marked runnable.
+ VoidCallback root_isolate_create_callback;
+} FlutterProjectArgs;
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineRun(size_t version,
+ const FlutterRendererConfig* config,
+ const FlutterProjectArgs* args,
+ void* user_data,
+ FlutterEngine* engine_out);
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineShutdown(FlutterEngine engine);
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
+ FlutterEngine engine,
+ const FlutterWindowMetricsEvent* event);
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineSendPointerEvent(
+ FlutterEngine engine,
+ const FlutterPointerEvent* events,
+ size_t events_count);
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineSendPlatformMessage(
+ FlutterEngine engine,
+ const FlutterPlatformMessage* message);
+
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
+ FlutterEngine engine,
+ const FlutterPlatformMessageResponseHandle* handle,
+ const uint8_t* data,
+ size_t data_length);
+
+// This API is only meant to be used by platforms that need to flush tasks on a
+// message loop not controlled by the Flutter engine. This API will be
+// deprecated soon.
+FLUTTER_EXPORT
+FlutterEngineResult __FlutterEngineFlushPendingTasksNow();
+
+// Register an external texture with a unique (per engine) identifier. Only
+// rendering backends that support external textures accept external texture
+// registrations. After the external texture is registered, the application can
+// mark that a frame is available by calling
+// |FlutterEngineMarkExternalTextureFrameAvailable|.
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineRegisterExternalTexture(
+ FlutterEngine engine,
+ int64_t texture_identifier);
+
+// Unregister a previous texture registration.
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineUnregisterExternalTexture(
+ FlutterEngine engine,
+ int64_t texture_identifier);
+
+// Mark that a new texture frame is available for a given texture identifier.
+FLUTTER_EXPORT
+FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable(
+ FlutterEngine engine,
+ int64_t texture_identifier);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif // FLUTTER_EMBEDDER_H_
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/A/Modules/module.modulemap b/feather/macos/FlutterEmbedder.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 00000000..60328fe1
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module FlutterEmbedder {
+ umbrella header "FlutterEmbedder.h"
+
+ export *
+ module * { export * }
+}
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/Info.plist b/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 00000000..02a4027b
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ FlutterEmbedder
+ CFBundleIdentifier
+ io.flutter.flutter-embedder
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ FlutterEmbedder
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ NSHumanReadableCopyright
+ Copyright 2013 The Flutter Authors. All rights reserved.
+
+
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/icudtl.dat b/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/icudtl.dat
new file mode 100644
index 00000000..d9677abf
Binary files /dev/null and b/feather/macos/FlutterEmbedder.framework/Versions/A/Resources/icudtl.dat differ
diff --git a/feather/macos/FlutterEmbedder.framework/Versions/Current b/feather/macos/FlutterEmbedder.framework/Versions/Current
new file mode 120000
index 00000000..8c7e5a66
--- /dev/null
+++ b/feather/macos/FlutterEmbedder.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/FlutterEmbedderColorPanel b/feather/macos/FlutterEmbedderColorPanel.framework/FlutterEmbedderColorPanel
new file mode 120000
index 00000000..12360a77
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/FlutterEmbedderColorPanel
@@ -0,0 +1 @@
+Versions/Current/FlutterEmbedderColorPanel
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Headers b/feather/macos/FlutterEmbedderColorPanel.framework/Headers
new file mode 120000
index 00000000..a177d2a6
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Modules b/feather/macos/FlutterEmbedderColorPanel.framework/Modules
new file mode 120000
index 00000000..5736f318
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Resources b/feather/macos/FlutterEmbedderColorPanel.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/FlutterEmbedderColorPanel b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/FlutterEmbedderColorPanel
new file mode 100755
index 00000000..851ed1c8
Binary files /dev/null and b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/FlutterEmbedderColorPanel differ
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FLEColorPanelPlugin.h b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FLEColorPanelPlugin.h
new file mode 100644
index 00000000..4fb9fb62
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FLEColorPanelPlugin.h
@@ -0,0 +1,26 @@
+// 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
+
+#import
+
+/**
+ * A FlutterPlugin to manage macOS's shared NSColorPanel singleton. Owned by
+ * the FlutterViewController. Responsible for managing the panel's display state
+ * and sending selected color data to Flutter, via system channels.
+ */
+@interface FLEColorPanelPlugin : NSObject
+
+@end
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FlutterEmbedderColorPanel.h b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FlutterEmbedderColorPanel.h
new file mode 100644
index 00000000..72a5d25c
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Headers/FlutterEmbedderColorPanel.h
@@ -0,0 +1,15 @@
+// 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 "FLEColorPanelPlugin.h"
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Modules/module.modulemap b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 00000000..e052ed19
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module FlutterEmbedderColorPanel {
+ umbrella header "FlutterEmbedderColorPanel.h"
+
+ export *
+ module * { export * }
+}
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Resources/Info.plist b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 00000000..20c61cf5
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ BuildMachineOSBuild
+ 17G65
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ FlutterEmbedderColorPanel
+ CFBundleIdentifier
+ com.google.FlutterEmbedderColorPanel
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ FlutterEmbedderColorPanel
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 9B55
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 17B41
+ DTSDKName
+ macosx10.13
+ DTXcode
+ 0910
+ DTXcodeBuild
+ 9B55
+
+
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/_CodeSignature/CodeResources b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/_CodeSignature/CodeResources
new file mode 100644
index 00000000..b33eb0c3
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/A/_CodeSignature/CodeResources
@@ -0,0 +1,165 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ lDWm2lz41WlzGN5GVYKvN+Dvg+A=
+
+
+ files2
+
+ Headers/FLEColorPanelPlugin.h
+
+ hash
+
+ wuYMqqm06LBgmR/Ryy9/Z76sWyE=
+
+ hash2
+
+ THw4AdeLFpGVSncji7AHKFk3BZs/uWYnG3X/ip4G2io=
+
+
+ Headers/FlutterEmbedderColorPanel.h
+
+ hash
+
+ krDdd7Gn+4ZVxmqeHp0dRPJaq8Q=
+
+ hash2
+
+ TLGm5riwPKF0UZ8yWaUqdPbWfNy7XLI+asTJducj6aI=
+
+
+ Modules/module.modulemap
+
+ hash
+
+ 3DztYnKLgja5CAHFjd3fYwXp6+c=
+
+ hash2
+
+ +MMHNdRhUj3gYt6OzL5zUV10MUCh+DnwJmOq5FIk5RM=
+
+
+ Resources/Info.plist
+
+ hash
+
+ lDWm2lz41WlzGN5GVYKvN+Dvg+A=
+
+ hash2
+
+ nw0NStaK1D1TzCo0Gi5XptdKHK9QNssgzaiopFSdmNI=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/feather/macos/FlutterEmbedderColorPanel.framework/Versions/Current b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/Current
new file mode 120000
index 00000000..8c7e5a66
--- /dev/null
+++ b/feather/macos/FlutterEmbedderColorPanel.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/FlutterEmbedderFileChooser b/feather/macos/FlutterEmbedderFileChooser.framework/FlutterEmbedderFileChooser
new file mode 120000
index 00000000..0a7908f4
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/FlutterEmbedderFileChooser
@@ -0,0 +1 @@
+Versions/Current/FlutterEmbedderFileChooser
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Headers b/feather/macos/FlutterEmbedderFileChooser.framework/Headers
new file mode 120000
index 00000000..a177d2a6
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Modules b/feather/macos/FlutterEmbedderFileChooser.framework/Modules
new file mode 120000
index 00000000..5736f318
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Resources b/feather/macos/FlutterEmbedderFileChooser.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/FlutterEmbedderFileChooser b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/FlutterEmbedderFileChooser
new file mode 100755
index 00000000..6ff04aac
Binary files /dev/null and b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/FlutterEmbedderFileChooser differ
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FLEFileChooserPlugin.h b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FLEFileChooserPlugin.h
new file mode 100644
index 00000000..7473ab37
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FLEFileChooserPlugin.h
@@ -0,0 +1,26 @@
+// 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
+
+#import
+
+/**
+ * A FlutterPlugin to handle file choosing affordances. Owned by the FlutterViewController.
+ * Responsible for creating and showing instances of NSSavePanel or NSOpenPanel and sending
+ * selected file paths to flutter clients, via system channels.
+ */
+@interface FLEFileChooserPlugin : NSObject
+
+@end
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FlutterEmbedderFileChooser.h b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FlutterEmbedderFileChooser.h
new file mode 100644
index 00000000..07b8f2be
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Headers/FlutterEmbedderFileChooser.h
@@ -0,0 +1,15 @@
+// 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 "FLEFileChooserPlugin.h"
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Modules/module.modulemap b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 00000000..16c92b80
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module FlutterEmbedderFileChooser {
+ umbrella header "FlutterEmbedderFileChooser.h"
+
+ export *
+ module * { export * }
+}
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Resources/Info.plist b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 00000000..395c4ada
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ BuildMachineOSBuild
+ 17G65
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ FlutterEmbedderFileChooser
+ CFBundleIdentifier
+ com.google.FlutterEmbedderFileChooser
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ FlutterEmbedderFileChooser
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 9B55
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 17B41
+ DTSDKName
+ macosx10.13
+ DTXcode
+ 0910
+ DTXcodeBuild
+ 9B55
+
+
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/_CodeSignature/CodeResources b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/_CodeSignature/CodeResources
new file mode 100644
index 00000000..3a83a983
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/A/_CodeSignature/CodeResources
@@ -0,0 +1,165 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ DFRRJ1XNFr8hsTcRjUFgl4ry8Yk=
+
+
+ files2
+
+ Headers/FLEFileChooserPlugin.h
+
+ hash
+
+ o7chZmnJ+qfIragP+xmEb3UC50A=
+
+ hash2
+
+ 6wAZtnWPC7f3ZN8Om2Ml3Ysu8xIeQK0ULkIh9tAoDq0=
+
+
+ Headers/FlutterEmbedderFileChooser.h
+
+ hash
+
+ J8d0BRuKDgrMz+Ednay0vgLNtZ8=
+
+ hash2
+
+ /nsFBdhdAgue64ue4CD05SUarkLJ3l8uVhwMxF/UGUI=
+
+
+ Modules/module.modulemap
+
+ hash
+
+ Q0zUkqeIk9RB1nYV2gh5TOxAh1k=
+
+ hash2
+
+ Wghb5KTXPF9nOL2atqe9NFOJ7GxUWOCQCSlppILYt/0=
+
+
+ Resources/Info.plist
+
+ hash
+
+ DFRRJ1XNFr8hsTcRjUFgl4ry8Yk=
+
+ hash2
+
+ j6623NJ1u6UqS6oXnFxCnW6Ohmf+WPUiQaJXacNhx1Q=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/feather/macos/FlutterEmbedderFileChooser.framework/Versions/Current b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/Current
new file mode 120000
index 00000000..8c7e5a66
--- /dev/null
+++ b/feather/macos/FlutterEmbedderFileChooser.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/FlutterEmbedderMacFeather b/feather/macos/FlutterEmbedderMacFeather.framework/FlutterEmbedderMacFeather
new file mode 120000
index 00000000..a8a2ae9b
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/FlutterEmbedderMacFeather
@@ -0,0 +1 @@
+Versions/Current/FlutterEmbedderMacFeather
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Headers b/feather/macos/FlutterEmbedderMacFeather.framework/Headers
new file mode 120000
index 00000000..a177d2a6
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Modules b/feather/macos/FlutterEmbedderMacFeather.framework/Modules
new file mode 120000
index 00000000..5736f318
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Resources b/feather/macos/FlutterEmbedderMacFeather.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/FlutterEmbedderMacFeather b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/FlutterEmbedderMacFeather
new file mode 100755
index 00000000..d2e8c6cc
Binary files /dev/null and b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/FlutterEmbedderMacFeather differ
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEChannels.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEChannels.h
new file mode 100644
index 00000000..3038bc68
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEChannels.h
@@ -0,0 +1,90 @@
+// 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
+
+/**
+ * An object encapsulating a method call from Flutter.
+ */
+@interface FLEMethodCall : NSObject
+
+/**
+ * Initializes an FLEMethodCall. If |arguments| is provided, it must be serializable to JSON.
+ */
+- (nonnull instancetype)initWithMethodName:(nonnull NSString *)name
+ arguments:(nullable id)arguments NS_DESIGNATED_INITIALIZER;
+- (nonnull instancetype)init NS_UNAVAILABLE;
+
+/**
+ * The name of the method being called.
+ */
+@property(readonly, nonatomic, nonnull) NSString *methodName;
+
+/**
+ * The arguments to the method being called, if any.
+ *
+ * This object must be serializable to JSON. See
+ * https://docs.flutter.io/flutter/services/JSONMethodCodec-class.html
+ * for supported types.
+ */
+@property(readonly, nonatomic, nullable) id arguments;
+
+@end
+
+#pragma mark -
+
+/**
+ * A method call result callback. Used for sending a method call's response back to the
+ * Flutter engine. The result must be serializable to JSON.
+ */
+typedef void (^FLEMethodResult)(id _Nullable result);
+
+/**
+ * A constant that can be passed to an FLEMethodResult to indicate that the message is not handled
+ * (e.g., the method name is unknown).
+ */
+extern NSString const *_Nonnull FLEMethodNotImplemented;
+
+/**
+ * An error object that can be passed to an FLEMethodResult to send an error response to the caller
+ * on the Flutter side.
+ */
+@interface FLEMethodError : NSObject
+
+/**
+ * Initializes an FLEMethodError. If |details| is provided, it must be serializable to JSON.
+ */
+- (nonnull instancetype)initWithCode:(nonnull NSString *)code
+ message:(nullable NSString *)message
+ details:(nullable id)details NS_DESIGNATED_INITIALIZER;
+- (nonnull instancetype)init NS_UNAVAILABLE;
+
+/**
+ * The error code, as a string.
+ */
+@property(readonly, nonatomic, nonnull) NSString *code;
+
+/**
+ * A human-readable description of the error.
+ */
+@property(readonly, nonatomic, nullable) NSString *message;
+
+/**
+ * Any additional details or context about the error.
+ *
+ * This object must be serializable to JSON.
+ */
+@property(readonly, nonatomic, nullable) id details;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLECodecs.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLECodecs.h
new file mode 100644
index 00000000..d4fa7298
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLECodecs.h
@@ -0,0 +1,181 @@
+// 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
+
+#import "FLEChannels.h"
+
+/**
+ * Translates between a binary message and higher-level method call and response/error objects.
+ */
+@protocol FLEMethodCodec
+
+/**
+ * Returns the shared instance of the codec.
+ */
++ (nonnull instancetype)sharedInstance;
+
+/**
+ * Returns a binary encoding of the given |methodCall|, or nil if the method call cannot be
+ * serialized by this codec.
+ */
+- (nullable NSData*)encodeMethodCall:(nonnull FLEMethodCall*)methodCall;
+
+/**
+ * Returns the FLEMethodCall encoded in |methodData|, or nil if it cannot be decoded.
+ */
+- (nullable FLEMethodCall*)decodeMethodCall:(nonnull NSData*)methodData;
+
+/**
+ * Returns a binary encoding of |result|. Returns nil if |result| is not a type supported by the
+ * codec.
+ */
+- (nullable NSData*)encodeSuccessEnvelope:(nullable id)result;
+
+/**
+ * Returns a binary encoding of |error|. Returns nil if the |details| parameter of |error| is not
+ * a type supported by the codec.
+ */
+- (nullable NSData*)encodeErrorEnvelope:(nonnull FLEMethodError*)error;
+
+@end
+
+/**
+ * A codec that uses JSON as the encoding format. Method arguments and error details for plugins
+ * using this codec must be serializable to JSON.
+ */
+@interface FLEJSONMethodCodec : NSObject
+@end
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, FLEStandardDataType) {
+ FLEStandardDataTypeUInt8,
+ FLEStandardDataTypeInt32,
+ FLEStandardDataTypeInt64,
+ FLEStandardDataTypeFloat64,
+};
+
+@interface FLEStandardWriter : NSObject
+- (instancetype )initWithData:(NSMutableData*)data;
+- (void)writeByte:(UInt8)value;
+- (void)writeBytes:(const void*)bytes length:(NSUInteger)length;
+- (void)writeData:(NSData*)data;
+- (void)writeSize:(UInt32)size;
+- (void)writeAlignment:(UInt8)alignment;
+- (void)writeUTF8:(NSString*)value;
+- (void)writeValue:(id )value;
+@end
+
+@interface FLEStandardReader : NSObject
+- (instancetype )initWithData:(NSData* )data;
+- (BOOL)hasMore;
+- (UInt8)readByte;
+- (void)readBytes:(void*)destination length:(NSUInteger)length;
+- (NSData*)readData:(NSUInteger)length;
+- (UInt32)readSize;
+- (void)readAlignment:(UInt8)alignment;
+- (NSString*)readUTF8;
+- (id _Nullable )readValue;
+- (id _Nullable )readValueOfType:(UInt8)type;
+@end
+
+
+@interface FLEStandardReaderWriter : NSObject
+- (FLEStandardWriter*)writerWithData:(NSMutableData*)data;
+- (FLEStandardReader*)readerWithData:(NSData*)data;
+@end
+
+
+/**
+ * A codec that uses JSON as the encoding format. Method arguments and error details for plugins
+ * using this codec must be serializable to JSON.
+ */
+@interface FLEStandardMethodCodec : NSObject
++ (instancetype _Nonnull )codecWithReaderWriter:(FLEStandardReaderWriter* _Nonnull)readerWriter;
+@end
+
+@interface FLEStandardTypedData : NSObject
+/**
+ Creates a `FlutterStandardTypedData` which interprets the specified data
+ as plain bytes.
+
+ - Parameter data: the byte data.
+ */
++ (instancetype)typedDataWithBytes:(NSData*)data;
+
+/**
+ Creates a `FlutterStandardTypedData` which interprets the specified data
+ as 32-bit signed integers.
+
+ - Parameter data: the byte data. The length must be divisible by 4.
+ */
++ (instancetype)typedDataWithInt32:(NSData*)data;
+
+/**
+ Creates a `FlutterStandardTypedData` which interprets the specified data
+ as 64-bit signed integers.
+
+ - Parameter data: the byte data. The length must be divisible by 8.
+ */
++ (instancetype)typedDataWithInt64:(NSData*)data;
+
+/**
+ Creates a `FlutterStandardTypedData` which interprets the specified data
+ as 64-bit floats.
+
+ - Parameter data: the byte data. The length must be divisible by 8.
+ */
++ (instancetype)typedDataWithFloat64:(NSData*)data;
+
++ (instancetype)typedDataWithData:(NSData*)data type:(FLEStandardDataType)type;
+
+/**
+ The raw underlying data buffer.
+ */
+@property(readonly, nonatomic) NSData* data;
+
+/**
+ The type of the encoded values.
+ */
+@property(readonly, nonatomic) FLEStandardDataType type;
+
+/**
+ The number of value items encoded.
+ */
+@property(readonly, nonatomic) UInt32 elementCount;
+
+/**
+ The number of bytes used by the encoding of a single value item.
+ */
+@property(readonly, nonatomic) UInt8 elementSize;
+@end
+
+@interface FLEStandardBigInteger : NSObject
+/**
+ Creates a `FlutterStandardBigInteger` from a hexadecimal representation.
+
+ - Parameter hex: a hexadecimal string.
+ */
++ (instancetype)bigIntegerWithHex:(NSString*)hex;
+
+/**
+ The hexadecimal string representation of this integer.
+ */
+@property(readonly, nonatomic) NSString* hex;
+@end
+
+
+NS_ASSUME_NONNULL_END
+// TODO: Implement the other core Flutter codecs. Issue #67.
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEOpenGLContextHandling.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEOpenGLContextHandling.h
new file mode 100644
index 00000000..4d54e6b1
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEOpenGLContextHandling.h
@@ -0,0 +1,32 @@
+// 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.
+
+/**
+ * Protocol for views owned by FLEViewController to handle context changes, specifically relating to
+ * OpenGL context changes.
+ */
+@protocol FLEOpenGLContextHandling
+
+/**
+ * Sets the receiver as the current context object.
+ */
+- (void)makeCurrentContext;
+
+/**
+ * Called when the display is updated. In an NSOpenGLView this is best handled via a flushBuffer
+ * call.
+ */
+- (void)onPresent;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEPlugin.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEPlugin.h
new file mode 100644
index 00000000..d5b57b98
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEPlugin.h
@@ -0,0 +1,68 @@
+// 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
+
+#import "FLEChannels.h"
+#import "FLECodecs.h"
+
+@class FLEViewController;
+
+/**
+ * A plugin is an object that can respond appropriately to Flutter platform messages over a specific
+ * named system channel. See https://flutter.io/platform-channels/.
+ *
+ * Note: This interface will be changing in the future to more closely match the iOS Flutter
+ * platform message API. It will be a one-time breaking change.
+ */
+@protocol FLEPlugin
+
+/**
+ * A weak reference to the owning controller. May be used to send messages to the Flutter
+ * framework.
+ */
+@property(nullable, weak) FLEViewController *controller;
+
+/**
+ * The name of the system channel via which this plugin communicates.
+ */
+@property(nonnull, readonly) NSString *channel;
+
+/**
+ * Called when a message is sent from Flutter on this plugin's channel.
+ * The result callback must be called exactly once, with one of:
+ * - FLEMethodNotImplemented, if the method call is unknown.
+ * - An FLEMethodError, if the method call was understood but there was a
+ * problem handling it.
+ * - Any other value (including nil) to indicate success. The value will
+ * be returned to the Flutter caller, and must be serializable to JSON.
+ *
+ * If handling the method involves multiple responses to Flutter, follow-up
+ * messages can be sent by calling the other direction using
+ * -[FLEViewController invokeMethod:arguments:onChannel:].
+ */
+- (void)handleMethodCall:(nonnull FLEMethodCall *)call result:(nonnull FLEMethodResult)result;
+
+@optional
+
+/**
+ * If implemented, returns the codec to use for method calls to this plugin.
+ *
+ * If not implemented, the codec is assumed to be FLEJSONMethodCodec. Note that this is different
+ * from existing Flutter platforms, which default to the standard codec; this is to preserve
+ * backwards compatibility for FLEPlugin until the breaking change for the platform messages API.
+ */
+@property(nonnull, readonly) id codec;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEReshapeListener.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEReshapeListener.h
new file mode 100644
index 00000000..f8557814
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEReshapeListener.h
@@ -0,0 +1,26 @@
+// 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
+
+/**
+ * Protocol for listening to reshape events on this FlutterView.
+ * Used to notify the underlying Flutter engine of the new screen dimensions.
+ * Reflected from [NSOpenGLView.reshape].
+ */
+@protocol FLEReshapeListener
+
+- (void)viewDidReshape:(nonnull NSOpenGLView *)view;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEView.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEView.h
new file mode 100644
index 00000000..1bf7a3f1
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEView.h
@@ -0,0 +1,29 @@
+// 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
+#import
+
+/**
+ * View capable of acting as a rendering target and input source for the Flutter
+ * engine.
+ */
+@interface FLEView : NSOpenGLView
+
+/**
+ * Listener for reshape events. See protocol description.
+ */
+@property(nonatomic, weak, nullable) IBOutlet id reshapeListener;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEViewController.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEViewController.h
new file mode 100644
index 00000000..3d8d6245
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FLEViewController.h
@@ -0,0 +1,116 @@
+// 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
+
+#import "FLECodecs.h"
+#import "FLEOpenGLContextHandling.h"
+#import "FLEPlugin.h"
+#import "FLEReshapeListener.h"
+
+/**
+ * Controls embedder plugins and communication with the underlying Flutter engine, managing a view
+ * intended to handle key inputs and drawing protocols (see |view|).
+ *
+ * Can be launched headless (no managed view), at which point a Dart executable will be run on the
+ * Flutter engine in non-interactive mode, or with a drawable Flutter canvas.
+ */
+@interface FLEViewController : NSViewController
+
+- (void)receivedUriLaunch:(nonnull NSString *)uri;
+
+
+- (void)setCurrentActivityMessage:(nonnull NSString *)message;
+
+- (void)showSignInView;
+
+- (void) hideSignInView;
+
+- (IBAction)signInCancelled:(id _Nonnull )sender;
+
+/**
+ * The view this controller manages when launched in interactive mode (headless set to false). Must
+ * be capable of handling text input events, and the OpenGL context handling protocols.
+ */
+@property(nullable) NSView *view;
+
+@property (strong, nullable) IBOutlet NSView *signInView;
+
+@property (nonatomic, retain, nullable) IBOutlet NSProgressIndicator* activityIndicator;
+
+@property (weak) IBOutlet NSTextField *currentActivityTextField;
+
+/**
+ * Launches the Flutter engine in snapshot mode with the provided configuration. The path argument
+ * is used to identify the Flutter application the engine should be configured with. See
+ * https://github.com/flutter/engine/wiki/Custom-Flutter-Engine-Embedders for more information
+ * on embedding, including the meaning of various possible arguments.
+ *
+ * In snapshot mode, the snapshot in the assets directory will be used instead of the original
+ * dart sources.
+ */
+- (BOOL)launchEngineWithAssetsPath:(nonnull NSURL *)assets
+ asHeadless:(BOOL)headless
+ commandLineArguments:(nonnull NSArray *)arguments;
+
+/**
+ * Launches the Flutter engine in source mode with the provided configuration. The path arguments
+ * are used to identify the Flutter application the engine should be configured with. See
+ * https://github.com/flutter/engine/wiki/Custom-Flutter-Engine-Embedders for more information
+ * on embedding, including the meaning of various possible arguments.
+ *
+ * In source mode, the Dart source and its dependencies will be read directly from their locations
+ * on disk.
+ * TODO: Evaluate whether this mode needs to be present in the long term, and if so reconsider this
+ * API structure.
+ */
+- (BOOL)launchEngineWithMainPath:(nonnull NSURL *)main
+ assetsPath:(nonnull NSURL *)assets
+ packagesPath:(nonnull NSURL *)packages
+ asHeadless:(BOOL)headless
+ commandLineArguments:(nonnull NSArray *)arguments;
+
+/**
+ * Adds a plugin to the view controller to handle domain-specific system messages. The plugin's
+ * channel property will determine which system channel the plugin operates on. Only one plugin
+ * can operate on a given named channel; if two plugins declare the same channel, the second
+ * add will return NO.
+ */
+- (BOOL)addPlugin:(nonnull id)plugin;
+
+/**
+ * Sends a platform message to the Flutter engine on |channel|, encoded using |codec|.
+ *
+ * // TODO: Move to an API that mirrors the FlutterMethodChannel API.
+ * // TODO: Support responses.
+ */
+- (void)invokeMethod:(nonnull NSString *)method
+ arguments:(nullable id)arguments
+ onChannel:(nonnull NSString *)channel
+ withCodec:(nonnull id)codec;
+
+/**
+ * Calls invokeMethod:arguments:onChannel:withCodec: using FLEJSONCodec. See the note in
+ * FLEPlugin.h for why JSON is the default.
+ */
+- (void)invokeMethod:(nonnull NSString *)method
+ arguments:(nullable id)arguments
+ onChannel:(nonnull NSString *)channel;
+
+@end
+
+@interface FoobarView : NSView
+
+@end
+
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FlutterEmbedderMacFeather.h b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FlutterEmbedderMacFeather.h
new file mode 100644
index 00000000..023bff70
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Headers/FlutterEmbedderMacFeather.h
@@ -0,0 +1,21 @@
+// 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 "FLEChannels.h"
+#import "FLECodecs.h"
+#import "FLEOpenGLContextHandling.h"
+#import "FLEPlugin.h"
+#import "FLEReshapeListener.h"
+#import "FLEView.h"
+#import "FLEViewController.h"
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Modules/module.modulemap b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 00000000..8f883a17
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module FlutterEmbedderMacFeather {
+ umbrella header "FlutterEmbedderMacFeather.h"
+
+ export *
+ module * { export * }
+}
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Resources/Info.plist b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 00000000..89adb754
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ BuildMachineOSBuild
+ 17G65
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ FlutterEmbedderMacFeather
+ CFBundleIdentifier
+ com.google.FlutterEmbedderMac
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ FlutterEmbedderMacFeather
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 9B55
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 17B41
+ DTSDKName
+ macosx10.13
+ DTXcode
+ 0910
+ DTXcodeBuild
+ 9B55
+ UIDeviceFamily
+
+ 1
+ 2
+
+
+
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/_CodeSignature/CodeResources b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/_CodeSignature/CodeResources
new file mode 100644
index 00000000..a0398173
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/A/_CodeSignature/CodeResources
@@ -0,0 +1,231 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ lktashG4aZTJEhxr605cyYaILHU=
+
+
+ files2
+
+ Headers/FLEChannels.h
+
+ hash
+
+ t5zUbejkzYkcAlKUVsA5M21gDSA=
+
+ hash2
+
+ 4mKeAe7nF9qt24yrhlHvHuT1v1Ye+wQcW+tGUrKLsTA=
+
+
+ Headers/FLECodecs.h
+
+ hash
+
+ JKfA4lXMRWQFJ8hB3iMQrIgmeZI=
+
+ hash2
+
+ Kzy9bmI1agViqQPBAyCN37rDNEV2YBD26K0b7j74TwI=
+
+
+ Headers/FLEOpenGLContextHandling.h
+
+ hash
+
+ rTAR+uM1y9Mt4sqmkBzoCPb1BxE=
+
+ hash2
+
+ Su0f5dK5dpmByikkCxttjOC846IpAHOto8V/e6pq5WQ=
+
+
+ Headers/FLEPlugin.h
+
+ hash
+
+ eKR2qITpnYr6cDxidyt3IlsBWvI=
+
+ hash2
+
+ dxZ8p/9jfuDU56q+1S8riMHCXA5/4xps6aTLpuCcxkU=
+
+
+ Headers/FLEReshapeListener.h
+
+ hash
+
+ LUMi2vMwfxTBRgMWHPm7Z/U030c=
+
+ hash2
+
+ OmLsafGShz9L3Z8NcLbXXxNEZjFe4bAJTfBM7lDePsI=
+
+
+ Headers/FLEView.h
+
+ hash
+
+ aJnsIo9F4+ZiD0o9PpmOmAQ/fYc=
+
+ hash2
+
+ Nrxc/b+P6qwhXL3zDta40TiZjxDL/xbuNG07Msw0JOM=
+
+
+ Headers/FLEViewController.h
+
+ hash
+
+ zr4PgAfxAkxYs7Z5QspOm2AHyy0=
+
+ hash2
+
+ ebi0gwZS2urwcXilvy6NSdXURspvZnPwuzeMF0b1zks=
+
+
+ Headers/FlutterEmbedderMacFeather.h
+
+ hash
+
+ uZ4eKVn+5raNoFuqiAh6O3apwBA=
+
+ hash2
+
+ ExeJ5QGTx+wHV+e8+kMxHG8bmip0MJilngighyodAYM=
+
+
+ Modules/module.modulemap
+
+ hash
+
+ 5eXQcABLV383S4akjJ/z3rJeN+I=
+
+ hash2
+
+ K2pPIp/B/AGYqEa/CAeWGoXUN3K5PyWMQHd1Xb4WxiM=
+
+
+ Resources/Info.plist
+
+ hash
+
+ lktashG4aZTJEhxr605cyYaILHU=
+
+ hash2
+
+ 5sg11gLbKPSLVK+zGrdT1cGF4rEyzY61kteayQzGUNM=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/feather/macos/FlutterEmbedderMacFeather.framework/Versions/Current b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/Current
new file mode 120000
index 00000000..8c7e5a66
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMacFeather.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/FlutterEmbedderMenubar b/feather/macos/FlutterEmbedderMenubar.framework/FlutterEmbedderMenubar
new file mode 120000
index 00000000..e6dbc0be
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/FlutterEmbedderMenubar
@@ -0,0 +1 @@
+Versions/Current/FlutterEmbedderMenubar
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Headers b/feather/macos/FlutterEmbedderMenubar.framework/Headers
new file mode 120000
index 00000000..a177d2a6
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Headers
@@ -0,0 +1 @@
+Versions/Current/Headers
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Modules b/feather/macos/FlutterEmbedderMenubar.framework/Modules
new file mode 120000
index 00000000..5736f318
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Modules
@@ -0,0 +1 @@
+Versions/Current/Modules
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Resources b/feather/macos/FlutterEmbedderMenubar.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/FlutterEmbedderMenubar b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/FlutterEmbedderMenubar
new file mode 100755
index 00000000..303f6793
Binary files /dev/null and b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/FlutterEmbedderMenubar differ
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FLEMenubarPlugin.h b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FLEMenubarPlugin.h
new file mode 100644
index 00000000..d751ee99
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FLEMenubarPlugin.h
@@ -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
+
+#import
+
+/**
+ * A Flutter plugin to control the native menu bar.
+ */
+@interface FLEMenubarPlugin : NSObject
+
+/**
+ * The menu item that Flutter-provided menus should be inserted after. If unset, Flutter-provided
+ * menus will be inserted after the application menu (i.e., starting at index 1).
+ */
+@property(nonatomic) NSMenuItem *insertAfterMenuItem;
+
+@end
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FlutterEmbedderMenubar.h b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FlutterEmbedderMenubar.h
new file mode 100644
index 00000000..cf7e9cce
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Headers/FlutterEmbedderMenubar.h
@@ -0,0 +1,15 @@
+// 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 "FLEMenubarPlugin.h"
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Modules/module.modulemap b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Modules/module.modulemap
new file mode 100644
index 00000000..c0c29431
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module FlutterEmbedderMenubar {
+ umbrella header "FlutterEmbedderMenubar.h"
+
+ export *
+ module * { export * }
+}
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Resources/Info.plist b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Resources/Info.plist
new file mode 100644
index 00000000..cab0d16a
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/Resources/Info.plist
@@ -0,0 +1,44 @@
+
+
+
+
+ BuildMachineOSBuild
+ 17G65
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ FlutterEmbedderMenubar
+ CFBundleIdentifier
+ com.google.FlutterEmbedderMenubar
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ FlutterEmbedderMenubar
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 9B55
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 17B41
+ DTSDKName
+ macosx10.13
+ DTXcode
+ 0910
+ DTXcodeBuild
+ 9B55
+ NSHumanReadableCopyright
+ Copyright © 2018 Google LLC. All rights reserved.
+
+
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/_CodeSignature/CodeResources b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/_CodeSignature/CodeResources
new file mode 100644
index 00000000..120ff65b
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/A/_CodeSignature/CodeResources
@@ -0,0 +1,165 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ TXRUQhCiny9jnk8/UNXj+9etaOU=
+
+
+ files2
+
+ Headers/FLEMenubarPlugin.h
+
+ hash
+
+ rwuFgTPBsXXvy9UEUts8PcTBhWE=
+
+ hash2
+
+ /uSrEvzhXQ4pP9BryiYfssFEvZcZheXuHcSy9QRyR6Y=
+
+
+ Headers/FlutterEmbedderMenubar.h
+
+ hash
+
+ Owj9VmFcYl/HVCK57y1vsF8wIo4=
+
+ hash2
+
+ p1KdGuH5TGbguNADO4nlyS8U7cSnLiepIJrvse7J1pQ=
+
+
+ Modules/module.modulemap
+
+ hash
+
+ mZ+PUPSHrEMG1DdWmD+PHkzG3Sc=
+
+ hash2
+
+ tDmy2s0D7II4n3NHSAPHghjgPSfC3Km8SxGjoUtqdAg=
+
+
+ Resources/Info.plist
+
+ hash
+
+ TXRUQhCiny9jnk8/UNXj+9etaOU=
+
+ hash2
+
+ mLPzl+7JZYsBNvyu0K+QDnaV+bIN8NnKQkkhxdvaYtU=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/feather/macos/FlutterEmbedderMenubar.framework/Versions/Current b/feather/macos/FlutterEmbedderMenubar.framework/Versions/Current
new file mode 120000
index 00000000..8c7e5a66
--- /dev/null
+++ b/feather/macos/FlutterEmbedderMenubar.framework/Versions/Current
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/feather/macos/Info.plist b/feather/macos/Info.plist
new file mode 100644
index 00000000..b8d0b3a0
--- /dev/null
+++ b/feather/macos/Info.plist
@@ -0,0 +1,36 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ $(PRODUCT_NAME)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0.0
+ CFBundleVersion
+ 1
+ LSApplicationCategoryType
+
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ Copyright © 2018. All rights reserved.
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/feather/macos/MainWindow.swift b/feather/macos/MainWindow.swift
new file mode 100644
index 00000000..abecf89c
--- /dev/null
+++ b/feather/macos/MainWindow.swift
@@ -0,0 +1,43 @@
+// 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
+
+class MainWindow: NSWindow {
+ @IBOutlet weak var flutterViewController: FLEViewController!
+
+ override func awakeFromNib() {
+ flutterViewController.add(FLEColorPanelPlugin())
+ flutterViewController.add(FLEFileChooserPlugin())
+ flutterViewController.add(FLEMenubarPlugin())
+
+ let assetsPath = Bundle.main.bundlePath + "/Contents/Resources/flutter_assets";
+ NSLog("Assets Path: %@", assetsPath);
+
+ let assets = NSURL.fileURL(withPath: assetsPath, isDirectory: true);
+ // Pass through argument zero, since the Flutter engine expects to be processing a full
+ // command line string.
+ var arguments = [CommandLine.arguments[0]];
+#if !DEBUG
+ arguments.append("--dart-non-checked-mode");
+#endif
+ flutterViewController.launchEngine(
+ withAssetsPath: assets,
+ asHeadless: false,
+ commandLineArguments: arguments)
+
+ super.awakeFromNib()
+ }
+}
+
diff --git a/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/AppAuth.xcscheme b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/AppAuth.xcscheme
new file mode 100644
index 00000000..433364ef
--- /dev/null
+++ b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/AppAuth.xcscheme
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMAppAuth.xcscheme b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMAppAuth.xcscheme
new file mode 100644
index 00000000..a2afd835
--- /dev/null
+++ b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMAppAuth.xcscheme
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme
new file mode 100644
index 00000000..53315c7a
--- /dev/null
+++ b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/GTMSessionFetcher.xcscheme
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/Pods-Example Embedder.xcscheme b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/Pods-Example Embedder.xcscheme
new file mode 100644
index 00000000..16da6269
--- /dev/null
+++ b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/Pods-Example Embedder.xcscheme
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plist b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 00000000..1cb509d5
--- /dev/null
+++ b/feather/macos/Pods/Pods.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ SchemeUserState
+
+ AppAuth.xcscheme
+
+ isShown
+
+ orderHint
+ 3
+
+ GTMAppAuth.xcscheme
+
+ isShown
+
+ orderHint
+ 4
+
+ GTMSessionFetcher.xcscheme
+
+ isShown
+
+ orderHint
+ 5
+
+ Pods-Example Embedder.xcscheme
+
+ isShown
+
+ orderHint
+ 6
+
+
+ SuppressBuildableAutocreation
+
+
+
diff --git a/feather/macos/SignInView.swift b/feather/macos/SignInView.swift
new file mode 100644
index 00000000..37417b11
--- /dev/null
+++ b/feather/macos/SignInView.swift
@@ -0,0 +1,16 @@
+import Cocoa
+
+
+class SignInView: NSView {
+
+ override init(frame frameRect: NSRect) {
+ super.init(frame:frameRect);
+// Bundle.main.loadNibNamed(NSNib.Name(rawValue: "SignInView"), owner: self, topLevelObjects: nil);
+// self.view.frame = NSMakeRect(0, 0, frame.size.width, frame.size.height);
+// self.addSubview(view);
+ }
+
+ required init?(coder decoder: NSCoder) {
+ super.init(coder: decoder);
+ }
+}
diff --git a/feather/macos/SignInView.xib b/feather/macos/SignInView.xib
new file mode 100644
index 00000000..ebda97f5
--- /dev/null
+++ b/feather/macos/SignInView.xib
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feather/macos/en.lproj/InfoPlist.strings b/feather/macos/en.lproj/InfoPlist.strings
new file mode 100644
index 00000000..e90f884c
--- /dev/null
+++ b/feather/macos/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+"CFBundleDisplayName" = "NKUST AP";
+"CFBundleName" = "NKUST AP";
diff --git a/feather/macos/en.lproj/MainMenu.strings b/feather/macos/en.lproj/MainMenu.strings
new file mode 100644
index 00000000..74bb6bb9
--- /dev/null
+++ b/feather/macos/en.lproj/MainMenu.strings
@@ -0,0 +1,195 @@
+
+/* Class = "NSMenuItem"; title = "FeatherApp"; ObjectID = "1Xt-HY-uBw"; */
+"1Xt-HY-uBw.title" = "NKUST AP";
+
+/* Class = "NSMenu"; title = "Find"; ObjectID = "1b7-l0-nxx"; */
+"1b7-l0-nxx.title" = "Find";
+
+/* Class = "NSMenuItem"; title = "Transformations"; ObjectID = "2oI-Rn-ZJC"; */
+"2oI-Rn-ZJC.title" = "Transformations";
+
+/* Class = "NSMenu"; title = "Spelling"; ObjectID = "3IN-sU-3Bg"; */
+"3IN-sU-3Bg.title" = "Spelling";
+
+/* Class = "NSMenu"; title = "Speech"; ObjectID = "3rS-ZA-NoH"; */
+"3rS-ZA-NoH.title" = "Speech";
+
+/* Class = "NSMenuItem"; title = "Find"; ObjectID = "4EN-yA-p0u"; */
+"4EN-yA-p0u.title" = "Find";
+
+/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "4J7-dP-txa"; */
+"4J7-dP-txa.title" = "Enter Full Screen";
+
+/* Class = "NSMenuItem"; title = "Quit FeatherApp"; ObjectID = "4sb-4s-VLi"; */
+"4sb-4s-VLi.title" = "Quit NKUST AP";
+
+/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "5QF-Oa-p0T"; */
+"5QF-Oa-p0T.title" = "Edit";
+
+/* Class = "NSMenuItem"; title = "About FeatherApp"; ObjectID = "5kV-Vb-QxS"; */
+"5kV-Vb-QxS.title" = "About NKUST AP";
+
+/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "6dh-zS-Vam"; */
+"6dh-zS-Vam.title" = "Redo";
+
+/* Class = "NSMenuItem"; title = "Correct Spelling Automatically"; ObjectID = "78Y-hA-62v"; */
+"78Y-hA-62v.title" = "Correct Spelling Automatically";
+
+/* Class = "NSMenuItem"; title = "Substitutions"; ObjectID = "9ic-FL-obx"; */
+"9ic-FL-obx.title" = "Substitutions";
+
+/* Class = "NSMenuItem"; title = "Smart Copy/Paste"; ObjectID = "9yt-4B-nSM"; */
+"9yt-4B-nSM.title" = "Smart Copy/Paste";
+
+/* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */
+"AYu-sK-qS6.title" = "Main Menu";
+
+/* Class = "NSMenuItem"; title = "Preferences…"; ObjectID = "BOF-NM-1cW"; */
+"BOF-NM-1cW.title" = "Preferences…";
+
+/* Class = "NSTextFieldCell"; title = "Waiting for web browser sign-in..."; ObjectID = "Cjz-n0-fmH"; */
+"Cjz-n0-fmH.title" = "Waiting for web browser sign-in...";
+
+/* Class = "NSMenuItem"; title = "Spelling and Grammar"; ObjectID = "Dv1-io-Yv7"; */
+"Dv1-io-Yv7.title" = "Spelling and Grammar";
+
+/* Class = "NSMenu"; title = "Substitutions"; ObjectID = "FeM-D8-WVr"; */
+"FeM-D8-WVr.title" = "Substitutions";
+
+/* Class = "NSMenuItem"; title = "View"; ObjectID = "H8h-7b-M4v"; */
+"H8h-7b-M4v.title" = "View";
+
+/* Class = "NSMenuItem"; title = "Text Replacement"; ObjectID = "HFQ-gK-NFA"; */
+"HFQ-gK-NFA.title" = "Text Replacement";
+
+/* Class = "NSMenuItem"; title = "Show Spelling and Grammar"; ObjectID = "HFo-cy-zxI"; */
+"HFo-cy-zxI.title" = "Show Spelling and Grammar";
+
+/* Class = "NSMenu"; title = "View"; ObjectID = "HyV-fh-RgO"; */
+"HyV-fh-RgO.title" = "View";
+
+/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "Kd2-mp-pUS"; */
+"Kd2-mp-pUS.title" = "Show All";
+
+/* Class = "NSMenuItem"; title = "Bring All to Front"; ObjectID = "LE2-aR-0XJ"; */
+"LE2-aR-0XJ.title" = "Bring All to Front";
+
+/* Class = "NSMenuItem"; title = "Services"; ObjectID = "NMo-om-nkz"; */
+"NMo-om-nkz.title" = "Services";
+
+/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "OY7-WF-poV"; */
+"OY7-WF-poV.title" = "Minimize";
+
+/* Class = "NSMenuItem"; title = "Hide FeatherApp"; ObjectID = "Olw-nP-bQN"; */
+"Olw-nP-bQN.title" = "Hide NKUST AP";
+
+/* Class = "NSMenuItem"; title = "Find Previous"; ObjectID = "OwM-mh-QMV"; */
+"OwM-mh-QMV.title" = "Find Previous";
+
+/* Class = "NSMenuItem"; title = "Stop Speaking"; ObjectID = "Oyz-dy-DGm"; */
+"Oyz-dy-DGm.title" = "Stop Speaking";
+
+/* Class = "NSWindow"; title = "FeatherApp"; ObjectID = "QvC-M9-y7g"; */
+"QvC-M9-y7g.title" = "NKUST AP";
+
+/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "R4o-n2-Eq4"; */
+"R4o-n2-Eq4.title" = "Zoom";
+
+/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "Ruw-6m-B2m"; */
+"Ruw-6m-B2m.title" = "Select All";
+
+/* Class = "NSMenuItem"; title = "Jump to Selection"; ObjectID = "S0p-oC-mLd"; */
+"S0p-oC-mLd.title" = "Jump to Selection";
+
+/* Class = "NSMenu"; title = "Window"; ObjectID = "Td7-aD-5lo"; */
+"Td7-aD-5lo.title" = "Window";
+
+/* Class = "NSMenuItem"; title = "Capitalize"; ObjectID = "UEZ-Bs-lqG"; */
+"UEZ-Bs-lqG.title" = "Capitalize";
+
+/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "Vdr-fp-XzO"; */
+"Vdr-fp-XzO.title" = "Hide Others";
+
+/* Class = "NSMenu"; title = "Edit"; ObjectID = "W48-6f-4Dl"; */
+"W48-6f-4Dl.title" = "Edit";
+
+/* Class = "NSMenuItem"; title = "Paste and Match Style"; ObjectID = "WeT-3V-zwk"; */
+"WeT-3V-zwk.title" = "Paste and Match Style";
+
+/* Class = "NSMenuItem"; title = "Find…"; ObjectID = "Xz5-n4-O0W"; */
+"Xz5-n4-O0W.title" = "Find…";
+
+/* Class = "NSMenuItem"; title = "Find and Replace…"; ObjectID = "YEy-JH-Tfz"; */
+"YEy-JH-Tfz.title" = "Find and Replace…";
+
+/* Class = "NSMenuItem"; title = "Start Speaking"; ObjectID = "Ynk-f8-cLZ"; */
+"Ynk-f8-cLZ.title" = "Start Speaking";
+
+/* Class = "NSMenuItem"; title = "Window"; ObjectID = "aUF-d1-5bR"; */
+"aUF-d1-5bR.title" = "Window";
+
+/* Class = "NSMenuItem"; title = "Use Selection for Find"; ObjectID = "buJ-ug-pKt"; */
+"buJ-ug-pKt.title" = "Use Selection for Find";
+
+/* Class = "NSMenu"; title = "Transformations"; ObjectID = "c8a-y6-VQd"; */
+"c8a-y6-VQd.title" = "Transformations";
+
+/* Class = "NSMenuItem"; title = "Smart Links"; ObjectID = "cwL-P1-jid"; */
+"cwL-P1-jid.title" = "Smart Links";
+
+/* Class = "NSMenuItem"; title = "Make Lower Case"; ObjectID = "d9M-CD-aMd"; */
+"d9M-CD-aMd.title" = "Make Lower Case";
+
+/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "dRJ-4n-Yzg"; */
+"dRJ-4n-Yzg.title" = "Undo";
+
+/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "fmb-7q-fym"; */
+"fmb-7q-fym.title" = "Cancel";
+
+/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "gVA-U4-sdL"; */
+"gVA-U4-sdL.title" = "Paste";
+
+/* Class = "NSMenuItem"; title = "Smart Quotes"; ObjectID = "hQb-2v-fYv"; */
+"hQb-2v-fYv.title" = "Smart Quotes";
+
+/* Class = "NSMenuItem"; title = "Check Document Now"; ObjectID = "hz2-CU-CR7"; */
+"hz2-CU-CR7.title" = "Check Document Now";
+
+/* Class = "NSMenu"; title = "Services"; ObjectID = "hz9-B4-Xy5"; */
+"hz9-B4-Xy5.title" = "Services";
+
+/* Class = "NSMenuItem"; title = "Check Grammar With Spelling"; ObjectID = "mK6-2p-4JG"; */
+"mK6-2p-4JG.title" = "Check Grammar With Spelling";
+
+/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "pa3-QI-u2k"; */
+"pa3-QI-u2k.title" = "Delete";
+
+/* Class = "NSMenuItem"; title = "Find Next"; ObjectID = "q09-fT-Sye"; */
+"q09-fT-Sye.title" = "Find Next";
+
+/* Class = "NSMenuItem"; title = "Check Spelling While Typing"; ObjectID = "rbD-Rh-wIN"; */
+"rbD-Rh-wIN.title" = "Check Spelling While Typing";
+
+/* Class = "NSMenuItem"; title = "Smart Dashes"; ObjectID = "rgM-f4-ycn"; */
+"rgM-f4-ycn.title" = "Smart Dashes";
+
+/* Class = "NSMenuItem"; title = "Data Detectors"; ObjectID = "tRr-pd-1PS"; */
+"tRr-pd-1PS.title" = "Data Detectors";
+
+/* Class = "NSMenu"; title = "FeatherApp"; ObjectID = "uQy-DD-JDr"; */
+"uQy-DD-JDr.title" = "NKUST AP";
+
+/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */
+"uRl-iY-unG.title" = "Cut";
+
+/* Class = "NSMenuItem"; title = "Make Upper Case"; ObjectID = "vmV-6d-7jI"; */
+"vmV-6d-7jI.title" = "Make Upper Case";
+
+/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "x3v-GG-iWU"; */
+"x3v-GG-iWU.title" = "Copy";
+
+/* Class = "NSMenuItem"; title = "Speech"; ObjectID = "xrE-MZ-jX0"; */
+"xrE-MZ-jX0.title" = "Speech";
+
+/* Class = "NSMenuItem"; title = "Show Substitutions"; ObjectID = "z6F-FW-3nz"; */
+"z6F-FW-3nz.title" = "Show Substitutions";
diff --git a/lib/main.dart b/lib/main.dart
index 032ca569..ea90a366 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -5,6 +5,7 @@ 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/material.dart';
import 'package:flutter_crashlytics/flutter_crashlytics.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
@@ -18,37 +19,46 @@ import 'package:nkust_ap/utils/utils.dart';
void main() async {
bool isInDebugMode = 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);
- }
- };
+ 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);
+ }
+ };
- await FlutterCrashlytics().initialize();
+ await FlutterCrashlytics().initialize();
- runZoned>(() async {
+ runZoned>(() async {
+ runApp(MyApp());
+ }, 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());
- }, 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);
- });
+ //TODO add other platform Crashlytics
+ }
}
class MyApp extends StatelessWidget {
- final FirebaseAnalytics analytics = FirebaseAnalytics();
- final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
+ FirebaseAnalytics analytics;
+ FirebaseMessaging _firebaseMessaging;
@override
Widget build(BuildContext context) {
- _initFCM();
- FA.analytics = analytics;
+ if (Platform.isAndroid || Platform.isIOS) {
+ analytics = FirebaseAnalytics();
+ _firebaseMessaging = FirebaseMessaging();
+ _initFCM();
+ FA.analytics = analytics;
+ }
return new MaterialApp(
localeResolutionCallback:
(Locale locale, Iterable supportedLocales) {
@@ -85,9 +95,11 @@ class MyApp extends StatelessWidget {
UnderlineInputBorder(borderSide: BorderSide(color: Colors.white)),
),
),
- navigatorObservers: [
- FirebaseAnalyticsObserver(analytics: analytics),
- ],
+ navigatorObservers: (Platform.isIOS || Platform.isAndroid)
+ ? [
+ FirebaseAnalyticsObserver(analytics: analytics),
+ ]
+ : [],
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
diff --git a/lib/pages/home/about/about_us_page.dart b/lib/pages/home/about/about_us_page.dart
index 92bc8b3f..2771d67a 100644
--- a/lib/pages/home/about/about_us_page.dart
+++ b/lib/pages/home/about/about_us_page.dart
@@ -56,6 +56,7 @@ class AboutUsPageState extends State
onPressed: () {
Navigator.of(context)
.pushNamed(OpenSourcePage.routerName);
+ FA.logAction('open_source', 'click');
})
],
backgroundColor: Resource.Colors.blue,
@@ -121,9 +122,16 @@ class AboutUsPageState extends State
Utils.launchUrl('fb://page/735951703168873')
.catchError((onError) => Utils.launchUrl(
'https://www.facebook.com/NKUST.ITC/'));
- else
+ else if (Platform.isIOS)
Utils.launchUrl(
'https://www.facebook.com/NKUST.ITC/');
+ else
+ Utils.launchUrl(
+ 'https://www.facebook.com/NKUST.ITC/')
+ .catchError((onError) =>
+ Utils.showToast(app.platformError));
+ ;
+ FA.logAction('fb', 'click');
},
iconSize: 48.0,
),
@@ -135,15 +143,23 @@ class AboutUsPageState extends State
'github://organization/NKUST-ITC')
.catchError((onError) => Utils.launchUrl(
'https://github.com/NKUST-ITC'));
- else
+ else if (Platform.isIOS)
Utils.launchUrl('https://github.com/NKUST-ITC');
+ else
+ Utils.launchUrl('https://github.com/NKUST-ITC')
+ .catchError((onError) =>
+ Utils.showToast(app.platformError));
+ FA.logAction('github', 'click');
},
iconSize: 48.0,
),
IconButton(
icon: Image.asset("assets/images/ic_email.webp"),
onPressed: () {
- Utils.launchUrl('mailto:abc873693@gmail.com');
+ Utils.launchUrl('mailto:abc873693@gmail.com')
+ .catchError((onError) =>
+ Utils.showToast(app.platformError));
+ FA.logAction('email', 'click');
},
iconSize: 48.0,
),
diff --git a/lib/pages/home/bus/bus_reservations_page.dart b/lib/pages/home/bus/bus_reservations_page.dart
index b3d5e9e2..e2182201 100644
--- a/lib/pages/home/bus/bus_reservations_page.dart
+++ b/lib/pages/home/bus/bus_reservations_page.dart
@@ -67,7 +67,10 @@ class BusReservationsPageState extends State
case _State.error:
case _State.empty:
return FlatButton(
- onPressed: _getBusReservations,
+ onPressed: () {
+ _getBusReservations();
+ FA.logAction('retry', 'click');
+ },
child: HintContent(
icon: Icons.assignment,
content: state == _State.error
@@ -77,7 +80,10 @@ class BusReservationsPageState extends State
);
default:
return RefreshIndicator(
- onRefresh: () => _getBusReservations(),
+ onRefresh: () {
+ _getBusReservations();
+ FA.logAction('refresh', 'swipe');
+ },
child: ListView(
children: busReservationWeights,
),
@@ -142,12 +148,14 @@ class BusReservationsPageState extends State
textAlign: TextAlign.center,
),
leftActionText: app.back,
- rightActionText: app.busCancelReserve,
+ rightActionText: app.determine,
rightActionFunction: () {
_cancelBusReservation(busReservation);
+ FA.logAction('cancel_bus', 'click');
},
),
);
+ FA.logAction('cancel_bus', 'create');
},
)
: Container(),
@@ -232,6 +240,8 @@ class BusReservationsPageState extends State
style: TextStyle(
color: Resource.Colors.grey, height: 1.3, fontSize: 16.0),
);
+ FA.logAction('cancel_bus', 'status',
+ message: 'fail_${response.data["message"]}');
} else {
title = app.busCancelReserveSuccess;
messageWidget = RichText(
@@ -264,6 +274,7 @@ class BusReservationsPageState extends State
]),
);
_getBusReservations();
+ FA.logAction('cancel_bus', 'status', message: 'success');
}
Navigator.pop(context, 'dialog');
showDialog(
@@ -275,21 +286,19 @@ class BusReservationsPageState extends State
actionFunction: () =>
Navigator.of(context, rootNavigator: true).pop('dialog')),
);
- //Utils.showDefaultDialog(context, title, message, app.iKnow, () {});
}).catchError((e) {
Navigator.pop(context, 'dialog');
if (e is DioError) {
switch (e.type) {
case DioErrorType.RESPONSE:
- Utils.handleResponseError(
- context, 'getBusReservations', mounted, e);
+ Utils.handleResponseError(context, 'cancel_bus', mounted, e);
break;
case DioErrorType.DEFAULT:
if (e.message.contains("HttpException")) {
setState(() {
state = _State.error;
- Utils.showToast(app.busFailInfinity);
});
+ Utils.showToast(app.busFailInfinity);
}
break;
case DioErrorType.CANCEL:
diff --git a/lib/pages/home/bus/bus_reserve_page.dart b/lib/pages/home/bus/bus_reserve_page.dart
index 70a8aa62..e7fe444f 100644
--- a/lib/pages/home/bus/bus_reserve_page.dart
+++ b/lib/pages/home/bus/bus_reserve_page.dart
@@ -71,7 +71,10 @@ class BusReservePageState extends State
case _State.error:
case _State.empty:
return FlatButton(
- onPressed: _getBusTimeTables,
+ onPressed: () {
+ _getBusTimeTables();
+ FA.logAction('retry', 'click');
+ },
child: HintContent(
icon: Icons.assignment,
content: state == _State.error ? app.clickToRetry : app.busEmpty,
@@ -79,7 +82,10 @@ class BusReservePageState extends State
);
default:
return RefreshIndicator(
- onRefresh: () => _getBusTimeTables(),
+ onRefresh: () {
+ _getBusTimeTables();
+ FA.logAction('refresh', 'swipe');
+ },
child: ListView(
physics: const NeverScrollableScrollPhysics(),
children: _renderBusTimeWidgets(),
@@ -155,7 +161,27 @@ class BusReservePageState extends State
),
);
}
- : null,
+ : () {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => YesNoDialog(
+ title: app.busCancelReserve,
+ contentWidget: Text(
+ "${app.busCancelReserveConfirmContent1}${busTime.getStart(app)}"
+ "${app.busCancelReserveConfirmContent2}${busTime.getEnd(app)}\n"
+ "${busTime.getTime()}${app.busCancelReserveConfirmContent3}",
+ textAlign: TextAlign.center,
+ ),
+ leftActionText: app.back,
+ rightActionText: app.determine,
+ rightActionFunction: () {
+ _cancelBusReservation(busTime);
+ FA.logAction('cancel_bus', 'click');
+ },
+ ),
+ );
+ FA.logAction('cancel_bus', 'create');
+ },
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -247,6 +273,7 @@ class BusReservePageState extends State
onDateSelected: (DateTime datetime) {
dateTime = datetime;
_getBusTimeTables();
+ FA.logAction('date_select', 'click');
},
initialCalendarDateOverride: dateTime,
dayChildAspectRatio:
@@ -290,6 +317,7 @@ class BusReservePageState extends State
selectStartStation = text;
});
}
+ FA.logAction('segment', 'click');
},
),
),
@@ -364,7 +392,7 @@ class BusReservePageState extends State
builder: (BuildContext context) => ProgressDialog(app.reserving),
barrierDismissible: true);
Helper.instance.bookingBusReservation(busTime.busId).then((response) {
- //TODO 優化成物件
+ //TODO to object
String title = "";
Widget messageWidget;
if (!response.data["success"]) {
@@ -374,6 +402,8 @@ class BusReservePageState extends State
style: TextStyle(
color: Resource.Colors.grey, height: 1.3, fontSize: 16.0),
);
+ FA.logAction('book_bus', 'status',
+ message: 'fail_${response.data["message"]}');
} else {
title = app.busReserveSuccess;
messageWidget = RichText(
@@ -406,6 +436,7 @@ class BusReservePageState extends State
]),
);
_getBusTimeTables();
+ FA.logAction('book_bus', 'status', message: 'success');
}
Navigator.pop(context, 'dialog');
showDialog(
@@ -423,7 +454,7 @@ class BusReservePageState extends State
if (e is DioError) {
switch (e.type) {
case DioErrorType.RESPONSE:
- Utils.handleResponseError(context, 'bookingBus', mounted, e);
+ Utils.handleResponseError(context, 'book_bus', mounted, e);
break;
case DioErrorType.DEFAULT:
if (e.message.contains("HttpException")) {
@@ -446,4 +477,92 @@ class BusReservePageState extends State
}
});
}
+
+ _cancelBusReservation(BusTime busTime) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => ProgressDialog(app.canceling),
+ barrierDismissible: true);
+ Helper.instance.cancelBusReservation(busTime.cancelKey).then((response) {
+ String title = "";
+ Widget messageWidget;
+ if (!response.data["success"]) {
+ title = app.busCancelReserveFail;
+ messageWidget = Text(
+ response.data["message"],
+ style: TextStyle(
+ color: Resource.Colors.grey, height: 1.3, fontSize: 16.0),
+ );
+ FA.logAction('cancel_bus', 'status',
+ message: 'fail_${response.data["message"]}');
+ } else {
+ title = app.busCancelReserveSuccess;
+ messageWidget = RichText(
+ textAlign: TextAlign.left,
+ text: TextSpan(
+ style: TextStyle(
+ color: Resource.Colors.grey, height: 1.3, fontSize: 16.0),
+ children: [
+ TextSpan(
+ text: '${app.busReserveCancelDate}:',
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ TextSpan(
+ text: '${busTime.getDate()}\n',
+ ),
+ TextSpan(
+ text: '${app.busReserveCancelLocation}:',
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ TextSpan(
+ text: '${busTime.getStart(app)}${app.campus}\n',
+ ),
+ TextSpan(
+ text: '${app.busReserveCancelTime}:',
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ TextSpan(
+ text: '${busTime.getTime()}',
+ ),
+ ]),
+ );
+ _getBusTimeTables();
+ FA.logAction('cancel_bus', 'status', message: 'success');
+ }
+ Navigator.pop(context, 'dialog');
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => DefaultDialog(
+ title: title,
+ contentWidget: messageWidget,
+ actionText: app.iKnow,
+ actionFunction: () =>
+ Navigator.of(context, rootNavigator: true).pop('dialog')),
+ );
+ }).catchError((e) {
+ Navigator.pop(context, 'dialog');
+ if (e is DioError) {
+ switch (e.type) {
+ case DioErrorType.RESPONSE:
+ Utils.handleResponseError(context, 'cancel_bus', mounted, e);
+ break;
+ case DioErrorType.DEFAULT:
+ if (e.message.contains("HttpException")) {
+ setState(() {
+ state = _State.error;
+ });
+ Utils.showToast(app.busFailInfinity);
+ }
+ break;
+ case DioErrorType.CANCEL:
+ break;
+ default:
+ Utils.handleDioError(e, app);
+ break;
+ }
+ } else {
+ throw e;
+ }
+ });
+ }
}
diff --git a/lib/pages/home/bus/bus_rule_page.dart b/lib/pages/home/bus/bus_rule_page.dart
index c19b07e5..d27254b3 100644
--- a/lib/pages/home/bus/bus_rule_page.dart
+++ b/lib/pages/home/bus/bus_rule_page.dart
@@ -30,6 +30,7 @@ class BusRulePageState extends State
@override
void initState() {
super.initState();
+ FA.setCurrentScreen("BusRulePage", "bus_rule_page.dart");
}
@override
@@ -40,7 +41,7 @@ class BusRulePageState extends State
@override
Widget build(BuildContext context) {
app = AppLocalizations.of(context);
- ;
+ //TODO English version
return new Scaffold(
appBar: AppBar(
title: Text(app.busRule),
diff --git a/lib/pages/home/calculate_units_page.dart b/lib/pages/home/calculate_units_page.dart
index a6a1bdae..b2dacc49 100644
--- a/lib/pages/home/calculate_units_page.dart
+++ b/lib/pages/home/calculate_units_page.dart
@@ -153,7 +153,10 @@ class CalculateUnitsPageState extends State
Expanded(
flex: 19,
child: RefreshIndicator(
- onRefresh: () => _calculate(),
+ onRefresh: () {
+ FA.logAction('refresh', 'swipe');
+ _calculate();
+ },
child: _body(),
),
),
diff --git a/lib/pages/home/course_page.dart b/lib/pages/home/course_page.dart
index ad6d53e0..32a12cdf 100644
--- a/lib/pages/home/course_page.dart
+++ b/lib/pages/home/course_page.dart
@@ -146,6 +146,7 @@ class CoursePageState extends State
),
),
);
+ FA.logAction('show_course', 'click');
},
child: Text(
(course.title[0] + course.title[1]) ?? "",
@@ -162,7 +163,13 @@ class CoursePageState extends State
case _State.empty:
case _State.error:
return FlatButton(
- onPressed: state == _State.error ? _getCourseTables : _selectSemester,
+ onPressed: () {
+ if (state == _State.error)
+ _getCourseTables();
+ else
+ _selectSemester();
+ FA.logAction('retry', 'click');
+ },
child: HintContent(
icon: Icons.class_,
content:
@@ -236,7 +243,10 @@ class CoursePageState extends State
Expanded(
flex: 19,
child: RefreshIndicator(
- onRefresh: () => _getCourseTables(),
+ onRefresh: () {
+ _getCourseTables();
+ FA.logAction('refresh', 'swipe');
+ },
child: _body(),
),
),
@@ -312,6 +322,7 @@ class CoursePageState extends State
for (var semester in semesterData.semesters) {
semesters.add(_dialogItem(semesters.length, semester.text));
}
+ FA.logAction('pick_yms', 'click');
showDialog(
context: context,
builder: (BuildContext context) => SimpleDialog(
diff --git a/lib/pages/home/info/notification_page.dart b/lib/pages/home/info/notification_page.dart
index 26778794..d5098fcd 100644
--- a/lib/pages/home/info/notification_page.dart
+++ b/lib/pages/home/info/notification_page.dart
@@ -66,11 +66,15 @@ class NotificationPageState extends State
return GestureDetector(
onLongPress: () {
Utils.shareTo("${notification.info.title}\n${notification.link}");
+ FA.logAction('share', 'long_click',
+ message: '${notification.info.title}');
},
child: FlatButton(
padding: EdgeInsets.all(0.0),
onPressed: () {
Utils.launchUrl(notification.link);
+ FA.logAction('notification_link"', 'click',
+ message: '${notification.info.title}');
},
child: Container(
width: double.infinity,
@@ -126,9 +130,11 @@ class NotificationPageState extends State
child: CircularProgressIndicator(), alignment: Alignment.center);
case _State.error:
case _State.empty:
- //TODO 優化
+ //TODO improve
return FlatButton(
- onPressed: () {},
+ onPressed: () {
+ FA.logAction('rerty', 'click');
+ },
child: HintContent(
icon: Icons.assignment,
content:
@@ -143,6 +149,7 @@ class NotificationPageState extends State
});
notificationList.clear();
_getNotifications();
+ FA.logAction('refresh', 'swipe');
},
child: ListView.builder(
controller: controller,
diff --git a/lib/pages/home/info/phone_page.dart b/lib/pages/home/info/phone_page.dart
index 8f1e85c8..66d2d73a 100644
--- a/lib/pages/home/info/phone_page.dart
+++ b/lib/pages/home/info/phone_page.dart
@@ -101,7 +101,13 @@ class PhonePageState extends State
),
),
onPressed: () {
- Utils.callPhone(phone.number);
+ FA.logAction('call_phone', 'click');
+ try {
+ Utils.callPhone(phone.number);
+ FA.logAction('call_phone', 'status', message: 'succes');
+ } catch (e) {
+ FA.logAction('call_phone', 'status', message: 'error');
+ }
},
);
}
@@ -159,8 +165,8 @@ class PhonePageState extends State
phoneList.add(PhoneModel("楠梓校區", ""));
phoneList.add(PhoneModel("總機", "07-3617141"));
phoneList.add(PhoneModel("課外活動組", "07-3617141 #22070"));
- phoneList.add(PhoneModel("海洋校區", ""));
- phoneList.add(PhoneModel("海洋校區", "07-8100888"));
+ phoneList.add(PhoneModel("旗津校區", ""));
+ phoneList.add(PhoneModel("旗津校區", "07-8100888"));
phoneList.add(PhoneModel("學生事務處", "07-3617141 #2052"));
phoneList.add(PhoneModel("課外活動組", "07-8100888 #25065"));
phoneList.add(PhoneModel("生活輔導組", "07-3617141 #23967"));
diff --git a/lib/pages/home/info/schedule_page.dart b/lib/pages/home/info/schedule_page.dart
index ade8f7aa..b1183581 100644
--- a/lib/pages/home/info/schedule_page.dart
+++ b/lib/pages/home/info/schedule_page.dart
@@ -177,6 +177,7 @@ class SchedulePageState extends State
return FlatButton(
padding: EdgeInsets.all(0.0),
onPressed: () {
+ FA.logAction('add_schedule', 'create');
showDialog(
context: context,
builder: (BuildContext context) => YesNoDialog(
@@ -200,6 +201,7 @@ class SchedulePageState extends State
leftActionFunction: null,
rightActionFunction: () {
_addToCalendar(schedule.events[index]);
+ FA.logAction('add_schedule', 'click');
},
),
);
diff --git a/lib/pages/home/news_content_page.dart b/lib/pages/home/news_content_page.dart
index b1d4dd10..40adc0cb 100644
--- a/lib/pages/home/news_content_page.dart
+++ b/lib/pages/home/news_content_page.dart
@@ -144,6 +144,10 @@ class NewsContentPageState extends State
),
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,
diff --git a/lib/pages/home/score_page.dart b/lib/pages/home/score_page.dart
index 673ce095..e5063cc4 100644
--- a/lib/pages/home/score_page.dart
+++ b/lib/pages/home/score_page.dart
@@ -148,7 +148,10 @@ class ScorePageState extends State
Expanded(
flex: 19,
child: RefreshIndicator(
- onRefresh: () => _getSemesterScore(),
+ onRefresh: () {
+ _getSemesterScore();
+ FA.logAction('refresh', 'swipe');
+ },
child: _body(),
),
),
@@ -166,8 +169,13 @@ class ScorePageState extends State
case _State.error:
case _State.empty:
return FlatButton(
- onPressed:
- state == _State.error ? _getSemesterScore : _selectSemester,
+ onPressed: () {
+ if (state == _State.error)
+ _getSemesterScore();
+ else
+ _selectSemester();
+ FA.logAction('retry', 'click');
+ },
child: HintContent(
icon: Icons.assignment,
content: state == _State.error ? app.clickToRetry : app.scoreEmpty,
@@ -242,6 +250,7 @@ class ScorePageState extends State
for (var semester in semesterData.semesters) {
semesters.add(_dialogItem(semesters.length, semester.text));
}
+ FA.logAction('pick_yms', 'click');
showDialog(
context: context,
builder: (BuildContext context) => SimpleDialog(
diff --git a/lib/pages/home/setting_page.dart b/lib/pages/home/setting_page.dart
index 874b410c..e34c73cb 100644
--- a/lib/pages/home/setting_page.dart
+++ b/lib/pages/home/setting_page.dart
@@ -60,66 +60,82 @@ class SettingPageState extends State
backgroundColor: Resource.Colors.blue,
),
body: SingleChildScrollView(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _titleItem(app.notificationItem),
- _itemSwitch(app.courseNotify, courseNotify, () async {
- if (!courseNotify)
- _setupCourseNotify(context);
- else {
- await Utils.cancelCourseNotify();
- }
- setState(() {
- courseNotify = !courseNotify;
- });
- prefs.setBool(Constants.PREF_COURSE_NOTIFY, courseNotify);
- }),
- _itemSwitch(app.busNotify, busNotify, () async {
- bool bus = prefs.getBool(Constants.PREF_BUS_ENABLE) ?? true;
- if (bus) {
- if (!busNotify)
- _setupBusNotify(context);
- else {
- await Utils.cancelBusNotify();
- }
- setState(() {
- busNotify = !busNotify;
- });
- prefs.setBool(Constants.PREF_BUS_NOTIFY, busNotify);
- } else {
- Utils.showToast(app.canNotUseFeature);
- }
- }),
- Container(
- color: Colors.grey,
- height: 0.5,
- ),
- _titleItem(app.otherSettings),
- _itemSwitch(app.headPhotoSetting, displayPicture, () {
- displayPicture = !displayPicture;
- prefs.setBool(Constants.PREF_DISPLAY_PICTURE, displayPicture);
- setState(() {});
- }),
- Container(
- color: Colors.grey,
- height: 0.5,
- ),
- _titleItem(app.otherInfo),
- _item(app.feedback, app.feedbackViaFacebook, () {
- if (Platform.isAndroid)
- Utils.launchUrl('fb://messaging/954175941266264').catchError(
- (onError) => Utils.launchUrl(
- 'https://www.facebook.com/954175941266264/'));
- else
- Utils.launchUrl('https://www.facebook.com/954175941266264/');
- }),
- _item(app.donateTitle, app.donateContent, () {
- Utils.launchUrl(
- "https://payment.ecpay.com.tw/QuickCollect/PayData?mLM7iy8RpUGk%2fyBotSDMdvI0qGI5ToToqBW%2bOQbOE80%3d");
- }),
- _item(app.appVersion, "v$appVersion", () {}),
- ]),
+ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
+ Widget>[
+ _titleItem(app.notificationItem),
+ _itemSwitch(app.courseNotify, courseNotify, () async {
+ FA.logAction('notify_course', 'create');
+ setState(() {
+ courseNotify = !courseNotify;
+ });
+ if (courseNotify)
+ _setupCourseNotify(context);
+ else {
+ await Utils.cancelCourseNotify();
+ }
+ FA.logAction('notify_course', 'create', message: '$courseNotify');
+ prefs.setBool(Constants.PREF_COURSE_NOTIFY, courseNotify);
+ }),
+ _itemSwitch(app.busNotify, busNotify, () async {
+ FA.logAction('notify_bus', 'create');
+ bool bus = prefs.getBool(Constants.PREF_BUS_ENABLE) ?? true;
+ if (bus) {
+ setState(() {
+ busNotify = !busNotify;
+ });
+ if (busNotify)
+ _setupBusNotify(context);
+ else {
+ await Utils.cancelBusNotify();
+ }
+ prefs.setBool(Constants.PREF_BUS_NOTIFY, busNotify);
+ FA.logAction('notify_bus', 'click', message: '$busNotify');
+ } else {
+ Utils.showToast(app.canNotUseFeature);
+ FA.logAction('notify_bus', 'staus',
+ message: 'can\'t use feature');
+ }
+ }),
+ Container(
+ color: Colors.grey,
+ height: 0.5,
+ ),
+ _titleItem(app.otherSettings),
+ _itemSwitch(app.headPhotoSetting, displayPicture, () {
+ prefs.setBool(Constants.PREF_DISPLAY_PICTURE, displayPicture);
+ setState(() {
+ displayPicture = !displayPicture;
+ });
+ FA.logAction('head_photo', 'click');
+ }),
+ Container(
+ color: Colors.grey,
+ height: 0.5,
+ ),
+ _titleItem(app.otherInfo),
+ _item(app.feedback, app.feedbackViaFacebook, () {
+ if (Platform.isAndroid)
+ Utils.launchUrl('fb://messaging/954175941266264').catchError(
+ (onError) => Utils.launchUrl(
+ 'https://www.facebook.com/954175941266264/'));
+ else if (Platform.isIOS)
+ Utils.launchUrl('https://www.facebook.com/954175941266264/');
+ else {
+ Utils.launchUrl('https://www.facebook.com/954175941266264/')
+ .catchError((onError) => Utils.showToast(app.platformError));
+ }
+ FA.logAction('feedback', 'click');
+ }),
+ _item(app.donateTitle, app.donateContent, () {
+ Utils.launchUrl(
+ "https://payment.ecpay.com.tw/QuickCollect/PayData?mLM7iy8RpUGk%2fyBotSDMdvI0qGI5ToToqBW%2bOQbOE80%3d")
+ .catchError((onError) => Utils.showToast(app.platformError));
+ FA.logAction('donate', 'click');
+ }),
+ _item(app.appVersion, "v$appVersion", () {
+ //FA.logAction('donate', 'click');
+ }),
+ ]),
),
);
}
@@ -158,11 +174,12 @@ class SettingPageState extends State
_getPreference() async {
prefs = await SharedPreferences.getInstance();
PackageInfo packageInfo = await PackageInfo.fromPlatform();
- appVersion = packageInfo.version;
- courseNotify = prefs.getBool(Constants.PREF_COURSE_NOTIFY) ?? false;
- displayPicture = prefs.getBool(Constants.PREF_DISPLAY_PICTURE) ?? true;
- busNotify = prefs.getBool(Constants.PREF_BUS_NOTIFY) ?? false;
- setState(() {});
+ setState(() {
+ appVersion = packageInfo.version;
+ courseNotify = prefs.getBool(Constants.PREF_COURSE_NOTIFY) ?? false;
+ displayPicture = prefs.getBool(Constants.PREF_DISPLAY_PICTURE) ?? true;
+ busNotify = prefs.getBool(Constants.PREF_BUS_NOTIFY) ?? false;
+ });
}
_item(String text, String subText, Function function) => FlatButton(
@@ -242,8 +259,8 @@ class SettingPageState extends State
}).catchError((e) {
setState(() {
courseNotify = false;
- prefs.setBool(Constants.PREF_COURSE_NOTIFY, courseNotify);
});
+ prefs.setBool(Constants.PREF_COURSE_NOTIFY, courseNotify);
if (e is DioError) {
switch (e.type) {
case DioErrorType.RESPONSE:
@@ -275,8 +292,8 @@ class SettingPageState extends State
}).catchError((e) {
setState(() {
busNotify = false;
- prefs.setBool(Constants.PREF_BUS_NOTIFY, busNotify);
});
+ prefs.setBool(Constants.PREF_BUS_NOTIFY, busNotify);
if (Navigator.canPop(context)) Navigator.pop(context, 'dialog');
if (e is DioError) {
switch (e.type) {
diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart
index 900261a7..2e85a699 100644
--- a/lib/pages/home_page.dart
+++ b/lib/pages/home_page.dart
@@ -62,6 +62,10 @@ class HomePageState extends State {
child: GestureDetector(
onTap: () {
Navigator.of(context).push(NewsContentPageRoute(news));
+ String message = news.content.length > 12
+ ? news.content
+ : news.content.substring(0, 12);
+ FA.logAction('news_image', 'click', message: message);
},
child: Hero(
tag: news.hashCode,
@@ -119,6 +123,7 @@ class HomePageState extends State {
setState(() {
_currentNewsIndex = current;
});
+ FA.logAction('news_image', 'swipe');
},
),
SizedBox(height: orientation == Orientation.portrait ? 16.0 : 4.0),
diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart
index ead17e18..c39ce1bf 100644
--- a/lib/pages/login_page.dart
+++ b/lib/pages/login_page.dart
@@ -187,7 +187,10 @@ class LoginPageState extends State
),
),
padding: EdgeInsets.all(14.0),
- onPressed: _login,
+ onPressed: () {
+ FA.logAction('login', 'click');
+ _login();
+ },
color: Colors.white,
child: Text(
app.login,
@@ -384,8 +387,6 @@ class LoginPageState extends State
}),
barrierDismissible: false);
prefs.setString(Constants.PREF_USERNAME, _username.text);
- if (isRememberPassword)
- prefs.setString(Constants.PREF_PASSWORD, _password.text);
Helper.instance
.login(_username.text, _password.text)
.then((LoginResponse response) async {
@@ -409,6 +410,7 @@ class LoginPageState extends State
switch (e.type) {
case DioErrorType.RESPONSE:
Utils.showToast(app.loginFail);
+ Utils.handleResponseError(context, 'login', mounted, e);
break;
case DioErrorType.CANCEL:
break;
diff --git a/lib/utils/app_localizations.dart b/lib/utils/app_localizations.dart
index 369b04b7..133e6a33 100644
--- a/lib/utils/app_localizations.dart
+++ b/lib/utils/app_localizations.dart
@@ -20,8 +20,7 @@ class AppLocalizations {
'en': {
'app_name': 'NKUST AP',
'update_note_title': 'Update Notes',
- 'update_note_content':
- '1.Fix some crash.\n2.Fix Calculate Units error.\n3.Update dialog style.\n4.Fix home page slider.\n5.Add bus rule page.',
+ 'update_note_content': '1.Fix some crash.\n2.Add bus page cancel bus.',
'splash_content': '我們全都包了\n只剩下學校不包我們',
'share': 'Share',
'teacher_confirm_title': 'Are you a teacher?',
@@ -268,12 +267,12 @@ class AppLocalizations {
'load_offline_data': 'Load offline data',
'reserve_deadline': 'Reserve Deadline',
'bus_rule': 'Bus Rule',
+ 'platform_error': 'Current platform can\'t use this feature.',
},
'zh': {
'app_name': '高科校務通',
'update_note_title': '更新日誌',
- 'update_note_content':
- '1.修正部分崩潰.\n2.修正學分計算錯誤\n3.修改對話框風格\n4.修改首頁輪播\n5.新增校車規則說明',
+ 'update_note_content': '1.修正部分崩潰.\n2.新增校車預定頁面可取消預約',
'splash_content': '我們全都包了\n只剩下學校不包我們',
'share': '分享',
'teacher_confirm_title': '您是老師嗎?',
@@ -507,6 +506,7 @@ class AppLocalizations {
'load_offline_data': '載入離線資料',
'reserve_deadline': '預約截止時間',
'bus_rule': '校車搭乘規則',
+ 'platform_error': '此平台無法使用此功能',
},
};
@@ -905,6 +905,8 @@ class AppLocalizations {
String get offlineCourse => _vocabularies['offline_course'];
String get busRule => _vocabularies['bus_rule'];
+
+ String get platformError => _vocabularies['platform_error'];
}
class AppLocalizationsDelegate extends LocalizationsDelegate {
diff --git a/lib/utils/firebase_analytics_utils.dart b/lib/utils/firebase_analytics_utils.dart
index 0a8d66f9..cd73b32a 100644
--- a/lib/utils/firebase_analytics_utils.dart
+++ b/lib/utils/firebase_analytics_utils.dart
@@ -9,62 +9,84 @@ class FA {
static Future setCurrentScreen(
String screenName, String screenClassOverride) async {
- await analytics.setCurrentScreen(
- screenName: screenName,
- screenClassOverride: screenClassOverride,
- );
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.setCurrentScreen(
+ screenName: screenName,
+ screenClassOverride: screenClassOverride,
+ );
}
static Future setUserId(String id) async {
- await analytics.setUserId(id);
+ if (Platform.isIOS || Platform.isAndroid) await analytics.setUserId(id);
print('setUserId succeeded');
}
static Future setUserProperty(String name, String value) async {
- await analytics.setUserProperty(
- name: name,
- value: value,
- );
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.setUserProperty(
+ name: name,
+ value: value,
+ );
print('setUserProperty succeeded');
}
- static Future logApiEvent(String type, int status) async {
+ static Future logApiEvent(String type, int status,
+ {String message}) async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
- await analytics.logEvent(
- name: 'ap_api',
- parameters: {
- 'type': type,
- 'status': status,
- 'version': packageInfo.version,
- 'platform': Platform.operatingSystem,
- },
- );
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.logEvent(
+ name: 'ap_api',
+ parameters: {
+ 'type': type,
+ 'status': status,
+ 'message': message,
+ 'version': packageInfo.version,
+ 'platform': Platform.operatingSystem,
+ },
+ );
print('logEvent succeeded');
}
static Future logAESErrorEvent(String encryptPassword) async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
- await analytics.logEvent(
- name: 'aes_error',
- parameters: {
- 'type': encryptPassword,
- 'version': packageInfo.version,
- 'platform': Platform.operatingSystem,
- },
- );
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.logEvent(
+ name: 'aes_error',
+ parameters: {
+ 'encryptPassword': encryptPassword,
+ 'version': packageInfo.version,
+ 'platform': Platform.operatingSystem,
+ },
+ );
print('log encryptPassword succeeded');
}
static Future logCalculateUnits(double seconds) async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
- await analytics.logEvent(
- name: 'calculate_units_time',
- parameters: {
- 'time': seconds,
- 'version': packageInfo.version,
- 'platform': Platform.operatingSystem,
- },
- );
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.logEvent(
+ name: 'calculate_units_time',
+ parameters: {
+ 'time': seconds,
+ 'version': packageInfo.version,
+ 'platform': Platform.operatingSystem,
+ },
+ );
print('log CalculateUnits succeeded');
}
+
+ static Future logAction(String name, String action,
+ {String message}) async {
+ PackageInfo packageInfo = await PackageInfo.fromPlatform();
+ if (Platform.isIOS || Platform.isAndroid)
+ await analytics.logEvent(
+ name: name,
+ parameters: {
+ 'action': action,
+ 'message': message,
+ 'version': packageInfo.version,
+ 'platform': Platform.operatingSystem,
+ },
+ );
+ }
}
diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart
index eefdc00d..e75f9ed1 100644
--- a/lib/utils/utils.dart
+++ b/lib/utils/utils.dart
@@ -36,14 +36,15 @@ class Utils {
static void handleResponseError(
BuildContext context, String type, bool mounted, DioError e) {
var app = AppLocalizations.of(context);
- FA.logApiEvent(type, e.response.statusCode);
+ FA.logApiEvent(type, e.response.statusCode, message: e.message);
if (e.response.statusCode == 401) {
Utils.showToast(app.tokenExpiredContent);
if (mounted)
Navigator.popUntil(
context, ModalRoute.withName(Navigator.defaultRouteName));
- } else
+ } else {
Utils.showToast(app.somethingError);
+ }
}
static void showToast(String message) {
diff --git a/lib/widgets/drawer_body.dart b/lib/widgets/drawer_body.dart
index 271f4c6d..57cb3f5d 100644
--- a/lib/widgets/drawer_body.dart
+++ b/lib/widgets/drawer_body.dart
@@ -95,7 +95,11 @@ class DrawerBodyState extends State {
image: new DecorationImage(
fit: BoxFit.fitWidth,
image: CachedNetworkImageProvider(
- pictureUrl),
+ pictureUrl,
+ errorListener: () {
+ print('error');
+ },
+ ),
),
),
),
diff --git a/pubspec.yaml b/pubspec.yaml
index d107be22..50e9a71b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: nkust_ap
description: A new Flutter application.
-version: 3.1.2+30102
+version: 3.1.3+30103
environment:
sdk: ">=2.1.0 <3.0.0"