diff --git a/.gitignore b/.gitignore index 4048ad8..f43ff35 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# gitignore contributors: remember to update .npmignore + # OSX # .DS_Store @@ -152,4 +154,38 @@ captures/ code-push-plugin-testing-framework/node_modules # RN New Version App Generation -Examples/testapp_rn \ No newline at end of file +Examples/testapp_rn + +# Windows +windows/.vs/ +windows/obj/ + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.opendb +*.unsuccessfulbuild +ipch/ +[Oo]bj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad \ No newline at end of file diff --git a/.npmignore b/.npmignore index 83a811d..a957854 100644 --- a/.npmignore +++ b/.npmignore @@ -43,3 +43,45 @@ android/local.properties android/.gradle android/**/*.iml android/.idea + + +# Windows +windows/.vs/ +windows/obj/ + +#Tests +windows/CodePush.Net46.Test + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.opendb +*.unsuccessfulbuild +ipch/ +[Oo]bj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad + +#NuGet +packages/ +*.nupkg diff --git a/Examples/CodePushDemoApp/NuGet.Config b/Examples/CodePushDemoApp/NuGet.Config new file mode 100644 index 0000000..73ee8d8 --- /dev/null +++ b/Examples/CodePushDemoApp/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/index.windows.js b/Examples/CodePushDemoApp/index.windows.js new file mode 100644 index 0000000..8f8f835 --- /dev/null +++ b/Examples/CodePushDemoApp/index.windows.js @@ -0,0 +1 @@ +require("./demo"); \ No newline at end of file diff --git a/Examples/CodePushDemoApp/package.json b/Examples/CodePushDemoApp/package.json index 8ab24cd..7532aca 100644 --- a/Examples/CodePushDemoApp/package.json +++ b/Examples/CodePushDemoApp/package.json @@ -9,6 +9,7 @@ "babel-preset-react-native-stage-0": "1.0.1", "react": "15.4.0", "react-native": "0.40.0", - "react-native-code-push": "file:../../" + "react-native-code-push": "file:../../", + "react-native-windows": "0.40.0-rc.1" } } diff --git a/Examples/CodePushDemoApp/windows/.gitignore b/Examples/CodePushDemoApp/windows/.gitignore new file mode 100644 index 0000000..33d3fde --- /dev/null +++ b/Examples/CodePushDemoApp/windows/.gitignore @@ -0,0 +1,89 @@ +*AppPackages* +*BundleArtifacts* +*ReactAssets* + +#OS junk files +[Tt]humbs.db +*.DS_Store + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.opendb +*.unsuccessfulbuild +ipch/ +[Oo]bj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +#MonoDevelop +*.pidb +*.userprefs + +#Tooling +_ReSharper*/ +*.resharper +[Tt]est[Rr]esult* +*.sass-cache + +#Project files +[Bb]uild/ + +#Subversion files +.svn + +# Office Temp Files +~$* + +# vim Temp Files +*~ + +#NuGet +packages/ +*.nupkg + +#ncrunch +*ncrunch* +*crunch*.local.xml + +# visual studio database projects +*.dbmdl + +#Test files +*.testsettings + +#Other files +*.DotSettings +.vs/ +*project.lock.json diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.config b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.config new file mode 100644 index 0000000..2d2a12d --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml new file mode 100644 index 0000000..07709dd --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml @@ -0,0 +1,5 @@ + + + diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml.cs new file mode 100644 index 0000000..8c5c4e4 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/App.xaml.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Navigation; + +namespace CodePushDemoApp.Wpf +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + private readonly AppReactPage _reactPage = new AppReactPage(); + + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + } + + /// + /// Override method fired prior to the Startup event when the Run method of the Application object is called... + /// + /// + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + OnCreate(e.Args); + } + + /// + /// Called whenever the app is opened to initialized... + /// + /// + private void OnCreate(string[] arguments) + { + var shellWindow = Application.Current.MainWindow; + + if (shellWindow == null) + { + shellWindow = new Window + { + ShowActivated = true, + ShowInTaskbar = true, + Title = "CodePushDemoApp.WPF", + Height = 768, + Width = 1024, + WindowStartupLocation = WindowStartupLocation.CenterScreen + }; + + Application.Current.MainWindow = shellWindow; + } + + //Show Window if it is not already active... + if (!shellWindow.IsLoaded) + { + shellWindow.Show(); + } + + var rootFrame = shellWindow.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + _reactPage.OnCreate(arguments); + + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + // Place the frame in the current Window + shellWindow.Content = rootFrame; + } + + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Content = _reactPage; + } + + // Ensure the current window is active + shellWindow.Activate(); + } + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page..."); + } + } +} + diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/AppReactPage.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/AppReactPage.cs new file mode 100644 index 0000000..4fea5bc --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/AppReactPage.cs @@ -0,0 +1,60 @@ +using CodePush.ReactNative; +using ReactNative; +using ReactNative.Modules.Core; +using ReactNative.Shell; +using System; +using System.Collections.Generic; + +namespace CodePushDemoApp.Wpf +{ + internal class AppReactPage : ReactPage + { + public override string MainComponentName + { + get + { + return "CodePushDemoApp"; + } + } + + private CodePushReactPackage codePushReactPackage = null; + public override string JavaScriptBundleFile + { + get + { + codePushReactPackage = new CodePushReactPackage("deployment-key-here", this); + +#if BUNDLE + return codePushReactPackage.GetJavaScriptBundleFile(); +#else + return null; +#endif + } + } + + + public override List Packages + { + get + { + return new List + { + new MainReactPackage(), + codePushReactPackage + }; + } + } + + public override bool UseDeveloperSupport + { + get + { +#if !BUNDLE || DEBUG + return true; +#else + return false; +#endif + } + } + } +} diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/CodePushDemoApp.Wpf.csproj b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/CodePushDemoApp.Wpf.csproj new file mode 100644 index 0000000..23dcba7 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/CodePushDemoApp.Wpf.csproj @@ -0,0 +1,184 @@ + + + + + + Debug + AnyCPU + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA} + WinExe + Properties + CodePushDemoApp.Wpf + CodePushDemoApp.Wpf + v4.6 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + + + + true + bin\x86\Debug\ + TRACE;DEBUG + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x86\DebugBundle\ + TRACE;DEBUG;BUNDLE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\ReleaseBundle\ + TRACE;BUNDLE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x64\Debug\ + TRACE;DEBUG + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x64\DebugBundle\ + TRACE;DEBUG;BUNDLE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\ReleaseBundle\ + TRACE;BUNDLE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + + False + ..\packages\Facebook.Yoga.1.0.1-pre\lib\netstandard\Facebook.Yoga.dll + True + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + {4dfe3f9f-5e15-4f17-8fd4-33ff0519348e} + CodePush.Net46 + + + {22cbff9c-fe36-43e8-a246-266c7635e662} + ReactNative.Net46 + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/AssemblyInfo.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1df7e6d --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodePushDemoApp.Wpf")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodePushDemoApp.Wpf")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.Designer.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.Designer.cs new file mode 100644 index 0000000..abab04e --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodePushDemoApp.Wpf.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodePushDemoApp.Wpf.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.resx b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.Designer.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.Designer.cs new file mode 100644 index 0000000..553c4b3 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodePushDemoApp.Wpf.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.settings b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/packages.config b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/packages.config new file mode 100644 index 0000000..683c938 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.Wpf/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp.sln b/Examples/CodePushDemoApp/windows/CodePushDemoApp.sln new file mode 100644 index 0000000..9f2ebd9 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp.sln @@ -0,0 +1,245 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodePushDemoApp", "CodePushDemoApp\CodePushDemoApp.csproj", "{3E8198A5-7E41-43F1-880E-E7E2840E5A59}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative", "..\node_modules\react-native-windows\ReactWindows\ReactNative\ReactNative.csproj", "{C7673AD5-E3AA-468C-A5FD-FA38154E205C}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ReactNative.Shared", "..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.shproj", "{EEA8B852-4D07-48E1-8294-A21AB5909FE5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChakraBridge", "..\node_modules\react-native-windows\ReactWindows\ChakraBridge\ChakraBridge.vcxproj", "{4B72C796-16D5-4E3A-81C0-3E36F531E578}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodePush", "..\..\..\windows\CodePush\CodePush.csproj", "{446A85D9-55EB-4C7D-8B9D-448306C833D6}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CodePush.Shared", "..\..\..\windows\CodePush.Shared\CodePush.Shared.shproj", "{4AD4C826-CC26-4F1D-B60D-B97B22F52E90}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative.Net46", "..\node_modules\react-native-windows\ReactWindows\ReactNative.Net46\ReactNative.Net46.csproj", "{22CBFF9C-FE36-43E8-A246-266C7635E662}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodePush.Net46", "..\..\..\windows\CodePush.Net46\CodePush.Net46.csproj", "{4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodePushDemoApp.Wpf", "CodePushDemoApp.Wpf\CodePushDemoApp.Wpf.csproj", "{96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodePush.Net46.Test", "..\..\..\windows\CodePush.Net46.Test\CodePush.Net46.Test.csproj", "{BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{22cbff9c-fe36-43e8-a246-266c7635e662}*SharedItemsImports = 4 + ..\..\..\windows\CodePush.Shared\CodePush.Shared.projitems*{446a85d9-55eb-4c7d-8b9d-448306c833d6}*SharedItemsImports = 4 + ..\..\..\windows\CodePush.Shared\CodePush.Shared.projitems*{4ad4c826-cc26-4f1d-b60d-b97b22f52e90}*SharedItemsImports = 13 + ..\..\..\windows\CodePush.Shared\CodePush.Shared.projitems*{4dfe3f9f-5e15-4f17-8fd4-33ff0519348e}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{eea8b852-4d07-48e1-8294-a21ab5909fe5}*SharedItemsImports = 13 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + DebugBundle|ARM = DebugBundle|ARM + DebugBundle|x64 = DebugBundle|x64 + DebugBundle|x86 = DebugBundle|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseBundle|ARM = ReleaseBundle|ARM + ReleaseBundle|x64 = ReleaseBundle|x64 + ReleaseBundle|x86 = ReleaseBundle|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|ARM.ActiveCfg = Debug|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|ARM.Build.0 = Debug|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|ARM.Deploy.0 = Debug|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x64.ActiveCfg = Debug|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x64.Build.0 = Debug|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x64.Deploy.0 = Debug|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x86.ActiveCfg = Debug|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x86.Build.0 = Debug|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Debug|x86.Deploy.0 = Debug|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|ARM.ActiveCfg = DebugBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|ARM.Build.0 = DebugBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|ARM.Deploy.0 = DebugBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x64.ActiveCfg = DebugBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x64.Build.0 = DebugBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x64.Deploy.0 = DebugBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x86.ActiveCfg = DebugBundle|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x86.Build.0 = DebugBundle|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.DebugBundle|x86.Deploy.0 = DebugBundle|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|ARM.ActiveCfg = Release|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|ARM.Build.0 = Release|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|ARM.Deploy.0 = Release|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x64.ActiveCfg = Release|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x64.Build.0 = Release|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x64.Deploy.0 = Release|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x86.ActiveCfg = Release|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x86.Build.0 = Release|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.Release|x86.Deploy.0 = Release|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|ARM.ActiveCfg = ReleaseBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|ARM.Build.0 = ReleaseBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|ARM.Deploy.0 = ReleaseBundle|ARM + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x64.ActiveCfg = ReleaseBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x64.Build.0 = ReleaseBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x64.Deploy.0 = ReleaseBundle|x64 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x86.ActiveCfg = ReleaseBundle|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x86.Build.0 = ReleaseBundle|x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59}.ReleaseBundle|x86.Deploy.0 = ReleaseBundle|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.ActiveCfg = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.Build.0 = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.ActiveCfg = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.Build.0 = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.ActiveCfg = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.Build.0 = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|ARM.ActiveCfg = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|ARM.Build.0 = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x64.ActiveCfg = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x64.Build.0 = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x86.ActiveCfg = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x86.Build.0 = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.ActiveCfg = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.Build.0 = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.ActiveCfg = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.Build.0 = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.ActiveCfg = Release|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.Build.0 = Release|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|ARM.ActiveCfg = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|ARM.Build.0 = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x64.Build.0 = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x86.ActiveCfg = Release|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x86.Build.0 = Release|x86 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.ActiveCfg = Debug|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.Build.0 = Debug|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.ActiveCfg = Debug|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.Build.0 = Debug|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.ActiveCfg = Debug|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.Build.0 = Debug|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|ARM.ActiveCfg = Debug|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|ARM.Build.0 = Debug|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x64.ActiveCfg = Debug|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x64.Build.0 = Debug|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x86.ActiveCfg = Debug|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x86.Build.0 = Debug|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.ActiveCfg = Release|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.Build.0 = Release|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.ActiveCfg = Release|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.Build.0 = Release|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.ActiveCfg = Release|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.Build.0 = Release|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|ARM.ActiveCfg = Release|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|ARM.Build.0 = Release|ARM + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x64.Build.0 = Release|x64 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x86.ActiveCfg = Release|Win32 + {4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x86.Build.0 = Release|Win32 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|ARM.ActiveCfg = Debug|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|ARM.Build.0 = Debug|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|x64.ActiveCfg = Debug|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|x64.Build.0 = Debug|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|x86.ActiveCfg = Debug|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Debug|x86.Build.0 = Debug|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|ARM.ActiveCfg = Debug|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|ARM.Build.0 = Debug|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|x64.ActiveCfg = Debug|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|x64.Build.0 = Debug|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|x86.ActiveCfg = Debug|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.DebugBundle|x86.Build.0 = Debug|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|ARM.ActiveCfg = Release|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|ARM.Build.0 = Release|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|x64.ActiveCfg = Release|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|x64.Build.0 = Release|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|x86.ActiveCfg = Release|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.Release|x86.Build.0 = Release|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|ARM.ActiveCfg = Release|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|ARM.Build.0 = Release|ARM + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|x64.Build.0 = Release|x64 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|x86.ActiveCfg = Release|x86 + {446A85D9-55EB-4C7D-8B9D-448306C833D6}.ReleaseBundle|x86.Build.0 = Release|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|ARM.ActiveCfg = Debug|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|ARM.Build.0 = Debug|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|x64.ActiveCfg = Debug|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|x64.Build.0 = Debug|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|x86.ActiveCfg = Debug|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Debug|x86.Build.0 = Debug|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|ARM.ActiveCfg = Debug|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|ARM.Build.0 = Debug|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|x64.ActiveCfg = Debug|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|x64.Build.0 = Debug|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|x86.ActiveCfg = Debug|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.DebugBundle|x86.Build.0 = Debug|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|ARM.ActiveCfg = Release|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|ARM.Build.0 = Release|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|x64.ActiveCfg = Release|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|x64.Build.0 = Release|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|x86.ActiveCfg = Release|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.Release|x86.Build.0 = Release|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|ARM.ActiveCfg = Release|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|ARM.Build.0 = Release|ARM + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|x64.Build.0 = Release|x64 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|x86.ActiveCfg = Release|x86 + {22CBFF9C-FE36-43E8-A246-266C7635E662}.ReleaseBundle|x86.Build.0 = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Debug|ARM.ActiveCfg = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Debug|x64.ActiveCfg = Debug|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Debug|x64.Build.0 = Debug|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Debug|x86.ActiveCfg = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Debug|x86.Build.0 = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.DebugBundle|ARM.ActiveCfg = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.DebugBundle|x64.ActiveCfg = Debug|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.DebugBundle|x64.Build.0 = Debug|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.DebugBundle|x86.ActiveCfg = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.DebugBundle|x86.Build.0 = Debug|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Release|ARM.ActiveCfg = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Release|x64.ActiveCfg = Release|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Release|x64.Build.0 = Release|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Release|x86.ActiveCfg = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.Release|x86.Build.0 = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.ReleaseBundle|ARM.ActiveCfg = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.ReleaseBundle|x64.Build.0 = Release|x64 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.ReleaseBundle|x86.ActiveCfg = Release|x86 + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E}.ReleaseBundle|x86.Build.0 = Release|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Debug|ARM.ActiveCfg = Debug|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Debug|x64.ActiveCfg = Debug|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Debug|x64.Build.0 = Debug|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Debug|x86.ActiveCfg = Debug|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Debug|x86.Build.0 = Debug|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.DebugBundle|ARM.ActiveCfg = Debug|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.DebugBundle|x64.ActiveCfg = DebugBundle|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.DebugBundle|x64.Build.0 = DebugBundle|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.DebugBundle|x86.ActiveCfg = DebugBundle|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.DebugBundle|x86.Build.0 = DebugBundle|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Release|ARM.ActiveCfg = Release|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Release|x64.ActiveCfg = Release|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Release|x64.Build.0 = Release|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Release|x86.ActiveCfg = Release|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.Release|x86.Build.0 = Release|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.ReleaseBundle|ARM.ActiveCfg = Release|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.ReleaseBundle|x64.ActiveCfg = ReleaseBundle|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.ReleaseBundle|x64.Build.0 = ReleaseBundle|x64 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.ReleaseBundle|x86.ActiveCfg = ReleaseBundle|x86 + {96E3B61A-F3CA-4297-97FA-5D45C2DBC2BA}.ReleaseBundle|x86.Build.0 = ReleaseBundle|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Debug|ARM.ActiveCfg = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Debug|x64.ActiveCfg = Debug|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Debug|x64.Build.0 = Debug|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Debug|x86.ActiveCfg = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Debug|x86.Build.0 = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.DebugBundle|ARM.ActiveCfg = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.DebugBundle|x64.ActiveCfg = Debug|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.DebugBundle|x64.Build.0 = Debug|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.DebugBundle|x86.ActiveCfg = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.DebugBundle|x86.Build.0 = Debug|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Release|ARM.ActiveCfg = Release|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Release|x64.ActiveCfg = Release|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Release|x64.Build.0 = Release|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Release|x86.ActiveCfg = Release|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.Release|x86.Build.0 = Release|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.ReleaseBundle|ARM.ActiveCfg = Release|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.ReleaseBundle|x64.ActiveCfg = Release|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.ReleaseBundle|x64.Build.0 = Release|x64 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.ReleaseBundle|x86.ActiveCfg = Release|x86 + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF}.ReleaseBundle|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml b/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml new file mode 100644 index 0000000..e47bd98 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml.cs new file mode 100644 index 0000000..5f024f3 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/App.xaml.cs @@ -0,0 +1,147 @@ +using ReactNative; +using ReactNative.Modules.Launch; +using System; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace CodePushDemoApp +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + sealed partial class App : Application + { + private readonly ReactPage _reactPage; + + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + this.Suspending += OnSuspending; + this.Resuming += OnResuming; + + _reactPage = new MainPage(); + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { + base.OnLaunched(e); + OnCreate(e.Arguments); + } + + /// + /// Invoked when the application is activated. + /// + /// The activated event arguments. + protected override void OnActivated(IActivatedEventArgs args) + { + base.OnActivated(args); + + switch (args.Kind) + { + case ActivationKind.Protocol: + case ActivationKind.ProtocolForResults: + var protocolArgs = (IProtocolActivatedEventArgs)args; + LauncherModule.SetActivatedUrl(protocolArgs.Uri.AbsoluteUri); + break; + } + + if (args.PreviousExecutionState != ApplicationExecutionState.Running && + args.PreviousExecutionState != ApplicationExecutionState.Suspended) + { + OnCreate(null); + } + } + + /// + /// Called whenever the app is opened to initia + /// + /// + private void OnCreate(string arguments) + { + _reactPage.OnResume(Exit); + +#if DEBUG + if (System.Diagnostics.Debugger.IsAttached) + { + this.DebugSettings.EnableFrameRateCounter = true; + } + + SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = + AppViewBackButtonVisibility.Visible; +#endif + + Frame rootFrame = Window.Current.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + _reactPage.OnCreate(arguments); + + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Content = _reactPage; + } + + // Ensure the current window is active + Window.Current.Activate(); + } + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + } + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + _reactPage.OnSuspend(); + } + + /// + /// Invoked when application execution is being resumed. + /// + /// The source of the resume request. + /// Details about the resume request. + private void OnResuming(object sender, object e) + { + _reactPage.OnResume(Exit); + } + } +} diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/LockScreenLogo.scale-200.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000..735f57a Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/LockScreenLogo.scale-200.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/SplashScreen.scale-200.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000..023e7f1 Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/SplashScreen.scale-200.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square150x150Logo.scale-200.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..af49fec Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square150x150Logo.scale-200.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.scale-200.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..ce342a2 Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.scale-200.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..f6c02ce Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/StoreLogo.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/StoreLogo.png new file mode 100644 index 0000000..7385b56 Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/StoreLogo.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Wide310x150Logo.scale-200.png b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..288995b Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Assets/Wide310x150Logo.scale-200.png differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.csproj b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.csproj new file mode 100644 index 0000000..d9b4b2f --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.csproj @@ -0,0 +1,230 @@ + + + + + Debug + x86 + {3E8198A5-7E41-43F1-880E-E7E2840E5A59} + AppContainerExe + Properties + CodePushDemoApp + CodePushDemoApp + en-US + UAP + 10.0.10586.0 + 10.0.10240.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + CodePushDemoApp_TemporaryKey.pfx + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + true + bin\x86\DebugBundle\ + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + ;2008 + true + full + x86 + false + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + true + + + bin\x86\ReleaseBundle\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + true + ;2008 + true + pdbonly + x86 + false + prompt + MinimumRecommendedRules.ruleset + true + true + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + true + bin\ARM\DebugBundle\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + ;2008 + true + full + ARM + false + prompt + MinimumRecommendedRules.ruleset + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + true + + + bin\ARM\ReleaseBundle\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + true + ;2008 + true + pdbonly + ARM + false + prompt + MinimumRecommendedRules.ruleset + true + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + + + true + bin\x64\DebugBundle\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + ;2008 + true + full + x64 + false + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + true + + + bin\x64\ReleaseBundle\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE + true + ;2008 + true + pdbonly + x64 + false + prompt + MinimumRecommendedRules.ruleset + true + true + + + + + + + + App.xaml + + + + + + + Designer + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + + + {446a85d9-55eb-4c7d-8b9d-448306c833d6} + CodePush + + + {c7673ad5-e3aa-468c-a5fd-fa38154e205c} + ReactNative + + + + + PreserveNewest + + + + 14.0 + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.nuget.targets b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.nuget.targets new file mode 100644 index 0000000..ff4a29f --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp.nuget.targets @@ -0,0 +1,9 @@ + + + + $(UserProfile)\.nuget\packages\ + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp_TemporaryKey.pfx b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp_TemporaryKey.pfx new file mode 100644 index 0000000..08328fc Binary files /dev/null and b/Examples/CodePushDemoApp/windows/CodePushDemoApp/CodePushDemoApp_TemporaryKey.pfx differ diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/MainPage.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp/MainPage.cs new file mode 100644 index 0000000..030d764 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/MainPage.cs @@ -0,0 +1,62 @@ +using CodePush.ReactNative; +using ReactNative; +using ReactNative.Modules.Core; +using ReactNative.Shell; +using System.Collections.Generic; + +namespace CodePushDemoApp +{ + class MainPage : ReactPage + { + + public override string MainComponentName + { + get + { + return "CodePushDemoApp"; + } + } + + private CodePushReactPackage codePushReactPackage = null; + public override string JavaScriptBundleFile + { + get + { + + codePushReactPackage = new CodePushReactPackage("deployment-key-here", this); + +#if BUNDLE + return codePushReactPackage.GetJavaScriptBundleFile(); +#else + return null; +#endif + } + } + + + public override List Packages + { + get + { + return new List + { + new MainReactPackage(), + codePushReactPackage + }; + } + } + + public override bool UseDeveloperSupport + { + get + { +#if !BUNDLE || DEBUG + return true; +#else + return false; +#endif + } + } + } + +} diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Package.appxmanifest b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Package.appxmanifest new file mode 100644 index 0000000..1643e92 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + + + CodePushDemoApp + React Native for UWP + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/AssemblyInfo.cs b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a70a7f9 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodePushDemoApp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodePushDemoApp")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/Default.rd.xml b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/Default.rd.xml new file mode 100644 index 0000000..a43bcd5 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/CodePushDemoApp/project.json b/Examples/CodePushDemoApp/windows/CodePushDemoApp/project.json new file mode 100644 index 0000000..ba45017 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/CodePushDemoApp/project.json @@ -0,0 +1,17 @@ +{ + "dependencies": { + "Facebook.Yoga": "1.0.1-pre", + "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2" + }, + "frameworks": { + "uap10.0": {} + }, + "runtimes": { + "win10-arm": {}, + "win10-arm-aot": {}, + "win10-x86": {}, + "win10-x86-aot": {}, + "win10-x64": {}, + "win10-x64-aot": {} + } +} \ No newline at end of file diff --git a/Examples/CodePushDemoApp/windows/nuget.config b/Examples/CodePushDemoApp/windows/nuget.config new file mode 100644 index 0000000..724f227 --- /dev/null +++ b/Examples/CodePushDemoApp/windows/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index f7fb4c1..519bd73 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,14 @@ "code-push-plugin-testing-framework": "file:./code-push-plugin-testing-framework", "del": "latest", "express": "latest", - "gulp-typescript": "2.12.2", "gulp-insert": "latest", "gulp-tslint": "latest", + "gulp-typescript": "2.12.2", "mkdirp": "latest", "q": "^1.4.1", - "run-sequence": "latest" + "run-sequence": "latest", + "tslint": "^4.3.1", + "typescript": "^2.1.5" }, "rnpm": { "android": { diff --git a/windows/CodePush.Net46.Test/ApplicationDataContainerTest.cs b/windows/CodePush.Net46.Test/ApplicationDataContainerTest.cs new file mode 100644 index 0000000..98ae82a --- /dev/null +++ b/windows/CodePush.Net46.Test/ApplicationDataContainerTest.cs @@ -0,0 +1,105 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using CodePush.Net46.Adapters.Storage; +using System.Threading.Tasks; + +namespace CodePush.Net46.Test +{ + [TestClass] + public class ApplicationDataContainerTest + { + readonly static string key1 = "key1"; + readonly static string key2 = "key2"; + readonly static string key3 = "key3"; + + readonly static string val1 = "string data1"; + readonly static string val2 = "string data2"; + readonly static string val3 = "string data1"; + + [TestMethod] + public void TestInMemmorySet() + { + var settings = new ApplicationDataContainer(); + + settings.Values[key1] = val1; + settings.Values[key2] = val2; + settings.Values[key3] = val3; + + Assert.AreEqual(settings.Values[key1], val1); + Assert.AreEqual(settings.Values[key2], val2); + Assert.AreEqual(settings.Values[key3], val3); + + settings.DeleteAsync().Wait(); + } + + [TestMethod] + public void TestInMemmoryReset() + { + var settings = new ApplicationDataContainer(); + + settings.Values[key1] = val1; + settings.Values[key1] = val2; + settings.Values[key1] = val3; + + Assert.AreEqual(settings.Values[key1], val3); + + settings.DeleteAsync().Wait(); + } + + [TestMethod] + public void TestInMemmoryRemove() + { + var settings = new ApplicationDataContainer(); + + settings.Values[key1] = val1; + settings.Values[key2] = val2; + settings.Values[key3] = val3; + + settings.Values.Remove(key2); + + Assert.AreEqual(settings.Values[key1], val1); + Assert.IsNull(settings.Values[key2]); + Assert.AreEqual(settings.Values[key3], val3); + + settings.DeleteAsync().Wait(); + } + + [TestMethod] + public void TestPersistentSet() + { + var settings = new ApplicationDataContainer(); + + settings.Values[key1] = val1; + settings.Values[key2] = val2; + settings.Values[key3] = val3; + + settings = new ApplicationDataContainer(); + + Assert.AreEqual(settings.Values[key1], val1); + Assert.AreEqual(settings.Values[key2], val2); + Assert.AreEqual(settings.Values[key3], val3); + + settings.DeleteAsync().Wait(); + } + + [TestMethod] + public void TestPersistentRemove() + { + var settings = new ApplicationDataContainer(); + + settings.Values[key1] = val1; + settings.Values[key2] = val2; + settings.Values[key3] = val3; + + settings.Values.Remove(key2); + + settings = new ApplicationDataContainer(); + + Assert.AreEqual(settings.Values[key1], val1); + Assert.IsNull(settings.Values[key2]); + Assert.AreEqual(settings.Values[key3], val3); + + settings.DeleteAsync().Wait(); + } + } +} diff --git a/windows/CodePush.Net46.Test/CodePush.Net46.Test.csproj b/windows/CodePush.Net46.Test/CodePush.Net46.Test.csproj new file mode 100644 index 0000000..8366f05 --- /dev/null +++ b/windows/CodePush.Net46.Test/CodePush.Net46.Test.csproj @@ -0,0 +1,125 @@ + + + + Debug + AnyCPU + {BBB48F0B-AF6F-4A14-AFA4-306D3FB0B7CF} + Library + Properties + CodePush.Net46.Test + CodePush.Net46.Test + v4.6 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + {4dfe3f9f-5e15-4f17-8fd4-33ff0519348e} + CodePush.Net46 + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/windows/CodePush.Net46.Test/Properties/AssemblyInfo.cs b/windows/CodePush.Net46.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..53a3a9b --- /dev/null +++ b/windows/CodePush.Net46.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodePush.Net46.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodePush.Net46.Test")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bbb48f0b-af6f-4a14-afa4-306d3fb0b7cf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/windows/.gitignore b/windows/CodePush.Net46/.gitignore similarity index 100% rename from windows/.gitignore rename to windows/CodePush.Net46/.gitignore diff --git a/windows/CodePush.Net46/Adapters/Http/HttpProgress.cs b/windows/CodePush.Net46/Adapters/Http/HttpProgress.cs new file mode 100644 index 0000000..c76cea1 --- /dev/null +++ b/windows/CodePush.Net46/Adapters/Http/HttpProgress.cs @@ -0,0 +1,28 @@ +using System; + +namespace CodePush.Net46.Adapters.Http +{ + public enum HttpProgressStage + { + None = 0, + DetectingProxy = 10, + ResolvingName = 20, + ConnectingToServer = 30, + NegotiatingSsl = 40, + SendingHeaders = 50, + SendingContent = 60, + WaitingForResponse = 70, + ReceivingHeaders = 80, + ReceivingContent = 90 + } + + public struct HttpProgress + { + public UInt64 BytesReceived; + public UInt64 BytesSent; + public UInt32 Retries; + public HttpProgressStage Stage; + public UInt64? TotalBytesToReceive; + public UInt64? TotalBytesToSend; + } +} diff --git a/windows/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs b/windows/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs new file mode 100644 index 0000000..338bdae --- /dev/null +++ b/windows/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs @@ -0,0 +1,103 @@ +using CodePush.ReactNative; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PCLStorage; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace CodePush.Net46.Adapters.Storage +{ + public enum ApplicationDataCreateDisposition + { + Always = 0, + Existing = 1 + } + + public class DictionaryWithDefault : Dictionary + { + TValue _default; + public TValue DefaultValue + { + get { return _default; } + set { _default = value; } + } + public DictionaryWithDefault() : base() { } + public DictionaryWithDefault(TValue defaultValue) : base() + { + _default = defaultValue; + } + public new TValue this[TKey key] + { + get + { + TValue t; + return base.TryGetValue(key, out t) ? t : _default; + } + set + { + base[key] = value; + DataChanged(this, null); + } + } + + public bool Remove(TKey key) + { + var found = base.Remove(key); + if (found) + DataChanged(this, null); + + return found; + } + + public event EventHandler DataChanged; + + } + + // A naive implementation of Windows.Storage.ApplicationDataContainer + public class ApplicationDataContainer + { + public DictionaryWithDefault Values; + private readonly SemaphoreSlim mutex = new SemaphoreSlim(1, 1); + + const string STORAGE_NAME = "AppStorage.data"; + IFile storageFile = null; + + public ApplicationDataContainer(string name = STORAGE_NAME) + { + storageFile = FileSystem.Current.LocalStorage.CreateFileAsync(name, CreationCollisionOption.OpenIfExists).Result; + var data = CodePushUtils.GetJObjectFromFileAsync(storageFile).Result; + + if (data != null) + { + Values = data.ToObject>(); + } + else + { + Values = new DictionaryWithDefault(); + } + + Values.DataChanged += async (s, e) => await SaveAsync(); + } + + ~ApplicationDataContainer() + { + mutex.Dispose(); + } + + async Task SaveAsync() + { + await mutex.WaitAsync().ConfigureAwait(false); + var jobject = JObject.FromObject(Values); + await storageFile.WriteAllTextAsync(JsonConvert.SerializeObject(jobject)).ConfigureAwait(false); + mutex.Release(); + } + + public async Task DeleteAsync() + { + Values.Clear(); + await storageFile.DeleteAsync(); + } + } +} diff --git a/windows/CodePush.Net46/CodePush.Net46.csproj b/windows/CodePush.Net46/CodePush.Net46.csproj new file mode 100644 index 0000000..3d0a537 --- /dev/null +++ b/windows/CodePush.Net46/CodePush.Net46.csproj @@ -0,0 +1,104 @@ + + + + + Debug + AnyCPU + {4DFE3F9F-5E15-4F17-8FD4-33FF0519348E} + Library + Properties + CodePush.Net46 + CodePush.Net46 + v4.6 + 512 + + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + $(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + False + $(SolutionDir)\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.dll + True + + + False + $(SolutionDir)\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.Abstractions.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + {22cbff9c-fe36-43e8-a246-266c7635e662} + ReactNative.Net46 + + + + + + + \ No newline at end of file diff --git a/windows/CodePush.Net46/CodePushUtils.cs b/windows/CodePush.Net46/CodePushUtils.cs new file mode 100644 index 0000000..c49589b --- /dev/null +++ b/windows/CodePush.Net46/CodePushUtils.cs @@ -0,0 +1,53 @@ +using Newtonsoft.Json.Linq; +using PCLStorage; +using System; +using System.Management; +using System.Threading.Tasks; + +namespace CodePush.ReactNative +{ + internal partial class CodePushUtils + { + internal async static Task GetJObjectFromFileAsync(IFile file) + { + string jsonString = await file.ReadAllTextAsync().ConfigureAwait(false); + if (jsonString.Length == 0) + { + return new JObject(); + } + + try + { + return JObject.Parse(jsonString); + } + catch (Exception) + { + return null; + } + } + + static string GetDeviceIdImpl() + { + ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_BaseBoard"); + ManagementObjectCollection moc = mos.Get(); + string mbId = String.Empty; + foreach (ManagementObject mo in moc) + { + mbId = (string)mo["SerialNumber"]; + break; + } + + ManagementObjectCollection mbsList = null; + ManagementObjectSearcher mbs = new ManagementObjectSearcher("Select * From Win32_processor"); + mbsList = mbs.Get(); + string procId = string.Empty; + foreach (ManagementObject mo in mbsList) + { + procId = mo["ProcessorID"].ToString(); + break; + } + + return procId + "-" + mbId; + } + } +} diff --git a/windows/CodePush.Net46/FileUtils.cs b/windows/CodePush.Net46/FileUtils.cs new file mode 100644 index 0000000..428802d --- /dev/null +++ b/windows/CodePush.Net46/FileUtils.cs @@ -0,0 +1,55 @@ +using PCLStorage; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace CodePush.ReactNative +{ + internal class FileUtils + { + internal async static Task MergeFoldersAsync(IFolder source, IFolder target) + { + foreach (IFile sourceFile in await source.GetFilesAsync().ConfigureAwait(false)) + { + await CopyFileAsync(sourceFile.Path, Path.Combine(target.Path, sourceFile.Name)).ConfigureAwait(false); + } + + foreach (IFolder sourceDirectory in await source.GetFoldersAsync().ConfigureAwait(false)) + { + var nextTargetSubDir = await target.CreateFolderAsync(sourceDirectory.Name, CreationCollisionOption.OpenIfExists).ConfigureAwait(false); + await MergeFoldersAsync(sourceDirectory, nextTargetSubDir).ConfigureAwait(false); + } + } + + internal async static Task ClearReactDevBundleCacheAsync() + { + + if (await FileSystem.Current.LocalStorage.CheckExistsAsync(CodePushConstants.ReactDevBundleCacheFileName).ConfigureAwait(false) != ExistenceCheckResult.FileExists) + return; + + var devBundleCacheFile = await FileSystem.Current.LocalStorage.GetFileAsync(CodePushConstants.ReactDevBundleCacheFileName).ConfigureAwait(false); + await devBundleCacheFile.DeleteAsync().ConfigureAwait(false); + } + + internal static Task GetBinaryResourcesModifiedTimeAsync(string fileName) + { + var pathToAssembly = CodePushUtils.GetAppFolder(); + var pathToAssemblyResource = Path.Combine(pathToAssembly, CodePushConstants.AssetsBundlePrefix, fileName); + var lastUpdateTime = File.GetCreationTime(pathToAssemblyResource); + + return Task.FromResult(new DateTimeOffset(lastUpdateTime).ToUnixTimeMilliseconds()); + } + + internal async static Task CopyFileAsync(string sourcePath, string destinationPath) + { + using (var source = File.Open(sourcePath, FileMode.Open, System.IO.FileAccess.Read)) + { + using (var destination = File.Create(destinationPath)) // Replace if exists + { + await source.CopyToAsync(destination); + } + } + } + + } +} diff --git a/windows/CodePush.Net46/Properties/AssemblyInfo.cs b/windows/CodePush.Net46/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e399db8 --- /dev/null +++ b/windows/CodePush.Net46/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodePush.Net46")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodePush.Net46")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4dfe3f9f-5e15-4f17-8fd4-33ff0519348e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/windows/CodePush.Net46/UpdateManager.cs b/windows/CodePush.Net46/UpdateManager.cs new file mode 100644 index 0000000..d48128c --- /dev/null +++ b/windows/CodePush.Net46/UpdateManager.cs @@ -0,0 +1,313 @@ +using CodePush.Net46.Adapters.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PCLStorage; +using System; +using System.IO; +using System.IO.Compression; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + + +[assembly: InternalsVisibleTo("CodePush.Net46.UnitTest")] + +namespace CodePush.ReactNative +{ + internal class UpdateManager + { + #region Internal methods + + internal async Task ClearUpdatesAsync() + { + await (await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false)).DeleteAsync().ConfigureAwait(false); + } + + internal async Task DownloadPackageAsync(JObject updatePackage, string expectedBundleFileName, Progress downloadProgress) + { + // Using its hash, get the folder where the new update will be saved + var codePushFolder = await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false); + var newUpdateHash = (string)updatePackage[CodePushConstants.PackageHashKey]; + var newUpdateFolder = await GetPackageFolderAsync(newUpdateHash, false).ConfigureAwait(false); + if (newUpdateFolder != null) + { + // This removes any stale data in newUpdateFolder that could have been left + // uncleared due to a crash or error during the download or install process. + await newUpdateFolder.DeleteAsync().ConfigureAwait(false); + } + + newUpdateFolder = await GetPackageFolderAsync(newUpdateHash, true).ConfigureAwait(false); + var newUpdateMetadataFile = await newUpdateFolder.CreateFileAsync(CodePushConstants.PackageFileName, CreationCollisionOption.ReplaceExisting).ConfigureAwait(false); + var downloadUrlString = (string)updatePackage[CodePushConstants.DownloadUrlKey]; + var downloadFile = await GetDownloadFileAsync().ConfigureAwait(false); + + await UpdateUtils.DownloadBundleAsync(downloadUrlString, downloadFile.Path, downloadProgress); + + try + { + // Unzip the downloaded file and then delete the zip + var unzippedFolder = await GetUnzippedFolderAsync().ConfigureAwait(false); + /** + * TODO: + * 1) ZipFile.ExtractToDirectory is not reliable and throws exception if: + * - folder exists already + * - path is too long (> 250 chars) + * + * 2) Un-zipping is quite long operation. Does it make sense for async? + * await UpdateUtils.UnzipBundleAsync(downloadFile.Path, unzippedFolder.Path); + * + * Possible implementation + * + * internal async static Task UnzipBundleAsync(string zipFileName, string targetDir) + * { + * await Task.Run(() => + * { + * ZipFile.ExtractToDirectory(zipFileName, targetDir) + * return Task.CompletedTask; + * }); + * } + */ + ZipFile.ExtractToDirectory(downloadFile.Path, unzippedFolder.Path); + await downloadFile.DeleteAsync().ConfigureAwait(false); + + // Merge contents with current update based on the manifest + IFile diffManifestFile = null; + try + { + diffManifestFile = await unzippedFolder.GetFileAsync(CodePushConstants.DiffManifestFileName).ConfigureAwait(false); + } + catch (FileNotFoundException) + { + //file may not be present in folder just skip it + } + if (diffManifestFile != null) + { + var currentPackageFolder = await GetCurrentPackageFolderAsync().ConfigureAwait(false); + if (currentPackageFolder == null) + { + throw new InvalidDataException("Received a diff update, but there is no current version to diff against."); + } + + await UpdateUtils.CopyNecessaryFilesFromCurrentPackageAsync(diffManifestFile, currentPackageFolder, newUpdateFolder).ConfigureAwait(false); + await diffManifestFile.DeleteAsync().ConfigureAwait(false); + } + + await FileUtils.MergeFoldersAsync(unzippedFolder, newUpdateFolder).ConfigureAwait(false); + await unzippedFolder.DeleteAsync().ConfigureAwait(false); + + // For zip updates, we need to find the relative path to the jsBundle and save it in the + // metadata so that we can find and run it easily the next time. + var relativeBundlePath = await UpdateUtils.FindJSBundleInUpdateContentsAsync(newUpdateFolder, expectedBundleFileName).ConfigureAwait(false); + if (relativeBundlePath == null) + { + throw new InvalidDataException("Update is invalid - A JS bundle file named \"" + expectedBundleFileName + "\" could not be found within the downloaded contents. Please check that you are releasing your CodePush updates using the exact same JS bundle file name that was shipped with your app's binary."); + } + else + { + if (diffManifestFile != null) + { + // TODO verify hash for diff update + // CodePushUpdateUtils.verifyHashForDiffUpdate(newUpdateFolderPath, newUpdateHash); + } + + updatePackage[CodePushConstants.RelativeBundlePathKey] = relativeBundlePath; + } + } + catch (InvalidDataException) + { + // Downloaded file is not a zip, assume it is a jsbundle + await downloadFile.RenameAsync(expectedBundleFileName).ConfigureAwait(false); + await downloadFile.MoveAsync(newUpdateFolder.Path, NameCollisionOption.ReplaceExisting).ConfigureAwait(false); + } + + // Save metadata to the folder + await newUpdateMetadataFile.WriteAllTextAsync(JsonConvert.SerializeObject(updatePackage)).ConfigureAwait(false); + } + + internal async Task GetCurrentPackageAsync() + { + var packageHash = await GetCurrentPackageHashAsync().ConfigureAwait(false); + return packageHash == null ? null : await GetPackageAsync(packageHash).ConfigureAwait(false); + } + + internal async Task GetCurrentPackageBundleAsync(string bundleFileName) + { + var packageFolder = await GetCurrentPackageFolderAsync().ConfigureAwait(false); + if (packageFolder == null) + { + return null; + } + + var currentPackage = await GetCurrentPackageAsync().ConfigureAwait(false); + var relativeBundlePath = (string)currentPackage[CodePushConstants.RelativeBundlePathKey]; + + return relativeBundlePath == null + ? await packageFolder.GetFileAsync(bundleFileName).ConfigureAwait(false) + : await packageFolder.GetFileAsync(relativeBundlePath).ConfigureAwait(false); + } + + internal async Task GetCurrentPackageHashAsync() + { + var info = await GetCurrentPackageInfoAsync().ConfigureAwait(false); + var currentPackageShortHash = (string)info[CodePushConstants.CurrentPackageKey]; + if (currentPackageShortHash == null) + { + return null; + } + + var currentPackageMetadata = await GetPackageAsync(currentPackageShortHash).ConfigureAwait(false); + return currentPackageMetadata == null ? null : (string)currentPackageMetadata[CodePushConstants.PackageHashKey]; + } + + internal async Task GetPackageAsync(string packageHash) + { + var packageFolder = await GetPackageFolderAsync(packageHash, false).ConfigureAwait(false); + if (packageFolder == null) + { + return null; + } + + try + { + var packageFile = await packageFolder.GetFileAsync(CodePushConstants.PackageFileName).ConfigureAwait(false); + return await CodePushUtils.GetJObjectFromFileAsync(packageFile).ConfigureAwait(false); + } + catch (IOException) + { + return null; + } + } + + internal async Task GetPackageFolderAsync(string packageHash, bool createIfNotExists) + { + var codePushFolder = await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false); + try + { + packageHash = ShortenPackageHash(packageHash); + return createIfNotExists + ? await codePushFolder.CreateFolderAsync(packageHash, CreationCollisionOption.OpenIfExists).ConfigureAwait(false) + : await codePushFolder.GetFolderAsync(packageHash).ConfigureAwait(false); + } + catch (FileNotFoundException) + { + return null; + } + catch (DirectoryNotFoundException) + { + return null; + } + } + + internal async Task GetPreviousPackageAsync() + { + var packageHash = await GetPreviousPackageHashAsync().ConfigureAwait(false); + return packageHash == null ? null : await GetPackageAsync(packageHash).ConfigureAwait(false); + } + + internal async Task GetPreviousPackageHashAsync() + { + var info = await GetCurrentPackageInfoAsync().ConfigureAwait(false); + var previousPackageShortHash = (string)info[CodePushConstants.PreviousPackageKey]; + if (previousPackageShortHash == null) + { + return null; + } + + var previousPackageMetadata = await GetPackageAsync(previousPackageShortHash).ConfigureAwait(false); + return previousPackageMetadata == null ? null : (string)previousPackageMetadata[CodePushConstants.PackageHashKey]; + } + + internal async Task InstallPackageAsync(JObject updatePackage, bool currentUpdateIsPending) + { + var packageHash = (string)updatePackage[CodePushConstants.PackageHashKey]; + var info = await GetCurrentPackageInfoAsync().ConfigureAwait(false); + if (currentUpdateIsPending) + { + // Don't back up current update to the "previous" position because + // it is an unverified update which should not be rolled back to. + var currentPackageFolder = await GetCurrentPackageFolderAsync().ConfigureAwait(false); + if (currentPackageFolder != null) + { + await currentPackageFolder.DeleteAsync().ConfigureAwait(false); + } + } + else + { + var previousPackageHash = await GetPreviousPackageHashAsync().ConfigureAwait(false); + if (previousPackageHash != null && !previousPackageHash.Equals(packageHash)) + { + var previousPackageFolder = await GetPackageFolderAsync(previousPackageHash, false).ConfigureAwait(false); + if (previousPackageFolder != null) + { + await previousPackageFolder.DeleteAsync().ConfigureAwait(false); + } + } + + info[CodePushConstants.PreviousPackageKey] = info[CodePushConstants.CurrentPackageKey]; + } + + info[CodePushConstants.CurrentPackageKey] = packageHash; + await UpdateCurrentPackageInfoAsync(info).ConfigureAwait(false); + } + + internal async Task RollbackPackageAsync() + { + var info = await GetCurrentPackageInfoAsync().ConfigureAwait(false); + var currentPackageFolder = await GetCurrentPackageFolderAsync().ConfigureAwait(false); + if (currentPackageFolder != null) + { + await currentPackageFolder.DeleteAsync().ConfigureAwait(false); + } + + info[CodePushConstants.CurrentPackageKey] = info[CodePushConstants.PreviousPackageKey]; + info[CodePushConstants.PreviousPackageKey] = null; + await UpdateCurrentPackageInfoAsync(info).ConfigureAwait(false); + } + + #endregion + + #region Private methods + + private async Task GetCurrentPackageFolderAsync() + { + var info = await GetCurrentPackageInfoAsync().ConfigureAwait(false); + var packageHash = (string)info[CodePushConstants.CurrentPackageKey]; + return packageHash == null ? null : await GetPackageFolderAsync(packageHash, false).ConfigureAwait(false); + } + + private async Task GetCurrentPackageInfoAsync() + { + var statusFile = await GetStatusFileAsync().ConfigureAwait(false); + return await CodePushUtils.GetJObjectFromFileAsync(statusFile).ConfigureAwait(false); + } + + private async Task GetDownloadFileAsync() + { + var codePushFolder = await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false); + return await codePushFolder.CreateFileAsync(CodePushConstants.DownloadFileName, CreationCollisionOption.OpenIfExists).ConfigureAwait(false); + } + + private async Task GetStatusFileAsync() + { + var codePushFolder = await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false); + return await codePushFolder.CreateFileAsync(CodePushConstants.StatusFileName, CreationCollisionOption.OpenIfExists).ConfigureAwait(false); + } + + private async Task GetUnzippedFolderAsync() + { + var codePushFolder = await UpdateUtils.GetCodePushFolderAsync().ConfigureAwait(false); + return await codePushFolder.CreateFolderAsync(CodePushConstants.UnzippedFolderName, CreationCollisionOption.OpenIfExists).ConfigureAwait(false); + } + + private string ShortenPackageHash(string longPackageHash) + { + return longPackageHash.Substring(0, 8); + } + + private async Task UpdateCurrentPackageInfoAsync(JObject packageInfo) + { + var file = await GetStatusFileAsync().ConfigureAwait(false); + await file.WriteAllTextAsync(JsonConvert.SerializeObject(packageInfo)).ConfigureAwait(false); + } + #endregion + } +} diff --git a/windows/CodePush.Net46/UpdateUtils.cs b/windows/CodePush.Net46/UpdateUtils.cs new file mode 100644 index 0000000..c7e5ad0 --- /dev/null +++ b/windows/CodePush.Net46/UpdateUtils.cs @@ -0,0 +1,70 @@ +using CodePush.Net46.Adapters.Http; +using Newtonsoft.Json.Linq; +using PCLStorage; +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; + +namespace CodePush.ReactNative +{ + internal class UpdateUtils + { + internal async static Task CopyNecessaryFilesFromCurrentPackageAsync(IFile diffManifestFile, IFolder currentPackageFolder, IFolder newPackageFolder) + { + await FileUtils.MergeFoldersAsync(currentPackageFolder, newPackageFolder).ConfigureAwait(false); + JObject diffManifest = await CodePushUtils.GetJObjectFromFileAsync(diffManifestFile).ConfigureAwait(false); + var deletedFiles = (JArray)diffManifest["deletedFiles"]; + foreach (string fileNameToDelete in deletedFiles) + { + var fileToDelete = await newPackageFolder.GetFileAsync(fileNameToDelete).ConfigureAwait(false); + await fileToDelete.DeleteAsync().ConfigureAwait(false); + } + } + + internal async static Task FindJSBundleInUpdateContentsAsync(IFolder updateFolder, string expectedFileName) + { + foreach (IFile file in await updateFolder.GetFilesAsync().ConfigureAwait(false)) + { + string fileName = file.Name; + if (fileName.Equals(expectedFileName)) + { + return fileName; + } + } + + foreach (IFolder folder in await updateFolder.GetFoldersAsync().ConfigureAwait(false)) + { + string mainBundlePathInSubFolder = await FindJSBundleInUpdateContentsAsync(folder, expectedFileName).ConfigureAwait(false); + if (mainBundlePathInSubFolder != null) + { + return Path.Combine(folder.Name, mainBundlePathInSubFolder); + } + } + + return null; + } + + internal async static Task DownloadBundleAsync(string url, string fileName, IProgress downloadProgress) + { + var uri = new Uri(url); + var client = new WebClient(); + client.DownloadProgressChanged += (s, e) => + { + downloadProgress.Report(new HttpProgress + { + BytesReceived = (ulong)e.BytesReceived, //conversion long to ulong is safe + TotalBytesToReceive = (ulong)e.TotalBytesToReceive //because size can't be negative + }); + }; + + await client.DownloadFileTaskAsync(uri, fileName); + } + + internal static async Task GetCodePushFolderAsync() + { + var pathToCodePush = Path.Combine(CodePushUtils.GetAppFolder(), CodePushConstants.CodePushFolderPrefix); + return await FileSystem.Current.LocalStorage.CreateFolderAsync(pathToCodePush, CreationCollisionOption.OpenIfExists).ConfigureAwait(false); + } + } +} diff --git a/windows/CodePush.Net46/packages.config b/windows/CodePush.Net46/packages.config new file mode 100644 index 0000000..6ebb2f9 --- /dev/null +++ b/windows/CodePush.Net46/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/windows/CodePush.Shared/CodePush.Shared.projitems b/windows/CodePush.Shared/CodePush.Shared.projitems new file mode 100644 index 0000000..5f85c52 --- /dev/null +++ b/windows/CodePush.Shared/CodePush.Shared.projitems @@ -0,0 +1,21 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 4ad4c826-cc26-4f1d-b60d-b97b22f52e90 + + + CodePush.Shared + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/CodePush.Shared/CodePush.Shared.shproj b/windows/CodePush.Shared/CodePush.Shared.shproj new file mode 100644 index 0000000..fc4e637 --- /dev/null +++ b/windows/CodePush.Shared/CodePush.Shared.shproj @@ -0,0 +1,13 @@ + + + + 4ad4c826-cc26-4f1d-b60d-b97b22f52e90 + 14.0 + + + + + + + + diff --git a/windows/CodePushConstants.cs b/windows/CodePush.Shared/CodePushConstants.cs similarity index 95% rename from windows/CodePushConstants.cs rename to windows/CodePush.Shared/CodePushConstants.cs index d92a257..1abc0f6 100644 --- a/windows/CodePushConstants.cs +++ b/windows/CodePush.Shared/CodePushConstants.cs @@ -2,7 +2,6 @@ { internal class CodePushConstants { - internal const string AssetsBundlePrefix = "ms-appx:///ReactAssets/"; internal const string BinaryModifiedTimeKey = "binaryModifiedTime"; internal const string CodePushServerUrl = "https://codepush.azurewebsites.net/"; internal const string CodePushFolderPrefix = "CodePush"; @@ -14,7 +13,6 @@ internal const string DownloadProgressEventName = "CodePushDownloadProgress"; internal const string DownloadUrlKey = "downloadUrl"; internal const string FailedUpdatesKey = "CODE_PUSH_FAILED_UPDATES"; - internal const string FileBundlePrefix = "ms-appdata:///local"; internal const string PackageFileName = "app.json"; internal const string PackageHashKey = "packageHash"; internal const string PendingUpdateHashKey = "hash"; @@ -27,5 +25,11 @@ internal const string RelativeBundlePathKey = "bundlePath"; internal const string StatusFileName = "codepush.json"; internal const string UnzippedFolderName = "unzipped"; +#if WINDOWS_UWP + internal const string AssetsBundlePrefix = "ms-appx:///ReactAssets/"; + internal const string FileBundlePrefix = "ms-appdata:///local"; +#else + internal const string AssetsBundlePrefix = "ReactAssets/"; +#endif } } diff --git a/windows/CodePushNativeModule.cs b/windows/CodePush.Shared/CodePushNativeModule.cs similarity index 95% rename from windows/CodePushNativeModule.cs rename to windows/CodePush.Shared/CodePushNativeModule.cs index 0cecebd..81c9fd1 100644 --- a/windows/CodePushNativeModule.cs +++ b/windows/CodePush.Shared/CodePushNativeModule.cs @@ -7,7 +7,11 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading.Tasks; +#if WINDOWS_UWP using Windows.Web.Http; +#else +using CodePush.Net46.Adapters.Http; +#endif namespace CodePush.ReactNative { @@ -57,7 +61,7 @@ namespace CodePush.ReactNative { try { - updatePackage[CodePushConstants.BinaryModifiedTimeKey] = "" + await _codePush.GetBinaryResourcesModifiedTimeAsync().ConfigureAwait(false); + updatePackage[CodePushConstants.BinaryModifiedTimeKey] = "" + await FileUtils.GetBinaryResourcesModifiedTimeAsync(_codePush.AssetsBundleFileName).ConfigureAwait(false); await _codePush.UpdateManager.DownloadPackageAsync( updatePackage, _codePush.AssetsBundleFileName, @@ -194,7 +198,7 @@ namespace CodePush.ReactNative await LoadBundleAsync().ConfigureAwait(false); }); }; - + _minimumBackgroundListener = new MinimumBackgroundListener(loadBundleAction, minimumBackgroundDuration); _reactContext.AddLifecycleEventListener(_minimumBackgroundListener); } @@ -240,17 +244,22 @@ namespace CodePush.ReactNative await LoadBundleAsync().ConfigureAwait(false); } } - + internal async Task LoadBundleAsync() { // #1) Get the private ReactInstanceManager, which is what includes // the logic to reload the current React context. FieldInfo info = typeof(ReactPage) .GetField("_reactInstanceManager", BindingFlags.NonPublic | BindingFlags.Instance); - +#if WINDOWS_UWP var reactInstanceManager = (ReactInstanceManager)typeof(ReactPage) .GetField("_reactInstanceManager", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(_codePush.MainPage); +#else + var reactInstanceManager = ((Lazy)typeof(ReactPage) + .GetField("_reactInstanceManager", BindingFlags.NonPublic | BindingFlags.Instance) + .GetValue(_codePush.MainPage)).Value as ReactInstanceManager; +#endif // #2) Update the locally stored JS bundle file path Type reactInstanceManagerType = typeof(ReactInstanceManager); diff --git a/windows/CodePushReactPackage.cs b/windows/CodePush.Shared/CodePushReactPackage.cs similarity index 77% rename from windows/CodePushReactPackage.cs rename to windows/CodePush.Shared/CodePushReactPackage.cs index 921af15..ce633ba 100644 --- a/windows/CodePushReactPackage.cs +++ b/windows/CodePush.Shared/CodePushReactPackage.cs @@ -1,25 +1,18 @@ using Newtonsoft.Json.Linq; -using Newtonsoft.Json; using ReactNative; using ReactNative.Bridge; using ReactNative.Modules.Core; using ReactNative.UIManager; using System; using System.Collections.Generic; -using System.Reflection; using System.Threading.Tasks; -using Windows.ApplicationModel; -using Windows.Storage; -using Windows.Web.Http; + namespace CodePush.ReactNative { public sealed class CodePushReactPackage : IReactPackage { private static CodePushReactPackage CurrentInstance; - private static bool NeedToReportRollback = false; - - private CodePushNativeModule _codePushNativeModule; internal string AppVersion { get; private set; } internal string DeploymentKey { get; private set; } @@ -31,7 +24,7 @@ namespace CodePush.ReactNative public CodePushReactPackage(string deploymentKey, ReactPage mainPage) { - AppVersion = Package.Current.Id.Version.Major + "." + Package.Current.Id.Version.Minor + "." + Package.Current.Id.Version.Build; + AppVersion = CodePushUtils.GetAppVersion(); DeploymentKey = deploymentKey; MainPage = mainPage; UpdateManager = new UpdateManager(); @@ -84,8 +77,9 @@ namespace CodePush.ReactNative public async Task GetJavaScriptBundleFileAsync(string assetsBundleFileName) { AssetsBundleFileName = assetsBundleFileName; - string binaryJsBundleUrl = CodePushConstants.AssetsBundlePrefix + assetsBundleFileName; - var binaryResourcesModifiedTime = await GetBinaryResourcesModifiedTimeAsync().ConfigureAwait(false); + string binaryJsBundleUrl = CodePushUtils.GetAssetsBundlePrefix() + assetsBundleFileName; + + var binaryResourcesModifiedTime = await FileUtils.GetBinaryResourcesModifiedTimeAsync(AssetsBundleFileName).ConfigureAwait(false); var packageFile = await UpdateManager.GetCurrentPackageBundleAsync(AssetsBundleFileName).ConfigureAwait(false); if (packageFile == null) { @@ -111,7 +105,7 @@ namespace CodePush.ReactNative { CodePushUtils.LogBundleUrl(packageFile.Path); IsRunningBinaryVersion = false; - return CodePushConstants.FileBundlePrefix + packageFile.Path.Replace(ApplicationData.Current.LocalFolder.Path, "").Replace("\\", "/"); + return CodePushUtils.GetFileBundlePrefix() + packageFile.Path.Replace(CodePushUtils.GetAppFolder(), "").Replace("\\", "/"); } else { @@ -128,17 +122,10 @@ namespace CodePush.ReactNative } } - #endregion +#endregion - #region Internal methods +#region Internal methods - internal async Task GetBinaryResourcesModifiedTimeAsync() - { - var assetJSBundleFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(CodePushConstants.AssetsBundlePrefix + AssetsBundleFileName)).AsTask().ConfigureAwait(false); - var fileProperties = await assetJSBundleFile.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false); - return fileProperties.DateModified.ToUnixTimeMilliseconds(); - } - internal void InitializeUpdateAfterRestart() { JObject pendingUpdate = SettingsManager.GetPendingUpdate(); @@ -150,7 +137,6 @@ namespace CodePush.ReactNative // Pending update was initialized, but notifyApplicationReady was not called. // Therefore, deduce that it is a broken update and rollback. CodePushUtils.Log("Update did not finish loading the last time, rolling back to a previous version."); - NeedToReportRollback = true; RollbackPackageAsync().Wait(); } else @@ -159,7 +145,7 @@ namespace CodePush.ReactNative // Clear the React dev bundle cache so that new updates can be loaded. if (MainPage.UseDeveloperSupport) { - ClearReactDevBundleCacheAsync().Wait(); + FileUtils.ClearReactDevBundleCacheAsync().Wait(); } // Mark that we tried to initialize the new update, so that if it crashes, // we will know that we need to rollback when the app next starts. @@ -175,18 +161,9 @@ namespace CodePush.ReactNative SettingsManager.RemoveFailedUpdates(); } - #endregion +#endregion - #region Private methods - - private async Task ClearReactDevBundleCacheAsync() - { - var devBundleCacheFile = (StorageFile)await ApplicationData.Current.LocalFolder.TryGetItemAsync(CodePushConstants.ReactDevBundleCacheFileName).AsTask().ConfigureAwait(false); - if (devBundleCacheFile != null) - { - await devBundleCacheFile.DeleteAsync().AsTask().ConfigureAwait(false); - } - } +#region Private methods private async Task RollbackPackageAsync() { @@ -196,6 +173,6 @@ namespace CodePush.ReactNative SettingsManager.RemovePendingUpdate(); } - #endregion +#endregion } } \ No newline at end of file diff --git a/windows/CodePush.Shared/CodePushUtils.cs b/windows/CodePush.Shared/CodePushUtils.cs new file mode 100644 index 0000000..7b93461 --- /dev/null +++ b/windows/CodePush.Shared/CodePushUtils.cs @@ -0,0 +1,74 @@ +using System; +using System.Diagnostics; +#if WINDOWS_UWP +using Windows.ApplicationModel; +using Windows.Storage; +#else +using System.IO; +#endif + +namespace CodePush.ReactNative +{ + internal partial class CodePushUtils + { + internal static void Log(string message) + { + Debug.WriteLine("[CodePush] " + message, CodePushConstants.ReactNativeLogCategory); + } + + internal static void LogBundleUrl(string path) + { + Log("Loading JS bundle from \"" + path + "\""); + } + + static string _deviceId = String.Empty; + + internal static string GetDeviceId() + { + //It's quite long operation, cache it + if (!String.IsNullOrEmpty(_deviceId)) + return _deviceId; + + _deviceId = GetDeviceIdImpl(); + return _deviceId; + } + + internal static string GetAppVersion() + { +#if WINDOWS_UWP + return Package.Current.Id.Version.Major + "." + Package.Current.Id.Version.Minor + "." + Package.Current.Id.Version.Build; +#else + var version = FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]); + return $"{version.FileMajorPart}.{version.FileMinorPart}.{version.FileBuildPart}"; +#endif + } + + internal static string GetAppFolder() + { +#if WINDOWS_UWP + return ApplicationData.Current.LocalFolder.Path; +#else + return AppDomain.CurrentDomain.BaseDirectory; +#endif + } + + internal static string GetAssetsBundlePrefix() + { +#if WINDOWS_UWP + return CodePushConstants.AssetsBundlePrefix; +#else + return Path.Combine(GetAppFolder(), CodePushConstants.AssetsBundlePrefix); +#endif + } + + internal static string GetFileBundlePrefix() + { +#if WINDOWS_UWP + return CodePushConstants.FileBundlePrefix; +#else + return GetAppFolder(); +#endif + } + + } +} diff --git a/windows/InstallMode.cs b/windows/CodePush.Shared/InstallMode.cs similarity index 100% rename from windows/InstallMode.cs rename to windows/CodePush.Shared/InstallMode.cs diff --git a/windows/MinimumBackgroundListener.cs b/windows/CodePush.Shared/MinimumBackgroundListener.cs similarity index 100% rename from windows/MinimumBackgroundListener.cs rename to windows/CodePush.Shared/MinimumBackgroundListener.cs diff --git a/windows/SettingsManager.cs b/windows/CodePush.Shared/SettingsManager.cs similarity index 86% rename from windows/SettingsManager.cs rename to windows/CodePush.Shared/SettingsManager.cs index b8250f7..c68e914 100644 --- a/windows/SettingsManager.cs +++ b/windows/CodePush.Shared/SettingsManager.cs @@ -1,13 +1,29 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +#if WINDOWS_UWP using Windows.Storage; +#else +using CodePush.Net46.Adapters.Storage; +using System.IO; +#endif namespace CodePush.ReactNative { internal class SettingsManager { - private static ApplicationDataContainer Settings = ApplicationData.Current.LocalSettings.CreateContainer(CodePushConstants.CodePushPreferences, ApplicationDataCreateDisposition.Always); + private static ApplicationDataContainer Settings = null; + + static SettingsManager () + { + +#if WINDOWS_UWP + Settings = ApplicationData.Current.LocalSettings.CreateContainer(CodePushConstants.CodePushPreferences, ApplicationDataCreateDisposition.Always); +#else + var folder = UpdateUtils.GetCodePushFolderAsync().Result; + Settings = new ApplicationDataContainer(Path.Combine(folder.Path, CodePushConstants.CodePushPreferences)); +#endif + } public static JArray GetFailedUpdates() { diff --git a/windows/UpdateState.cs b/windows/CodePush.Shared/UpdateState.cs similarity index 100% rename from windows/UpdateState.cs rename to windows/CodePush.Shared/UpdateState.cs diff --git a/windows/.npmignore b/windows/CodePush/.gitignore similarity index 100% rename from windows/.npmignore rename to windows/CodePush/.gitignore diff --git a/windows/CodePush.csproj b/windows/CodePush/CodePush.csproj similarity index 82% rename from windows/CodePush.csproj rename to windows/CodePush/CodePush.csproj index c32ed74..b5e54e6 100644 --- a/windows/CodePush.csproj +++ b/windows/CodePush/CodePush.csproj @@ -17,25 +17,6 @@ 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;NETFX_CORE;WINDOWS_UWP - prompt - 4 - x86 true @@ -107,18 +88,11 @@ - - - - - - - @@ -132,6 +106,7 @@ ReactNative + 14.0 diff --git a/windows/CodePushUtils.cs b/windows/CodePush/CodePushUtils.cs similarity index 72% rename from windows/CodePushUtils.cs rename to windows/CodePush/CodePushUtils.cs index f147889..23b9297 100644 --- a/windows/CodePushUtils.cs +++ b/windows/CodePush/CodePushUtils.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json.Linq; using System; -using System.Diagnostics; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; @@ -8,7 +7,7 @@ using Windows.System.Profile; namespace CodePush.ReactNative { - internal class CodePushUtils + internal partial class CodePushUtils { internal async static Task GetJObjectFromFileAsync(StorageFile file) { @@ -28,17 +27,7 @@ namespace CodePush.ReactNative } } - internal static void Log(string message) - { - Debug.WriteLine("[CodePush] " + message, CodePushConstants.ReactNativeLogCategory); - } - - internal static void LogBundleUrl(string path) - { - Log("Loading JS bundle from \"" + path + "\""); - } - - internal static string GetDeviceId() + static string GetDeviceIdImpl() { HardwareToken token = HardwareIdentification.GetPackageSpecificToken(null); IBuffer hardwareId = token.Id; diff --git a/windows/FileUtils.cs b/windows/CodePush/FileUtils.cs similarity index 52% rename from windows/FileUtils.cs rename to windows/CodePush/FileUtils.cs index 732fc3e..639a022 100644 --- a/windows/FileUtils.cs +++ b/windows/CodePush/FileUtils.cs @@ -19,5 +19,22 @@ namespace CodePush.ReactNative await MergeFoldersAsync(sourceDirectory, nextTargetSubDir).ConfigureAwait(false); } } + + internal async static Task ClearReactDevBundleCacheAsync() + { + var devBundleCacheFile = (StorageFile)await ApplicationData.Current.LocalFolder.TryGetItemAsync(CodePushConstants.ReactDevBundleCacheFileName).AsTask().ConfigureAwait(false); + if (devBundleCacheFile != null) + { + await devBundleCacheFile.DeleteAsync().AsTask().ConfigureAwait(false); + } + } + + internal async static Task GetBinaryResourcesModifiedTimeAsync(string fileName) + { + var assetJSBundleFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(CodePushConstants.AssetsBundlePrefix + fileName)).AsTask().ConfigureAwait(false); + var fileProperties = await assetJSBundleFile.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false); + return fileProperties.DateModified.ToUnixTimeMilliseconds(); + } + } } diff --git a/windows/Properties/AssemblyInfo.cs b/windows/CodePush/Properties/AssemblyInfo.cs similarity index 100% rename from windows/Properties/AssemblyInfo.cs rename to windows/CodePush/Properties/AssemblyInfo.cs diff --git a/windows/Properties/CodePush.rd.xml b/windows/CodePush/Properties/CodePush.rd.xml similarity index 100% rename from windows/Properties/CodePush.rd.xml rename to windows/CodePush/Properties/CodePush.rd.xml diff --git a/windows/UpdateManager.cs b/windows/CodePush/UpdateManager.cs similarity index 97% rename from windows/UpdateManager.cs rename to windows/CodePush/UpdateManager.cs index ae17a91..9afb0f0 100644 --- a/windows/UpdateManager.cs +++ b/windows/CodePush/UpdateManager.cs @@ -22,13 +22,13 @@ namespace CodePush.ReactNative internal async Task DownloadPackageAsync(JObject updatePackage, string expectedBundleFileName, Progress downloadProgress) { - // Using its hash, get the folder where the new update will be saved + // Using its hash, get the folder where the new update will be saved StorageFolder codePushFolder = await GetCodePushFolderAsync().ConfigureAwait(false); var newUpdateHash = (string)updatePackage[CodePushConstants.PackageHashKey]; StorageFolder newUpdateFolder = await GetPackageFolderAsync(newUpdateHash, false).ConfigureAwait(false); if (newUpdateFolder != null) { - // This removes any stale data in newPackageFolderPath that could have been left + // This removes any stale data in newUpdateFolder that could have been left // uncleared due to a crash or error during the download or install process. await newUpdateFolder.DeleteAsync().AsTask().ConfigureAwait(false); } @@ -98,6 +98,11 @@ namespace CodePush.ReactNative await downloadFile.RenameAsync(expectedBundleFileName).AsTask().ConfigureAwait(false); await downloadFile.MoveAsync(newUpdateFolder).AsTask().ConfigureAwait(false); } + /*TODO: ZipFile.ExtractToDirectory is not reliable and throws exceptions if: + - folder exists already + - path is too long + it needs to be handled + */ // Save metadata to the folder await FileIO.WriteTextAsync(newUpdateMetadataFile, JsonConvert.SerializeObject(updatePackage)).AsTask().ConfigureAwait(false); diff --git a/windows/UpdateUtils.cs b/windows/CodePush/UpdateUtils.cs similarity index 100% rename from windows/UpdateUtils.cs rename to windows/CodePush/UpdateUtils.cs diff --git a/windows/project.json b/windows/CodePush/project.json similarity index 100% rename from windows/project.json rename to windows/CodePush/project.json