From 3fbea78918f1b8353b13fc050935fc846cb5e53e Mon Sep 17 00:00:00 2001 From: David Britch Date: Thu, 19 Sep 2024 17:03:36 +0100 Subject: [PATCH] HybridWebView sample (#509) * HybridWebView demo * Add readme. --- .../HybridWebViewDemo/HybridWebViewDemo.sln | 25 + .../HybridWebViewDemo/App.xaml | 14 + .../HybridWebViewDemo/App.xaml.cs | 15 + .../HybridWebViewDemo/AppShell.xaml | 15 + .../HybridWebViewDemo/AppShell.xaml.cs | 10 + .../HybridWebViewDemo.csproj | 67 +++ .../HybridWebViewDemo/MainPage.xaml | 28 ++ .../HybridWebViewDemo/MainPage.xaml.cs | 76 +++ .../HybridWebViewDemo/MauiProgram.cs | 25 + .../Platforms/Android/AndroidManifest.xml | 6 + .../Platforms/Android/MainActivity.cs | 11 + .../Platforms/Android/MainApplication.cs | 16 + .../Android/Resources/values/colors.xml | 6 + .../Platforms/MacCatalyst/AppDelegate.cs | 10 + .../Platforms/MacCatalyst/Entitlements.plist | 14 + .../Platforms/MacCatalyst/Info.plist | 38 ++ .../Platforms/MacCatalyst/Program.cs | 16 + .../HybridWebViewDemo/Platforms/Tizen/Main.cs | 17 + .../Platforms/Tizen/tizen-manifest.xml | 15 + .../Platforms/Windows/App.xaml | 8 + .../Platforms/Windows/App.xaml.cs | 25 + .../Platforms/Windows/Package.appxmanifest | 46 ++ .../Platforms/Windows/app.manifest | 15 + .../Platforms/iOS/AppDelegate.cs | 10 + .../Platforms/iOS/Info.plist | 32 ++ .../Platforms/iOS/Program.cs | 16 + .../iOS/Resources/PrivacyInfo.xcprivacy | 51 ++ .../Properties/launchSettings.json | 8 + .../Resources/AppIcon/appicon.svg | 4 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107300 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111188 bytes .../Resources/Images/dotnet_bot.png | Bin 0 -> 69811 bytes .../Resources/Raw/AboutAssets.txt | 15 + .../Resources/Raw/wwwroot/asyncdata.txt | 4 + .../Resources/Raw/wwwroot/docs/sample.pdf | Bin 0 -> 18144 bytes .../Resources/Raw/wwwroot/index.html | 58 +++ .../Raw/wwwroot/scripts/HybridWebView.js | 78 +++ .../Resources/Raw/wwwroot/styles/app.css | 10 + .../Resources/Splash/splash.svg | 8 + .../Resources/Styles/Colors.xaml | 45 ++ .../Resources/Styles/Styles.xaml | 451 ++++++++++++++++++ .../Views/HybridWebViewDemo/README.md | 19 + 43 files changed, 1335 insertions(+) create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo.sln create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/HybridWebViewDemo.csproj create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MainPage.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MainPage.xaml.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MauiProgram.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Android/AndroidManifest.xml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Android/MainActivity.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Android/MainApplication.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Android/Resources/values/colors.xml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/MacCatalyst/AppDelegate.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/MacCatalyst/Entitlements.plist create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/MacCatalyst/Info.plist create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/MacCatalyst/Program.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Tizen/Main.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Tizen/tizen-manifest.xml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Windows/App.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Windows/App.xaml.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Windows/Package.appxmanifest create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/Windows/app.manifest create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/iOS/AppDelegate.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/iOS/Info.plist create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/iOS/Program.cs create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Platforms/iOS/Resources/PrivacyInfo.xcprivacy create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Properties/launchSettings.json create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/AppIcon/appicon.svg create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/AppIcon/appiconfg.svg create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Images/dotnet_bot.png create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/AboutAssets.txt create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/asyncdata.txt create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/docs/sample.pdf create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/index.html create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/scripts/HybridWebView.js create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/styles/app.css create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Splash/splash.svg create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Colors.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Styles.xaml create mode 100644 9.0/UserInterface/Views/HybridWebViewDemo/README.md diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo.sln b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo.sln new file mode 100644 index 000000000..e2a6389b7 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35309.182 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HybridWebViewDemo", "HybridWebViewDemo\HybridWebViewDemo.csproj", "{AA468903-C211-421B-A09A-81F3745FD28B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA468903-C211-421B-A09A-81F3745FD28B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA468903-C211-421B-A09A-81F3745FD28B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA468903-C211-421B-A09A-81F3745FD28B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA468903-C211-421B-A09A-81F3745FD28B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AE92D381-467A-44BA-938E-5BB75E28EEB1} + EndGlobalSection +EndGlobal diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml new file mode 100644 index 000000000..f01ae5cb0 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml.cs b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml.cs new file mode 100644 index 000000000..9b343be76 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/App.xaml.cs @@ -0,0 +1,15 @@ +namespace HybridWebViewDemo +{ + public partial class App : Application + { + public App() + { + InitializeComponent(); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new AppShell()); + } + } +} diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml new file mode 100644 index 000000000..e517d743b --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml @@ -0,0 +1,15 @@ + + + + + + diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml.cs b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml.cs new file mode 100644 index 000000000..df84d5feb --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace HybridWebViewDemo +{ + public partial class AppShell : Shell + { + public AppShell() + { + InitializeComponent(); + } + } +} diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/HybridWebViewDemo.csproj b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/HybridWebViewDemo.csproj new file mode 100644 index 000000000..8adb15943 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/HybridWebViewDemo.csproj @@ -0,0 +1,67 @@ + + + + net9.0-android;net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-windows10.0.19041.0 + + + + + + + Exe + HybridWebViewDemo + true + true + enable + enable + + + HybridWebViewDemo + + + com.companyname.hybridwebviewdemo + + + 1.0 + 1 + + + None + + 15.0 + 15.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MainPage.xaml b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MainPage.xaml new file mode 100644 index 000000000..bcc472021 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/MainPage.xaml @@ -0,0 +1,28 @@ + + + + + + + +
+ Message from C#: +
+
+ + diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/scripts/HybridWebView.js b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/scripts/HybridWebView.js new file mode 100644 index 000000000..337c388ce --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/scripts/HybridWebView.js @@ -0,0 +1,78 @@ +window.HybridWebView = { + "Init": function () { + function DispatchHybridWebViewMessage(message) { + const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } }); + window.dispatchEvent(event); + } + + if (window.chrome && window.chrome.webview) { + // Windows WebView2 + window.chrome.webview.addEventListener('message', arg => { + DispatchHybridWebViewMessage(arg.data); + }); + } + else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { + // iOS and MacCatalyst WKWebView + window.external = { + "receiveMessage": message => { + DispatchHybridWebViewMessage(message); + } + }; + } + else { + // Android WebView + window.addEventListener('message', arg => { + DispatchHybridWebViewMessage(arg.data); + }); + } + }, + + "SendRawMessage": function (message) { + window.HybridWebView.__SendMessageInternal('RawMessage', message); + }, + + "__SendMessageInternal": function (type, message) { + + const messageToSend = type + '|' + message; + + if (window.chrome && window.chrome.webview) { + // Windows WebView2 + window.chrome.webview.postMessage(messageToSend); + } + else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { + // iOS and MacCatalyst WKWebView + window.webkit.messageHandlers.webwindowinterop.postMessage(messageToSend); + } + else { + // Android WebView + hybridWebViewHost.sendMessage(messageToSend); + } + }, + + "InvokeMethod": function (taskId, methodName, args) { + if (methodName[Symbol.toStringTag] === 'AsyncFunction') { + // For async methods, we need to call the method and then trigger the callback when it's done + const asyncPromise = methodName(...args); + asyncPromise + .then(asyncResult => { + window.HybridWebView.__TriggerAsyncCallback(taskId, asyncResult); + }) + .catch(error => console.error(error)); + } else { + // For sync methods, we can call the method and trigger the callback immediately + const syncResult = methodName(...args); + window.HybridWebView.__TriggerAsyncCallback(taskId, syncResult); + } + }, + + "__TriggerAsyncCallback": function (taskId, result) { + // Make sure the result is a string + if (result && typeof (result) !== 'string') { + result = JSON.stringify(result); + } + + window.HybridWebView.__SendMessageInternal('InvokeMethodCompleted', taskId + '|' + result); + } +} + +window.HybridWebView.Init(); diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/styles/app.css b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/styles/app.css new file mode 100644 index 000000000..5e2f4852d --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Raw/wwwroot/styles/app.css @@ -0,0 +1,10 @@ +button +{ + background-color: dodgerblue; + border-radius: 5px; + border-width: 1px; + color: white; + cursor: pointer; + margin: 6px; + text-align: center; +} diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Splash/splash.svg b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Splash/splash.svg new file mode 100644 index 000000000..21dfb25f1 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Splash/splash.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Colors.xaml b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Colors.xaml new file mode 100644 index 000000000..30307a5dd --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Colors.xaml @@ -0,0 +1,45 @@ + + + + + + + #512BD4 + #ac99ea + #242424 + #DFD8F7 + #9880e5 + #2B0B98 + + White + Black + #D600AA + #190649 + #1f1f1f + + #E1E1E1 + #C8C8C8 + #ACACAC + #919191 + #6E6E6E + #404040 + #212121 + #141414 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Styles.xaml b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Styles.xaml new file mode 100644 index 000000000..185d9b2a2 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/HybridWebViewDemo/Resources/Styles/Styles.xaml @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/9.0/UserInterface/Views/HybridWebViewDemo/README.md b/9.0/UserInterface/Views/HybridWebViewDemo/README.md new file mode 100644 index 000000000..e01c47d84 --- /dev/null +++ b/9.0/UserInterface/Views/HybridWebViewDemo/README.md @@ -0,0 +1,19 @@ +--- +name: .NET MAUI - HybridWebView +description: This sample demonstrates how to use a .NET MAUI HybridWebView to host HTML/JS/CSS content, and communicate between that content and .NET. +page_type: sample +languages: +- csharp +- xaml +products: +- dotnet-maui +urlFragment: userinterface-hybridwebview +--- + +# HybridWebView + +The .NET Multi-platform App UI (.NET MAUI) `HybridWebView` enables hosting arbitrary HTML/JS/CSS content in a web view, and enables communication between the code in the web view (JavaScript) and the code that hosts the web view (C#/.NET). The entire app, including the web content, is packaged and runs locally on a device, and can be published to applicable app stores. The web content is hosted within a native web view control and runs within the context of the app. Any part of the app can access external web services, but isn't required to. + +This sample demonstrates how to use a .NET MAUI `HybridWebView` to host HTML/JS/CSS content, and communicate between that content and .NET. + +For more information about this sample, see [.NET MAUI HybridWebView](https://docs.microsoft.com/dotnet/maui/user-interface/controls/hybridwebview).