From 6c272f0e66c97477ef8ba758f498aaefb455bb05 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:50:18 -0500 Subject: [PATCH 01/15] initial commit --- x/examples/outline-pwa/README.md | 217 +++++++++++++++++++++++++++++-- 1 file changed, 205 insertions(+), 12 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 765099d5..69690501 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -1,19 +1,212 @@ -# Outline PWA Wrapper +# Building a Censorship-Resistant Android/iOS App with CapacitorJS and the Outline SDK Mobileproxy -Demonstration of how to wrap any URL with CapacitorJS in a way that all content is loaded over Outline SDK's Mobileproxy. Turn your website into a censorship-resistant app! +This code lab guides you through creating a censorship-resistant Android/iOS app that wraps your Progressive Web App (PWA) using CapacitorJS and the Outline SDK Mobileproxy. This setup routes your site's traffic through the Outline proxy, bypassing network restrictions. Note that the source in this folder is representative of what your end product should look like once the code lab is completed. -TODO: step-by-step walkthrough of the app-building process +**Prerequisites:** -## Setup +* Node.js and npm installed +* Android Studio with Kotlin support installed +* Xcode installed +* An existing PWA -```sh -npm ci -npx cap sync -``` -### iOS +## Set up the Capacitor Project -Open the `ios/App/App.xcworkspace` folder in XCode and run the project! +* Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) -### Android + ```bash + npx cap init + ``` -Open the `android` folder in Android Studio and run the project! \ No newline at end of file +* Add iOS and Android platforms to your project: + + ```bash + npx cap add android + npx cap add ios + ``` + +## Configure Capacitor to Load Your Website + +* **Delete the `www` folder.** Capacitor's default configuration assumes your web app is in this folder. We'll be loading your PWA directly from its URL. +* **Update `capacitor.config.json`:** + + ```json + { + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-pwa-url.com" + } + } + ``` + +## Add Support for Service Workers on iOS + +* In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: + + ```json + { + "ios": { + "limitsNavigationsToAppBoundDomains": true + } + } + ``` + +* In your iOS project's `Info.plist`, add your PWA's URL to the `WKAppBoundDomains` array: + + ```xml + WKAppBoundDomains + + www.your-pwa-url.com + + ``` + +## Test the Basic App + +* Run your app on a device or emulator: + + ```bash + npx cap run ios + npx cap run android + ``` + +* Ensure your PWA loads correctly within the Capacitor app. + +## Integrate the Mobileproxy Library + +**Build the Mobileproxy library:** + * Follow the instructions in the Outline SDK repository: [https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build) + +### iOS Integration + * Add the compiled `mobileproxy` static library to your Xcode project. + * In `OutlineBridgeViewController.swift`, override the `webView` method to inject the proxy configuration: + + ```swift + override func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView { + if #available(iOS 17.0, *) { + let endpoint = NWEndpoint.hostPort( + host: NWEndpoint.Host(self.proxyHost), + port: NWEndpoint.Port(self.proxyPort)! + ) + let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint) + + let websiteDataStore = WKWebsiteDataStore.default() + websiteDataStore.proxyConfigurations = [proxyConfig] + + configuration.websiteDataStore = websiteDataStore + } + + return super.webView(with: frame, configuration: configuration) + } + ``` + + * In `AppDelegate.swift`, set up the dialer and start the proxy in `applicationDidBecomeActive`: + + ```swift + func applicationDidBecomeActive(_ application: UIApplication) { + var dialerError: NSError? + if let dialer = MobileproxyNewSmartStreamDialer( + MobileproxyNewListFromLines("www.your-pwa-url.com"), + "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", + MobileproxyNewStderrLogWriter(), + &dialerError + ) { + var proxyError: NSError? + self.proxy = MobileproxyRunProxy( + "127.0.0.1:8080", + dialer, + &proxyError + ) + } + } + ``` + + * Stop the proxy in `applicationWillResignActive`: + + ```swift + func applicationWillResignActive(_ application: UIApplication) { + if let proxy = self.proxy { + proxy.stop(3000) + self.proxy = nil + } + } + ``` + +### Android Integration + * **Convert your Android project to Kotlin.** You can do this by following the instructions in the Android documentation: [https://developer.android.com/kotlin/add-kotlin](https://developer.android.com/kotlin/add-kotlin) + * In your `MainActivity.kt`, confirm proxy override is available in `onCreate`: + + ```kotlin + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { + // Proxy override is supported + } + } + ``` + + * Initialize `mobileproxy` with a smart dialer in `onCreate`: + + ```kotlin + private var proxy: Proxy? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { + this.proxy = Mobileproxy.runProxy( + "127.0.0.1:0", + Mobileproxy.newSmartStreamDialer( + Mobileproxy.newListFromLines("www.your-pwa-url.com"), + "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", + Mobileproxy.newStderrLogWriter() + ) + ) + } + } + ``` + + * Proxy all app requests using `ProxyController`: + + ```kotlin + // NOTE: This affects all requests in the application + ProxyController.getInstance() + .setProxyOverride( + ProxyConfig.Builder() + .addProxyRule(this.proxy!!.address()) + .build(), + { + runOnUiThread { + // Capacitor does not expose a way to defer the loading of the webview, + // so we simply refresh the page + this.bridge.webView.reload() + } + }, + {} + ) + ``` + + * Turn off the proxy in `onDestroy`: + + ```kotlin + override fun onDestroy() { + this.proxy?.stop(3000) + this.proxy = null + + super.onDestroy() + } + ``` + +## Verify with Packet Tracing + +* **Android:** Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). +* **iOS:** Use [Charles Proxy](https://www.charlesproxy.com/) to view network traffic coming from your iOS simulator and observe DoH traffic coming from `9.9.9.9`. + +**Important Notes:** + +* Replace `"www.your-pwa-url.com"` with your actual PWA URL. +* This code lab provides a basic framework. You might need to adjust it depending on your PWA's specific requirements and the Outline SDK's updates. +* Consider adding error handling and UI elements to improve the user experience. + +By following these steps, you can create an Android/iOS app that utilizes the Outline SDK Mobileproxy to circumvent censorship and access your PWA securely. This approach empowers users in restricted environments to access information and services freely. From 89af4385fba3e585fc2d32e8c01deaa7fb605eba Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:58:38 -0500 Subject: [PATCH 02/15] refinements --- x/examples/outline-pwa/README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 69690501..d8219467 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -79,7 +79,17 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ### iOS Integration * Add the compiled `mobileproxy` static library to your Xcode project. - * In `OutlineBridgeViewController.swift`, override the `webView` method to inject the proxy configuration: + * Create a new file called `OutlineBridgeViewController.swift` that looks like the following: + ```swift + import UIKit + import Capacitor + + class OutlineBridgeViewController: CAPBridgeViewController { + private let proxyHost: String = "127.0.0.1" + private let proxyPort: String = "8080" + } + ``` + * Override the `webView` method to inject the proxy configuration: ```swift override func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView { @@ -100,7 +110,18 @@ This code lab guides you through creating a censorship-resistant Android/iOS app } ``` - * In `AppDelegate.swift`, set up the dialer and start the proxy in `applicationDidBecomeActive`: + * In `AppDelegate.swift`, set the `rootViewController` to your new `OutlineBridgeViewController`: + + ```swift + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + self.window?.rootViewController = OutlineBridgeViewController() + + return true + } + ``` + + * Then, set up the dialer and start the proxy in `applicationDidBecomeActive`: ```swift func applicationDidBecomeActive(_ application: UIApplication) { @@ -167,7 +188,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app } ``` - * Proxy all app requests using `ProxyController`: + * Proxy all app requests after the proxy is initialized using `ProxyController`: ```kotlin // NOTE: This affects all requests in the application From d6827a8c8c100b3e13caaba87d0001413b14fb38 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:07:22 -0500 Subject: [PATCH 03/15] verified up through the ios mobileproxy integration --- x/examples/outline-pwa/README.md | 90 +++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index d8219467..d8d91aac 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -4,29 +4,34 @@ This code lab guides you through creating a censorship-resistant Android/iOS app **Prerequisites:** -* Node.js and npm installed -* Android Studio with Kotlin support installed -* Xcode installed * An existing PWA +* [Node.js](https://nodejs.org/en/) +* [GoLang](https://go.dev/) +* [Android Studio](https://developer.android.com/studio/) +* [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) +* [Wireshark](https://www.wireshark.org/) and [Charles Proxy](https://www.charlesproxy.com/), to confirm the app is working ## Set up the Capacitor Project * Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) ```bash - npx cap init + npm init @capacitor/app # follow the instructions + cd + npm install ``` * Add iOS and Android platforms to your project: ```bash + npm install @capacitor/android @capacitor/ios npx cap add android npx cap add ios ``` ## Configure Capacitor to Load Your Website -* **Delete the `www` folder.** Capacitor's default configuration assumes your web app is in this folder. We'll be loading your PWA directly from its URL. +* **Delete the `src` folder.** Capacitor's default configuration assumes your web app is in this folder. We'll be loading your PWA directly from its URL. * **Update `capacitor.config.json`:** ```json @@ -46,13 +51,19 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ```json { - "ios": { - "limitsNavigationsToAppBoundDomains": true - } + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-pwa-url.com" + }, + "ios": { + "limitsNavigationsToAppBoundDomains": true + } } ``` -* In your iOS project's `Info.plist`, add your PWA's URL to the `WKAppBoundDomains` array: +* In your iOS project's `ios/App/App/Info.plist`, add your PWA's URL to a `WKAppBoundDomains` array in the top level ``: ```xml WKAppBoundDomains @@ -61,25 +72,52 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ``` -## Test the Basic App + Be sure to add any URLs you navigate to within your PWA in this array as well! -* Run your app on a device or emulator: +## Test the Basic Apps - ```bash - npx cap run ios - npx cap run android - ``` +* Sync your changes, then run your app on a device or emulator: + + > [!NOTE] + > Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! + + ```bash + npx cap run ios + + mkdir android/app/src/main/assets # sync will fail without this folder + npx cap run android + ``` * Ensure your PWA loads correctly within the Capacitor app. -## Integrate the Mobileproxy Library +## Integrate the Outline SDK Mobileproxy Library **Build the Mobileproxy library:** - * Follow the instructions in the Outline SDK repository: [https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build) + * [Follow the instructions in the Outline SDK repository](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build): + + ```bash + git clone https://github.com/Jigsaw-Code/outline-sdk.git + cd outline-sdk/x + go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind + + PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "$(pwd)/out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy + PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy + ``` ### iOS Integration - * Add the compiled `mobileproxy` static library to your Xcode project. - * Create a new file called `OutlineBridgeViewController.swift` that looks like the following: + * Open the XCode project with `npx cap open ios`. + * Add the compiled `mobileproxy.xcframework` static library to your Xcode project: + * Right click on the `Frameworks` folder. + * Select `Add Files to "App"...` + * Select the `mobileproxy.xcframework` folder. + + * Create a new file called `OutlineBridgeViewController.swift`: + * Right click on the `App` folder. + * Select `New File...` + * Select the `Swift File` and click `Next`. + * Enter in the file name. + + * Extend the base Capacitor Bridge View Controller: ```swift import UIKit import Capacitor @@ -121,7 +159,19 @@ This code lab guides you through creating a censorship-resistant Android/iOS app } ``` - * Then, set up the dialer and start the proxy in `applicationDidBecomeActive`: + * Import the Mobileproxy framework at the head of the `AppDelegate.swift` file: + + ```swift + import Mobileproxy + ``` + + * Create a `proxy` property on the `AppDelegate`: + + ```swift + var proxy: MobileproxyProxy? + ``` + + * Then set up the dialer and start the proxy in `applicationDidBecomeActive`: ```swift func applicationDidBecomeActive(_ application: UIApplication) { From 0ecff50a040c63daf911ce6ab1ec08aa8a67c67c Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:32:06 -0500 Subject: [PATCH 04/15] android good, build process placeholder, need android verify --- x/examples/outline-pwa/README.md | 57 ++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index d8d91aac..aebbd6f6 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -5,18 +5,19 @@ This code lab guides you through creating a censorship-resistant Android/iOS app **Prerequisites:** * An existing PWA -* [Node.js](https://nodejs.org/en/) -* [GoLang](https://go.dev/) -* [Android Studio](https://developer.android.com/studio/) -* [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) -* [Wireshark](https://www.wireshark.org/) and [Charles Proxy](https://www.charlesproxy.com/), to confirm the app is working +* Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) + * [Node.js](https://nodejs.org/en/) + * [GoLang](https://go.dev/) + * [Android Studio](https://developer.android.com/studio/) + * [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) + * [Wireshark](https://www.wireshark.org/) and [Charles Proxy](https://www.charlesproxy.com/), to confirm the app is working ## Set up the Capacitor Project * Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) ```bash - npm init @capacitor/app # follow the instructions + npm init @capacitor/app # follow the instructions - make sure to pick an app ID that's unique! cd npm install ``` @@ -204,8 +205,32 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ``` ### Android Integration - * **Convert your Android project to Kotlin.** You can do this by following the instructions in the Android documentation: [https://developer.android.com/kotlin/add-kotlin](https://developer.android.com/kotlin/add-kotlin) - * In your `MainActivity.kt`, confirm proxy override is available in `onCreate`: + * **Convert your Android project to Kotlin.** Open the Android project with `npx cap open android`. + * Navigate to `java//MainActivity` + * Right click on the file and select "Convert Java File to Kotlin File". Confirm the following dialogs. + * Once done, you will need to right click the `MainActivity` a second time and select "Convert Java File to Kotlin File" + + [See the official instructions if you encounter any issues.](https://developer.android.com/kotlin/add-kotlin) + + + * **Import dependencies:** + * Right click on `app` and select "Open Module Settings" + * In the sidebar, navigate to "Dependencies" + * Click the `+` button and select a Library Dependency + * Search for `androidx.webkit` and select it. + * Next we need to import the `mobileproxy.aar`. Click the `+` button again and select `JAR/AAR Dependency`. + * Type in the path `../../outline-sdk/x/out/mobileproxy.aar` + * Click `Apply`. + + * In the head of your new `MainActivity.kt`, import the following: + + ```kotlin + import android.os.* + import mobileproxy.* + import androidx.webkit.* + ``` + + * Now, in your `MainActivity.kt`, confirm proxy override is available in `onCreate`: ```kotlin override fun onCreate(savedInstanceState: Bundle?) { @@ -217,7 +242,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app } ``` - * Initialize `mobileproxy` with a smart dialer in `onCreate`: + * Initialize `mobileproxy` with a smart dialer in `onCreate`. Don't forget to replace `www.your-pwa-url.com`!: ```kotlin private var proxy: Proxy? = null @@ -271,8 +296,18 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ## Verify with Packet Tracing -* **Android:** Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). -* **iOS:** Use [Charles Proxy](https://www.charlesproxy.com/) to view network traffic coming from your iOS simulator and observe DoH traffic coming from `9.9.9.9`. +* **iOS:** Start the emulator with `npx cap run ios`. Use [Charles Proxy](https://www.charlesproxy.com/) to view network traffic coming from your iOS simulator and observe DoH traffic coming from `9.9.9.9`. +* **Android:** Start the emulator with `npx cap run android`. Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). + +## Building and Distributing your App + +### iOS + +TODO: you will probably need to create a provisioning profile for your new app - https://forum.ionicframework.com/t/ios-build-app-provisioning-errors/208170/2 + +### Android + +TODO: you will need to create a keystore in android studio and configure the path in the capacitor.config.json https://forum.ionicframework.com/t/error-missing-options-keystore-path-keystore-password-keystore-key-alias-keystore-key-password/243217/3 **Important Notes:** From c01068398b1602f6e59f79acb9098306ec6e170b Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:01:15 -0500 Subject: [PATCH 05/15] charles proxy is not doing what I want --- x/examples/outline-pwa/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index aebbd6f6..1959bace 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -296,7 +296,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ## Verify with Packet Tracing -* **iOS:** Start the emulator with `npx cap run ios`. Use [Charles Proxy](https://www.charlesproxy.com/) to view network traffic coming from your iOS simulator and observe DoH traffic coming from `9.9.9.9`. +* **iOS:** TBD * **Android:** Start the emulator with `npx cap run android`. Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). ## Building and Distributing your App From e181e3257533219f7326163de1f2a3cb2b015a39 Mon Sep 17 00:00:00 2001 From: James Peter Perrone Jefferies Date: Mon, 2 Dec 2024 21:16:11 +0100 Subject: [PATCH 06/15] Fix Android integration instructions (#338) - Added steps to apply Kotlin plugin and update SDK versions in Gradle files. - Clarified dependency management to ensure dependencies are moved from the generated file to avoid reset issues. Without these updates, the Android setup instructions were incomplete and led to build issues. --- x/examples/outline-pwa/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 1959bace..2098e12f 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -212,6 +212,15 @@ This code lab guides you through creating a censorship-resistant Android/iOS app [See the official instructions if you encounter any issues.](https://developer.android.com/kotlin/add-kotlin) + * **Update your Gradle files for Kotlin compatibility.** + * Inside: `/android/app/build.gradle`, add `apply plugin: 'kotlin-android'` on line 2, directly under `apply plugin: 'com.android.application'`. + * Inside: `/android/variables.gradle`, update the SDK variables to: + + ``` + minSdkVersion = 26 + compileSdkVersion = 35 + targetSdkVersion = 35 + ``` * **Import dependencies:** * Right click on `app` and select "Open Module Settings" @@ -221,6 +230,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * Next we need to import the `mobileproxy.aar`. Click the `+` button again and select `JAR/AAR Dependency`. * Type in the path `../../outline-sdk/x/out/mobileproxy.aar` * Click `Apply`. + * **Important:** The two added dependencies are initially placed in `/android/app/capacitor.build.gradle`, which is a generated file that gets reset frequently. To avoid losing these dependencies, manually move them to `/android/app/build.gradle`. * In the head of your new `MainActivity.kt`, import the following: From 00fd9252aa7f2b29061e7983c67f02aeb9f50106 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:22:08 -0500 Subject: [PATCH 07/15] kotlin multiplatform, android release --- x/examples/outline-pwa/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 2098e12f..1bf24b19 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -10,7 +10,11 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * [GoLang](https://go.dev/) * [Android Studio](https://developer.android.com/studio/) * [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) - * [Wireshark](https://www.wireshark.org/) and [Charles Proxy](https://www.charlesproxy.com/), to confirm the app is working + * [Wireshark](https://www.wireshark.org/), to confirm the app is working + +> [!TIP] +> Kotlin Multiplatform [provides a tool called `kdoctor`](https://github.com/Kotlin/kdoctor) you can use to verify that your system is set up for cross-platform mobile development. You can ensure that +> your system has the required dependencies by [installing it](https://github.com/Kotlin/kdoctor?tab=readme-ov-file#installation) and running `kdoctor`. ## Set up the Capacitor Project @@ -317,7 +321,11 @@ TODO: you will probably need to create a provisioning profile for your new app - ### Android -TODO: you will need to create a keystore in android studio and configure the path in the capacitor.config.json https://forum.ionicframework.com/t/error-missing-options-keystore-path-keystore-password-keystore-key-alias-keystore-key-password/243217/3 +First, generate a Key Store and use it to sign your app with Android Studio - follow these instructions: https://developer.android.com/studio/publish/app-signing#generate-key + +Note that you can choose to release your app as either an android app bundle (`.aab`) or an APK (`.apk`). +1. You need an android app bundle (`.aab`) to release your app in the Google Play Store. For this you will have to have a [Google Play Developer Account](https://play.google.com/console/u/0/developers) and at least twenty trusted testers to unlock production access. +2. APKs (`.apk`) can be freely sideloaded onto your user's devices. For an APK, you will have to take care of distribution yourself. **Important Notes:** From 71a37c32d28901c8df6d561ea040885bb6f6030b Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:35:29 -0500 Subject: [PATCH 08/15] ready for initial review! --- x/examples/outline-pwa/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 1bf24b19..51ba174b 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -310,14 +310,18 @@ This code lab guides you through creating a censorship-resistant Android/iOS app ## Verify with Packet Tracing -* **iOS:** TBD +* **iOS:** Load XCode and connect your iOS device. Make sure that your device allows your developer certificate (Check `Settings > General > VPN & Device Management` on your iOS device). Go to `Product > Profile` in XCode and select the "Network" option from the list of presets. Pressing the record button on the window that pops up should launch your app and record its traffic. Once done, set the view on the packet trace data to `History` and among the results you should see the DNS address `dn9.quad9.net`. * **Android:** Start the emulator with `npx cap run android`. Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). ## Building and Distributing your App ### iOS -TODO: you will probably need to create a provisioning profile for your new app - https://forum.ionicframework.com/t/ios-build-app-provisioning-errors/208170/2 +1. Create a developer account and add it to XCode Settings (`XCode > Settings... > Accounts`) +2. Select your App in the file explorer and pick your Account's Team in `Signing & Capabilities` +3. Connect your iOS device and select it as your build target in the top bar. XCode should automatically create a provisioning profile for the device and app combination. +4. Build a production archive with `Product > Archive` +5. On success, a window should pop up with your app listed in it. Select `Distribute App` and follow the instructions. ### Android From dab3955cc719a47496074dde45362616f83849c3 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:39:09 -0500 Subject: [PATCH 09/15] partial feedback --- x/examples/outline-pwa/README.md | 98 +++++++++++++++++--------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 51ba174b..d2ca558f 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -1,6 +1,6 @@ # Building a Censorship-Resistant Android/iOS App with CapacitorJS and the Outline SDK Mobileproxy -This code lab guides you through creating a censorship-resistant Android/iOS app that wraps your Progressive Web App (PWA) using CapacitorJS and the Outline SDK Mobileproxy. This setup routes your site's traffic through the Outline proxy, bypassing network restrictions. Note that the source in this folder is representative of what your end product should look like once the code lab is completed. +This code lab guides you through creating a censorship-resistant Android/iOS app from scratch that wraps your Progressive Web App (PWA) using CapacitorJS and the Outline SDK Mobileproxy. This setup routes your site's traffic through the Outline proxy, bypassing network restrictions. Note that the source in this folder is representative of what your end product should look like once the code lab is completed. **Prerequisites:** @@ -8,9 +8,9 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) * [Node.js](https://nodejs.org/en/) * [GoLang](https://go.dev/) - * [Android Studio](https://developer.android.com/studio/) + * [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) * [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) - * [Wireshark](https://www.wireshark.org/), to confirm the app is working +* [Wireshark](https://www.wireshark.org/), to confirm the app is working > [!TIP] > Kotlin Multiplatform [provides a tool called `kdoctor`](https://github.com/Kotlin/kdoctor) you can use to verify that your system is set up for cross-platform mobile development. You can ensure that @@ -20,6 +20,9 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) +> [!NOTE] +> This will create a new directory containing your project. Make sure you run these commands relative to where you want your project to live. + ```bash npm init @capacitor/app # follow the instructions - make sure to pick an app ID that's unique! cd @@ -28,34 +31,53 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * Add iOS and Android platforms to your project: - ```bash - npm install @capacitor/android @capacitor/ios - npx cap add android - npx cap add ios - ``` + ```bash + npm install @capacitor/android @capacitor/ios + npx cap add android + npx cap add ios + + mkdir android/app/src/main/assets # sync will fail without this folder + npx cap sync + ``` + +## Confirm that the default apps are able to run + +### iOS + +* Open the iOS project in XCode with `npx cap open ios`. +* Make sure the correct emulator or device is selected and press the ▶️ button. -## Configure Capacitor to Load Your Website +### Android + +* Open the Android project in Android Studio with `npx cap open android`. +* Ensure you have an emulator with Android API 35 or later (check `Tools > Device Manager`), then press the ▶️ button. + +--- + +## Configure Capacitor to Load Your PWA -* **Delete the `src` folder.** Capacitor's default configuration assumes your web app is in this folder. We'll be loading your PWA directly from its URL. * **Update `capacitor.config.json`:** - ```json - { - "appId": "com.yourcompany.yourapp", - "appName": "YourAppName", - "bundledWebRuntime": false, - "server": { - "url": "https://www.your-pwa-url.com" - } - } - ``` + ```json + { + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-pwa-url.com" + } + } + ``` + + > [!NOTE] + > Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! ## Add Support for Service Workers on iOS * In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: - ```json - { + ```json + { "appId": "com.yourcompany.yourapp", "appName": "YourAppName", "bundledWebRuntime": false, @@ -65,35 +87,19 @@ This code lab guides you through creating a censorship-resistant Android/iOS app "ios": { "limitsNavigationsToAppBoundDomains": true } - } - ``` + } + ``` * In your iOS project's `ios/App/App/Info.plist`, add your PWA's URL to a `WKAppBoundDomains` array in the top level ``: - ```xml - WKAppBoundDomains - - www.your-pwa-url.com - - ``` - - Be sure to add any URLs you navigate to within your PWA in this array as well! - -## Test the Basic Apps - -* Sync your changes, then run your app on a device or emulator: - - > [!NOTE] - > Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! - - ```bash - npx cap run ios - - mkdir android/app/src/main/assets # sync will fail without this folder - npx cap run android + ```xml + WKAppBoundDomains + + www.your-pwa-url.com + ``` -* Ensure your PWA loads correctly within the Capacitor app. + Be sure to add any URLs you navigate to within your PWA in this array as well! ## Integrate the Outline SDK Mobileproxy Library From 3e1f72c1413f0f32ab0948fe2a5b22793758dcf6 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:49:58 -0500 Subject: [PATCH 10/15] add vini's suggestion --- x/examples/outline-pwa/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index d2ca558f..f93d5c7d 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -8,8 +8,8 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) * [Node.js](https://nodejs.org/en/) * [GoLang](https://go.dev/) - * [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) - * [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) + * For Android, [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) + * For iOS, [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) * [Wireshark](https://www.wireshark.org/), to confirm the app is working > [!TIP] From 48bc47e7198e044cbe46262cff73d3abba4ce4e5 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:06:06 -0500 Subject: [PATCH 11/15] Update x/examples/outline-pwa/README.md Co-authored-by: Vinicius Fortuna --- x/examples/outline-pwa/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index f93d5c7d..1dade004 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -7,7 +7,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * An existing PWA * Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) * [Node.js](https://nodejs.org/en/) - * [GoLang](https://go.dev/) + * [GoLang](https://go.dev/), to build the Outline Mobile Proxy. * For Android, [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) * For iOS, [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) * [Wireshark](https://www.wireshark.org/), to confirm the app is working From b1a4a929407a54f49b9698c07b4b79ef7a9dd846 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:06:54 -0500 Subject: [PATCH 12/15] Update x/examples/outline-pwa/README.md Co-authored-by: Vinicius Fortuna --- x/examples/outline-pwa/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index 1dade004..e201c62c 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -6,7 +6,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app * An existing PWA * Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) - * [Node.js](https://nodejs.org/en/) + * [Node.js](https://nodejs.org/en/), for the Capacitor build system. * [GoLang](https://go.dev/), to build the Outline Mobile Proxy. * For Android, [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) * For iOS, [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) From 9962737259c57863ad9177a42590d755c5f62e48 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:20:39 -0500 Subject: [PATCH 13/15] fix issue --- x/examples/outline-pwa/README.md | 69 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md index f93d5c7d..9a8dec93 100644 --- a/x/examples/outline-pwa/README.md +++ b/x/examples/outline-pwa/README.md @@ -4,18 +4,14 @@ This code lab guides you through creating a censorship-resistant Android/iOS app **Prerequisites:** -* An existing PWA -* Make sure your development environment is set up with the following. [You can also follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup) +* A website you want to make censorship resistant. +* Make sure your development environment is set up with the following. **[Please follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup)** * [Node.js](https://nodejs.org/en/) * [GoLang](https://go.dev/) * For Android, [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) * For iOS, [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) * [Wireshark](https://www.wireshark.org/), to confirm the app is working -> [!TIP] -> Kotlin Multiplatform [provides a tool called `kdoctor`](https://github.com/Kotlin/kdoctor) you can use to verify that your system is set up for cross-platform mobile development. You can ensure that -> your system has the required dependencies by [installing it](https://github.com/Kotlin/kdoctor?tab=readme-ov-file#installation) and running `kdoctor`. - ## Set up the Capacitor Project * Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) @@ -37,6 +33,7 @@ This code lab guides you through creating a censorship-resistant Android/iOS app npx cap add ios mkdir android/app/src/main/assets # sync will fail without this folder + npm run build # this builds the stock web app that the default project ships with npx cap sync ``` @@ -72,35 +69,6 @@ This code lab guides you through creating a censorship-resistant Android/iOS app > [!NOTE] > Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! -## Add Support for Service Workers on iOS - -* In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: - - ```json - { - "appId": "com.yourcompany.yourapp", - "appName": "YourAppName", - "bundledWebRuntime": false, - "server": { - "url": "https://www.your-pwa-url.com" - }, - "ios": { - "limitsNavigationsToAppBoundDomains": true - } - } - ``` - -* In your iOS project's `ios/App/App/Info.plist`, add your PWA's URL to a `WKAppBoundDomains` array in the top level ``: - - ```xml - WKAppBoundDomains - - www.your-pwa-url.com - - ``` - - Be sure to add any URLs you navigate to within your PWA in this array as well! - ## Integrate the Outline SDK Mobileproxy Library **Build the Mobileproxy library:** @@ -344,3 +312,34 @@ Note that you can choose to release your app as either an android app bundle (`. * Consider adding error handling and UI elements to improve the user experience. By following these steps, you can create an Android/iOS app that utilizes the Outline SDK Mobileproxy to circumvent censorship and access your PWA securely. This approach empowers users in restricted environments to access information and services freely. + +## Advanced: Add Support for Service Workers on iOS + +By default, Service workers aren't supported in iOS webviews. To activate them, you need to do the following: + +* In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: + + ```json + { + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-pwa-url.com" + }, + "ios": { + "limitsNavigationsToAppBoundDomains": true + } + } + ``` + +* In your iOS project's `ios/App/App/Info.plist`, add your PWA's URL to a `WKAppBoundDomains` array in the top level ``: + + ```xml + WKAppBoundDomains + + www.your-pwa-url.com + + ``` + + Be sure to add any URLs you navigate to within your PWA in this array as well! From 5ac1619938828d5528bd735c2e2d70332e54e295 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:14:11 -0500 Subject: [PATCH 14/15] rename and move everything --- x/examples/outline-pwa/README.md | 345 -------------- .../.gitignore | 0 .../LICENSE | 0 x/examples/outline-web-wrapper/README.md | 425 ++++++++++++++++++ .../android/.gitignore | 0 .../android/app/.gitignore | 0 .../android/app/build.gradle | 0 .../android/app/capacitor.build.gradle | 0 .../android/app/proguard-rules.pro | 0 .../myapp/ExampleInstrumentedTest.java | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../java/org/getoutline/pwa/MainActivity.kt | 0 .../main/res/drawable-land-hdpi/splash.png | Bin .../main/res/drawable-land-mdpi/splash.png | Bin .../main/res/drawable-land-xhdpi/splash.png | Bin .../main/res/drawable-land-xxhdpi/splash.png | Bin .../main/res/drawable-land-xxxhdpi/splash.png | Bin .../main/res/drawable-port-hdpi/splash.png | Bin .../main/res/drawable-port-mdpi/splash.png | Bin .../main/res/drawable-port-xhdpi/splash.png | Bin .../main/res/drawable-port-xxhdpi/splash.png | Bin .../main/res/drawable-port-xxxhdpi/splash.png | Bin .../drawable-v24/ic_launcher_foreground.xml | 0 .../res/drawable/ic_launcher_background.xml | 0 .../app/src/main/res/drawable/splash.png | Bin .../app/src/main/res/layout/activity_main.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../res/values/ic_launcher_background.xml | 0 .../app/src/main/res/values/strings.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../app/src/main/res/xml/file_paths.xml | 0 .../getcapacitor/myapp/ExampleUnitTest.java | 0 .../android/build.gradle | 0 .../android/capacitor.settings.gradle | 0 .../android/gradle.properties | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/gradlew | 0 .../android/gradlew.bat | 0 .../android/settings.gradle | 0 .../android/variables.gradle | 0 .../capacitor.config.json | 0 .../generated/mobileproxy.aar | Bin .../mobileproxy.xcframework/Info.plist | 0 .../Headers/Mobileproxy.h | 0 .../Headers/Mobileproxy.objc.h | 0 .../Headers/Universe.objc.h | 0 .../Mobileproxy.framework/Headers/ref.h | 0 .../Mobileproxy.framework/Info.plist | 0 .../Mobileproxy.framework/Mobileproxy | Bin .../Modules/module.modulemap | 0 .../Headers/Mobileproxy.h | 0 .../Headers/Mobileproxy.objc.h | 0 .../Headers/Universe.objc.h | 0 .../Mobileproxy.framework/Headers/ref.h | 0 .../Mobileproxy.framework/Info.plist | 0 .../Mobileproxy.framework/Mobileproxy | Bin .../Modules/module.modulemap | 0 .../ios/.gitignore | 0 .../ios/App/App.xcodeproj/project.pbxproj | 0 .../App.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../ios/App/App/AppDelegate.swift | 0 .../AppIcon.appiconset/AppIcon-512@2x.png | Bin .../AppIcon.appiconset/Contents.json | 0 .../ios/App/App/Assets.xcassets/Contents.json | 0 .../Splash.imageset/Contents.json | 0 .../Splash.imageset/splash-2732x2732-1.png | Bin .../Splash.imageset/splash-2732x2732-2.png | Bin .../Splash.imageset/splash-2732x2732.png | Bin .../App/Base.lproj/LaunchScreen.storyboard | 0 .../ios/App/App/Base.lproj/Main.storyboard | 0 .../ios/App/App/Info.plist | 0 .../App/App/OutlineBridgeViewController.swift | 0 .../ios/App/Podfile | 0 .../ios/App/Podfile.lock | 0 .../package-lock.json | 0 .../package.json | 0 94 files changed, 425 insertions(+), 345 deletions(-) delete mode 100644 x/examples/outline-pwa/README.md rename x/examples/{outline-pwa => outline-web-wrapper}/.gitignore (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/LICENSE (100%) create mode 100644 x/examples/outline-web-wrapper/README.md rename x/examples/{outline-pwa => outline-web-wrapper}/android/.gitignore (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/.gitignore (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/build.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/capacitor.build.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/proguard-rules.pro (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/AndroidManifest.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-land-hdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-land-mdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-land-xhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-land-xxhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-land-xxxhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-port-hdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-port-mdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-port-xhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-port-xxhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-port-xxxhdpi/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable/ic_launcher_background.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/drawable/splash.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/layout/activity_main.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/values/ic_launcher_background.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/values/strings.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/values/styles.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/main/res/xml/file_paths.xml (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/build.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/capacitor.settings.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/gradle.properties (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/gradlew (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/gradlew.bat (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/settings.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/android/variables.gradle (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/capacitor.config.json (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.aar (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/Info.plist (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/.gitignore (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App.xcodeproj/project.pbxproj (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App.xcworkspace/contents.xcworkspacedata (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/AppDelegate.swift (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/Contents.json (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Base.lproj/LaunchScreen.storyboard (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Base.lproj/Main.storyboard (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/Info.plist (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/App/OutlineBridgeViewController.swift (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/Podfile (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/ios/App/Podfile.lock (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/package-lock.json (100%) rename x/examples/{outline-pwa => outline-web-wrapper}/package.json (100%) diff --git a/x/examples/outline-pwa/README.md b/x/examples/outline-pwa/README.md deleted file mode 100644 index 8017f35e..00000000 --- a/x/examples/outline-pwa/README.md +++ /dev/null @@ -1,345 +0,0 @@ -# Building a Censorship-Resistant Android/iOS App with CapacitorJS and the Outline SDK Mobileproxy - -This code lab guides you through creating a censorship-resistant Android/iOS app from scratch that wraps your Progressive Web App (PWA) using CapacitorJS and the Outline SDK Mobileproxy. This setup routes your site's traffic through the Outline proxy, bypassing network restrictions. Note that the source in this folder is representative of what your end product should look like once the code lab is completed. - -**Prerequisites:** - -* A website you want to make censorship resistant. -* Make sure your development environment is set up with the following. **[Please follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup)** - * [Node.js](https://nodejs.org/en/), for the Capacitor build system. - * [GoLang](https://go.dev/), to build the Outline Mobile Proxy. - * For Android, [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) - * For iOS, [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/) -* [Wireshark](https://www.wireshark.org/), to confirm the app is working - -## Set up the Capacitor Project - -* Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) - -> [!NOTE] -> This will create a new directory containing your project. Make sure you run these commands relative to where you want your project to live. - - ```bash - npm init @capacitor/app # follow the instructions - make sure to pick an app ID that's unique! - cd - npm install - ``` - -* Add iOS and Android platforms to your project: - - ```bash - npm install @capacitor/android @capacitor/ios - npx cap add android - npx cap add ios - - mkdir android/app/src/main/assets # sync will fail without this folder - npm run build # this builds the stock web app that the default project ships with - npx cap sync - ``` - -## Confirm that the default apps are able to run - -### iOS - -* Open the iOS project in XCode with `npx cap open ios`. -* Make sure the correct emulator or device is selected and press the ▶️ button. - -### Android - -* Open the Android project in Android Studio with `npx cap open android`. -* Ensure you have an emulator with Android API 35 or later (check `Tools > Device Manager`), then press the ▶️ button. - ---- - -## Configure Capacitor to Load Your PWA - -* **Update `capacitor.config.json`:** - - ```json - { - "appId": "com.yourcompany.yourapp", - "appName": "YourAppName", - "bundledWebRuntime": false, - "server": { - "url": "https://www.your-pwa-url.com" - } - } - ``` - - > [!NOTE] - > Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! - -## Integrate the Outline SDK Mobileproxy Library - -**Build the Mobileproxy library:** - * [Follow the instructions in the Outline SDK repository](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build): - - ```bash - git clone https://github.com/Jigsaw-Code/outline-sdk.git - cd outline-sdk/x - go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind - - PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "$(pwd)/out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy - PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy - ``` - -### iOS Integration - * Open the XCode project with `npx cap open ios`. - * Add the compiled `mobileproxy.xcframework` static library to your Xcode project: - * Right click on the `Frameworks` folder. - * Select `Add Files to "App"...` - * Select the `mobileproxy.xcframework` folder. - - * Create a new file called `OutlineBridgeViewController.swift`: - * Right click on the `App` folder. - * Select `New File...` - * Select the `Swift File` and click `Next`. - * Enter in the file name. - - * Extend the base Capacitor Bridge View Controller: - ```swift - import UIKit - import Capacitor - - class OutlineBridgeViewController: CAPBridgeViewController { - private let proxyHost: String = "127.0.0.1" - private let proxyPort: String = "8080" - } - ``` - * Override the `webView` method to inject the proxy configuration: - - ```swift - override func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView { - if #available(iOS 17.0, *) { - let endpoint = NWEndpoint.hostPort( - host: NWEndpoint.Host(self.proxyHost), - port: NWEndpoint.Port(self.proxyPort)! - ) - let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint) - - let websiteDataStore = WKWebsiteDataStore.default() - websiteDataStore.proxyConfigurations = [proxyConfig] - - configuration.websiteDataStore = websiteDataStore - } - - return super.webView(with: frame, configuration: configuration) - } - ``` - - * In `AppDelegate.swift`, set the `rootViewController` to your new `OutlineBridgeViewController`: - - ```swift - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - - self.window?.rootViewController = OutlineBridgeViewController() - - return true - } - ``` - - * Import the Mobileproxy framework at the head of the `AppDelegate.swift` file: - - ```swift - import Mobileproxy - ``` - - * Create a `proxy` property on the `AppDelegate`: - - ```swift - var proxy: MobileproxyProxy? - ``` - - * Then set up the dialer and start the proxy in `applicationDidBecomeActive`: - - ```swift - func applicationDidBecomeActive(_ application: UIApplication) { - var dialerError: NSError? - if let dialer = MobileproxyNewSmartStreamDialer( - MobileproxyNewListFromLines("www.your-pwa-url.com"), - "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", - MobileproxyNewStderrLogWriter(), - &dialerError - ) { - var proxyError: NSError? - self.proxy = MobileproxyRunProxy( - "127.0.0.1:8080", - dialer, - &proxyError - ) - } - } - ``` - - * Stop the proxy in `applicationWillResignActive`: - - ```swift - func applicationWillResignActive(_ application: UIApplication) { - if let proxy = self.proxy { - proxy.stop(3000) - self.proxy = nil - } - } - ``` - -### Android Integration - * **Convert your Android project to Kotlin.** Open the Android project with `npx cap open android`. - * Navigate to `java//MainActivity` - * Right click on the file and select "Convert Java File to Kotlin File". Confirm the following dialogs. - * Once done, you will need to right click the `MainActivity` a second time and select "Convert Java File to Kotlin File" - - [See the official instructions if you encounter any issues.](https://developer.android.com/kotlin/add-kotlin) - - * **Update your Gradle files for Kotlin compatibility.** - * Inside: `/android/app/build.gradle`, add `apply plugin: 'kotlin-android'` on line 2, directly under `apply plugin: 'com.android.application'`. - * Inside: `/android/variables.gradle`, update the SDK variables to: - - ``` - minSdkVersion = 26 - compileSdkVersion = 35 - targetSdkVersion = 35 - ``` - - * **Import dependencies:** - * Right click on `app` and select "Open Module Settings" - * In the sidebar, navigate to "Dependencies" - * Click the `+` button and select a Library Dependency - * Search for `androidx.webkit` and select it. - * Next we need to import the `mobileproxy.aar`. Click the `+` button again and select `JAR/AAR Dependency`. - * Type in the path `../../outline-sdk/x/out/mobileproxy.aar` - * Click `Apply`. - * **Important:** The two added dependencies are initially placed in `/android/app/capacitor.build.gradle`, which is a generated file that gets reset frequently. To avoid losing these dependencies, manually move them to `/android/app/build.gradle`. - - * In the head of your new `MainActivity.kt`, import the following: - - ```kotlin - import android.os.* - import mobileproxy.* - import androidx.webkit.* - ``` - - * Now, in your `MainActivity.kt`, confirm proxy override is available in `onCreate`: - - ```kotlin - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { - // Proxy override is supported - } - } - ``` - - * Initialize `mobileproxy` with a smart dialer in `onCreate`. Don't forget to replace `www.your-pwa-url.com`!: - - ```kotlin - private var proxy: Proxy? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { - this.proxy = Mobileproxy.runProxy( - "127.0.0.1:0", - Mobileproxy.newSmartStreamDialer( - Mobileproxy.newListFromLines("www.your-pwa-url.com"), - "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", - Mobileproxy.newStderrLogWriter() - ) - ) - } - } - ``` - - * Proxy all app requests after the proxy is initialized using `ProxyController`: - - ```kotlin - // NOTE: This affects all requests in the application - ProxyController.getInstance() - .setProxyOverride( - ProxyConfig.Builder() - .addProxyRule(this.proxy!!.address()) - .build(), - { - runOnUiThread { - // Capacitor does not expose a way to defer the loading of the webview, - // so we simply refresh the page - this.bridge.webView.reload() - } - }, - {} - ) - ``` - - * Turn off the proxy in `onDestroy`: - - ```kotlin - override fun onDestroy() { - this.proxy?.stop(3000) - this.proxy = null - - super.onDestroy() - } - ``` - -## Verify with Packet Tracing - -* **iOS:** Load XCode and connect your iOS device. Make sure that your device allows your developer certificate (Check `Settings > General > VPN & Device Management` on your iOS device). Go to `Product > Profile` in XCode and select the "Network" option from the list of presets. Pressing the record button on the window that pops up should launch your app and record its traffic. Once done, set the view on the packet trace data to `History` and among the results you should see the DNS address `dn9.quad9.net`. -* **Android:** Start the emulator with `npx cap run android`. Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). - -## Building and Distributing your App - -### iOS - -1. Create a developer account and add it to XCode Settings (`XCode > Settings... > Accounts`) -2. Select your App in the file explorer and pick your Account's Team in `Signing & Capabilities` -3. Connect your iOS device and select it as your build target in the top bar. XCode should automatically create a provisioning profile for the device and app combination. -4. Build a production archive with `Product > Archive` -5. On success, a window should pop up with your app listed in it. Select `Distribute App` and follow the instructions. - -### Android - -First, generate a Key Store and use it to sign your app with Android Studio - follow these instructions: https://developer.android.com/studio/publish/app-signing#generate-key - -Note that you can choose to release your app as either an android app bundle (`.aab`) or an APK (`.apk`). -1. You need an android app bundle (`.aab`) to release your app in the Google Play Store. For this you will have to have a [Google Play Developer Account](https://play.google.com/console/u/0/developers) and at least twenty trusted testers to unlock production access. -2. APKs (`.apk`) can be freely sideloaded onto your user's devices. For an APK, you will have to take care of distribution yourself. - -**Important Notes:** - -* Replace `"www.your-pwa-url.com"` with your actual PWA URL. -* This code lab provides a basic framework. You might need to adjust it depending on your PWA's specific requirements and the Outline SDK's updates. -* Consider adding error handling and UI elements to improve the user experience. - -By following these steps, you can create an Android/iOS app that utilizes the Outline SDK Mobileproxy to circumvent censorship and access your PWA securely. This approach empowers users in restricted environments to access information and services freely. - -## Advanced: Add Support for Service Workers on iOS - -By default, Service workers aren't supported in iOS webviews. To activate them, you need to do the following: - -* In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: - - ```json - { - "appId": "com.yourcompany.yourapp", - "appName": "YourAppName", - "bundledWebRuntime": false, - "server": { - "url": "https://www.your-pwa-url.com" - }, - "ios": { - "limitsNavigationsToAppBoundDomains": true - } - } - ``` - -* In your iOS project's `ios/App/App/Info.plist`, add your PWA's URL to a `WKAppBoundDomains` array in the top level ``: - - ```xml - WKAppBoundDomains - - www.your-pwa-url.com - - ``` - - Be sure to add any URLs you navigate to within your PWA in this array as well! diff --git a/x/examples/outline-pwa/.gitignore b/x/examples/outline-web-wrapper/.gitignore similarity index 100% rename from x/examples/outline-pwa/.gitignore rename to x/examples/outline-web-wrapper/.gitignore diff --git a/x/examples/outline-pwa/LICENSE b/x/examples/outline-web-wrapper/LICENSE similarity index 100% rename from x/examples/outline-pwa/LICENSE rename to x/examples/outline-web-wrapper/LICENSE diff --git a/x/examples/outline-web-wrapper/README.md b/x/examples/outline-web-wrapper/README.md new file mode 100644 index 00000000..72740bac --- /dev/null +++ b/x/examples/outline-web-wrapper/README.md @@ -0,0 +1,425 @@ +# Wrapping your website in a Censorship-Resistant iOS App with CapacitorJS and the Outline SDK Mobileproxy + +## Prerequisites + +* A website you want to make censorship resistant. +* Set up your development environment with the following. + * [Node.js](https://nodejs.org/en/), for the Capacitor build system. + * [GoLang](https://go.dev/), to build the Outline Mobile Proxy. + * [XCode](https://developer.apple.com/xcode/) and [cocoapods](https://cocoapods.org/). [Please follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup#ios-requirements) + +## Important Notes + +* Replace `"www.your-website-url.com"` with your actual website URL. +* This code lab provides a basic framework. You might need to adjust it depending on your website's specific requirements and the Outline SDK's updates. + +## Create your app + +**1. Set up the Capacitor Project** + +Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) + +> [!NOTE] +> This will create a new directory containing your project. Make sure you run these commands relative to where you want your project to live. + +```bash +npm init @capacitor/app # follow the instructions - make sure to pick an app ID that's unique! +cd +npm install +``` + +Add iOS platform to your project: + +```bash +npm install @capacitor/ios +npx cap add ios + +npm run build # this builds the stock web app that the default project ships with +npx cap sync ios +``` + +**2. Confirm that the default app is able to run** + +Open the iOS project in XCode with `npx cap open ios`. + +Make sure the correct emulator or device is selected and press the ▶️ button. + +**3. Configure Capacitor to Load Your Website** + +Update `capacitor.config.json`: + +```json +{ + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-website-url.com" + } +} +``` + +> [!NOTE] +> Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! + +**4. Integrate the Outline SDK Mobileproxy Library** + + Build the Mobileproxy library: + +[Follow the instructions in the Outline SDK repository](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build): + +```bash +git clone https://github.com/Jigsaw-Code/outline-sdk.git +cd outline-sdk/x +go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind + +PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "$(pwd)/out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy +``` +Open the XCode project with `npx cap open ios`. +Add the compiled `mobileproxy.xcframework` static library to your Xcode project: +* Right click on the `Frameworks` folder. +* Select `Add Files to "App"...` +* Select the `mobileproxy.xcframework` folder. + +Create a new file called `OutlineBridgeViewController.swift`: +* Right click on the `App` folder. +* Select `New File...` +* Select the `Swift File` and click `Next`. +* Enter in the file name. + +Extend the base Capacitor Bridge View Controller: + +```swift +import UIKit +import Capacitor + +class OutlineBridgeViewController: CAPBridgeViewController { + private let proxyHost: String = "127.0.0.1" + private let proxyPort: String = "8080" +} +``` + +Override the `webView` method to inject the proxy configuration: + +```swift +override func webView(with frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView { + if #available(iOS 17.0, *) { + let endpoint = NWEndpoint.hostPort( + host: NWEndpoint.Host(self.proxyHost), + port: NWEndpoint.Port(self.proxyPort)! + ) + let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint) + + let websiteDataStore = WKWebsiteDataStore.default() + websiteDataStore.proxyConfigurations = [proxyConfig] + + configuration.websiteDataStore = websiteDataStore + } + + return super.webView(with: frame, configuration: configuration) +} +``` + +In `AppDelegate.swift`, set the `rootViewController` to your new `OutlineBridgeViewController`: + +```swift +func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + self.window?.rootViewController = OutlineBridgeViewController() + + return true +} +``` + +Import the Mobileproxy framework at the head of the `AppDelegate.swift` file: + +```swift +import Mobileproxy +``` + +Create a `proxy` property on the `AppDelegate`: + +```swift +var proxy: MobileproxyProxy? +``` + +Then set up the dialer and start the proxy in `applicationDidBecomeActive`: + +```swift +func applicationDidBecomeActive(_ application: UIApplication) { + var dialerError: NSError? + if let dialer = MobileproxyNewSmartStreamDialer( + MobileproxyNewListFromLines("www.your-website-url.com"), + "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", + MobileproxyNewStderrLogWriter(), + &dialerError + ) { + var proxyError: NSError? + self.proxy = MobileproxyRunProxy( + "127.0.0.1:8080", + dialer, + &proxyError + ) + } +} +``` + +Stop the proxy in `applicationWillResignActive`: + +```swift +func applicationWillResignActive(_ application: UIApplication) { + if let proxy = self.proxy { + proxy.stop(3000) + self.proxy = nil + } +} +``` + +**5. Verify with Packet Tracing** + +Load XCode and connect your iOS device. Make sure that your device allows your developer certificate (Check `Settings > General > VPN & Device Management` on your iOS device). Go to `Product > Profile` in XCode and select the "Network" option from the list of presets. Pressing the record button on the window that pops up should launch your app and record its traffic. Once done, set the view on the packet trace data to `History` and among the results you should see the DNS address `dn9.quad9.net`. + +**6. Building and Distributing your App** + +* Create a developer account and add it to XCode Settings (`XCode > Settings... > Accounts`) +* Select your App in the file explorer and pick your Account's Team in `Signing & Capabilities` +* Connect your iOS device and select it as your build target in the top bar. XCode should automatically create a provisioning profile for the device and app combination. +* Build a production archive with `Product > Archive` +* On success, a window should pop up with your app listed in it. Select `Distribute App` and follow the instructions. + +**7. Advanced: Add Support for Service Workers on iOS** + +By default, Service workers aren't supported in iOS webviews. To activate them, you need to do the following: + +In `capacitor.config.json`, enable `limitsNavigationsToAppBoundDomains`: + +```json +{ + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-website-url.com" + }, + "ios": { + "limitsNavigationsToAppBoundDomains": true + } +} +``` + +In your iOS project's `ios/App/App/Info.plist`, add your Website's URL to a `WKAppBoundDomains` array in the top level ``: + +```xml +WKAppBoundDomains + + www.your-website-url.com + +``` + +Be sure to add any URLs you navigate to within your website in this array as well! + + +--- + + +# Building a Censorship-Resistant Android App with CapacitorJS and the Outline SDK Mobileproxy + +## Prerequisites + +* A website you want to make censorship resistant. +* Make sure your development environment is set up with the following. + * [Node.js](https://nodejs.org/en/), for the Capacitor build system. + * [GoLang](https://go.dev/), to build the Outline Mobile Proxy. + * [OpenJDK 17](https://stackoverflow.com/a/70649641) and [Android Studio](https://developer.android.com/studio/) [Please follow CapacitorJS's environment setup guide](https://capacitorjs.com/docs/getting-started/environment-setup#android-requirements) +* [Wireshark](https://www.wireshark.org/), to confirm traffic is going through the Outline proxy + +## Important Notes + +* Replace `"www.your-website-url.com"` with your actual website URL in all the relevant code snippets. +* This code lab provides a basic framework. You might need to adjust it depending on your website's specific requirements and the Outline SDK's updates. +* **Security:** Keep your key store file and passwords secure. Losing them can prevent you from updating your app in the future. +* **Testing:** Thoroughly test your app on different devices and Android versions before releasing it. + +## Create your app + +**1. Set up the Capacitor Project** + +Follow the CapacitorJS Getting Started guide to initialize a new project: [https://capacitorjs.com/docs/getting-started](https://capacitorjs.com/docs/getting-started) + +> [!NOTE] +> This will create a new directory containing your project. Make sure you run these commands relative to where you want your project to live. + +```bash +npm init @capacitor/app # follow the instructions - make sure to pick an app ID that's unique! +cd +npm install +``` + +Add Android platform to your project: + +```bash +npm install @capacitor/android +npx cap add android + +mkdir android/app/src/main/assets # sync will fail without this folder +npm run build # this builds the stock web app that the default project ships with +npx cap sync android +``` + +**2. Confirm that the default app is able to run** + +Open the Android project in Android Studio with `npx cap open android`. + +Ensure you have an emulator with Android API 35 or later (check `Tools > Device Manager`), then press the ▶️ button. + +**3. Configure Capacitor to Load Your Website** + +Update `capacitor.config.json`: + +```json +{ + "appId": "com.yourcompany.yourapp", + "appName": "YourAppName", + "bundledWebRuntime": false, + "server": { + "url": "https://www.your-website-url.com" + } +} +``` + +> [!NOTE] +> Capacitor will silently fail to read your config if the JSON is invalid. Be sure to check `capacitor.config.json` for any hanging commas! + +**4. Integrate the Outline SDK Mobileproxy Library** + +**Build the Mobileproxy library:** +[Follow the instructions in the Outline SDK repository](https://github.com/Jigsaw-Code/outline-sdk/tree/main/x/mobileproxy#build-the-go-mobile-binaries-with-go-build): + +```bash + git clone https://github.com/Jigsaw-Code/outline-sdk.git + cd outline-sdk/x + go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind + + PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy +``` + +**Convert your Android project to Kotlin.** Open the Android project with `npx cap open android`. + +* Navigate to `java//MainActivity` +* Right click on the file and select "Convert Java File to Kotlin File". Confirm the following dialogs. +* Once done, you will need to right click the `MainActivity` a second time and select "Convert Java File to Kotlin File" + +[See the official instructions if you encounter any issues.](https://developer.android.com/kotlin/add-kotlin) + +**Update your Gradle files for Kotlin compatibility.** + +* Inside: `/android/app/build.gradle`, add `apply plugin: 'kotlin-android'` on line 2, directly under `apply plugin: 'com.android.application'`. +* Inside: `/android/variables.gradle`, update the SDK variables to: + +```kotlin +minSdkVersion = 26 +compileSdkVersion = 35 +targetSdkVersion = 35 +``` + +**Import dependencies:** + +* Right click on `app` and select "Open Module Settings" +* In the sidebar, navigate to "Dependencies" +* Click the `+` button and select a Library Dependency +* Search for `androidx.webkit` and select it. +* Next we need to import the `mobileproxy.aar`. Click the `+` button again and select `JAR/AAR Dependency`. +* Type in the path `../../outline-sdk/x/out/mobileproxy.aar` +* Click `Apply`. +* **Important:** The two added dependencies are initially placed in `/android/app/capacitor.build.gradle`, which is a generated file that gets reset frequently. To avoid losing these dependencies, manually move them to `/android/app/build.gradle`. +* In the head of your new `MainActivity.kt`, import the following: + +```kotlin +import android.os.* +import mobileproxy.* +import androidx.webkit.* +``` + +Now, in your `MainActivity.kt`, confirm proxy override is available in `onCreate`: + +```kotlin +override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { + // Proxy override is supported + } +} +``` + +Initialize `mobileproxy` with a smart dialer in `onCreate`. Don't forget to replace `www.your-website-url.com`!: + +```kotlin +private var proxy: Proxy? = null + +override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) { + this.proxy = Mobileproxy.runProxy( + "127.0.0.1:0", + Mobileproxy.newSmartStreamDialer( + Mobileproxy.newListFromLines("www.your-website-url.com"), + "{\"dns\":[{\"https\":{\"name\":\"9.9.9.9\"}}],\"tls\":[\"\",\"split:1\",\"split:2\",\"tlsfrag:1\"]}", + Mobileproxy.newStderrLogWriter() + ) + ) + } +} +``` + +Proxy all app requests after the proxy is initialized using `ProxyController`: + +```kotlin +// NOTE: This affects all requests in the application +ProxyController.getInstance() + .setProxyOverride( + ProxyConfig.Builder() + .addProxyRule(this.proxy!!.address()) + .build(), + { + runOnUiThread { + // Capacitor does not expose a way to defer the loading of the webview, + // so we simply refresh the page + this.bridge.webView.reload() + } + }, + {} + ) +``` + +Turn off the proxy in `onDestroy`: + +```kotlin +override fun onDestroy() { + this.proxy?.stop(3000) + this.proxy = null + + super.onDestroy() +} +``` + +**6. Verify with Packet Tracing** + +Start the emulator with `npx cap run android`. Use Wireshark to capture network traffic. Filter by `ip.addr == 9.9.9.9` (your chosen DNS server). You should see TCP and TLS traffic, indicating that your app is using DNS over HTTPS (DoH). + +**7. Building and Distributing your App** + +First, generate a Key Store and use it to sign your app with Android Studio - follow these instructions: [https://developer.android.com/studio/publish/app-signing#generate-key](https://developer.android.com/studio/publish/app-signing#generate-key) + +Note that you can choose to release your app as either an android app bundle (`.aab`) or an APK (`.apk`). + +You need an android app bundle (`.aab`) to release your app in the Google Play Store. For this you will have to have a [Google Play Developer Account](https://play.google.com/console/u/0/developers) and at least twenty trusted testers to unlock production access. + +* Create a new application. +* Fill in the required information (store listing, pricing, etc.). +* Navigate to "App releases" and select a release track (e.g., internal testing, closed testing, open testing, or production). +* Upload your `.aab` file. +* Follow the instructions to complete the release process. + +APKs (`.apk`) can be freely sideloaded onto your user's devices. For an APK, you will have to take care of distribution yourself. **Note that users need to enable "Install unknown apps"** on their Android devices to install apps from sources other than the Google Play Store. This setting is usually found under `Settings > Security` or `Settings > Apps & notifications > Special app access`. diff --git a/x/examples/outline-pwa/android/.gitignore b/x/examples/outline-web-wrapper/android/.gitignore similarity index 100% rename from x/examples/outline-pwa/android/.gitignore rename to x/examples/outline-web-wrapper/android/.gitignore diff --git a/x/examples/outline-pwa/android/app/.gitignore b/x/examples/outline-web-wrapper/android/app/.gitignore similarity index 100% rename from x/examples/outline-pwa/android/app/.gitignore rename to x/examples/outline-web-wrapper/android/app/.gitignore diff --git a/x/examples/outline-pwa/android/app/build.gradle b/x/examples/outline-web-wrapper/android/app/build.gradle similarity index 100% rename from x/examples/outline-pwa/android/app/build.gradle rename to x/examples/outline-web-wrapper/android/app/build.gradle diff --git a/x/examples/outline-pwa/android/app/capacitor.build.gradle b/x/examples/outline-web-wrapper/android/app/capacitor.build.gradle similarity index 100% rename from x/examples/outline-pwa/android/app/capacitor.build.gradle rename to x/examples/outline-web-wrapper/android/app/capacitor.build.gradle diff --git a/x/examples/outline-pwa/android/app/proguard-rules.pro b/x/examples/outline-web-wrapper/android/app/proguard-rules.pro similarity index 100% rename from x/examples/outline-pwa/android/app/proguard-rules.pro rename to x/examples/outline-web-wrapper/android/app/proguard-rules.pro diff --git a/x/examples/outline-pwa/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/x/examples/outline-web-wrapper/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java similarity index 100% rename from x/examples/outline-pwa/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java rename to x/examples/outline-web-wrapper/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java diff --git a/x/examples/outline-pwa/android/app/src/main/AndroidManifest.xml b/x/examples/outline-web-wrapper/android/app/src/main/AndroidManifest.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/AndroidManifest.xml rename to x/examples/outline-web-wrapper/android/app/src/main/AndroidManifest.xml diff --git a/x/examples/outline-pwa/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt b/x/examples/outline-web-wrapper/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt rename to x/examples/outline-web-wrapper/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-land-hdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-hdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-land-hdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-hdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-land-mdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-mdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-land-mdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-mdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-land-xhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxxhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxxhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxxhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-port-hdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-hdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-port-hdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-hdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-port-mdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-mdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-port-mdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-mdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-port-xhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxxhdpi/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxxhdpi/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxxhdpi/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable/ic_launcher_background.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable/ic_launcher_background.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/drawable/splash.png b/x/examples/outline-web-wrapper/android/app/src/main/res/drawable/splash.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/drawable/splash.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/drawable/splash.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/layout/activity_main.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/layout/activity_main.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/layout/activity_main.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/layout/activity_main.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/x/examples/outline-pwa/android/app/src/main/res/values/ic_launcher_background.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/values/ic_launcher_background.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/values/ic_launcher_background.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/values/strings.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/values/strings.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/values/strings.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/values/strings.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/values/styles.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/values/styles.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/values/styles.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/values/styles.xml diff --git a/x/examples/outline-pwa/android/app/src/main/res/xml/file_paths.xml b/x/examples/outline-web-wrapper/android/app/src/main/res/xml/file_paths.xml similarity index 100% rename from x/examples/outline-pwa/android/app/src/main/res/xml/file_paths.xml rename to x/examples/outline-web-wrapper/android/app/src/main/res/xml/file_paths.xml diff --git a/x/examples/outline-pwa/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/x/examples/outline-web-wrapper/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java similarity index 100% rename from x/examples/outline-pwa/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java rename to x/examples/outline-web-wrapper/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java diff --git a/x/examples/outline-pwa/android/build.gradle b/x/examples/outline-web-wrapper/android/build.gradle similarity index 100% rename from x/examples/outline-pwa/android/build.gradle rename to x/examples/outline-web-wrapper/android/build.gradle diff --git a/x/examples/outline-pwa/android/capacitor.settings.gradle b/x/examples/outline-web-wrapper/android/capacitor.settings.gradle similarity index 100% rename from x/examples/outline-pwa/android/capacitor.settings.gradle rename to x/examples/outline-web-wrapper/android/capacitor.settings.gradle diff --git a/x/examples/outline-pwa/android/gradle.properties b/x/examples/outline-web-wrapper/android/gradle.properties similarity index 100% rename from x/examples/outline-pwa/android/gradle.properties rename to x/examples/outline-web-wrapper/android/gradle.properties diff --git a/x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.jar b/x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.jar rename to x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.jar diff --git a/x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.properties b/x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.properties rename to x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.properties diff --git a/x/examples/outline-pwa/android/gradlew b/x/examples/outline-web-wrapper/android/gradlew similarity index 100% rename from x/examples/outline-pwa/android/gradlew rename to x/examples/outline-web-wrapper/android/gradlew diff --git a/x/examples/outline-pwa/android/gradlew.bat b/x/examples/outline-web-wrapper/android/gradlew.bat similarity index 100% rename from x/examples/outline-pwa/android/gradlew.bat rename to x/examples/outline-web-wrapper/android/gradlew.bat diff --git a/x/examples/outline-pwa/android/settings.gradle b/x/examples/outline-web-wrapper/android/settings.gradle similarity index 100% rename from x/examples/outline-pwa/android/settings.gradle rename to x/examples/outline-web-wrapper/android/settings.gradle diff --git a/x/examples/outline-pwa/android/variables.gradle b/x/examples/outline-web-wrapper/android/variables.gradle similarity index 100% rename from x/examples/outline-pwa/android/variables.gradle rename to x/examples/outline-web-wrapper/android/variables.gradle diff --git a/x/examples/outline-pwa/capacitor.config.json b/x/examples/outline-web-wrapper/capacitor.config.json similarity index 100% rename from x/examples/outline-pwa/capacitor.config.json rename to x/examples/outline-web-wrapper/capacitor.config.json diff --git a/x/examples/outline-pwa/generated/mobileproxy.aar b/x/examples/outline-web-wrapper/generated/mobileproxy.aar similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.aar rename to x/examples/outline-web-wrapper/generated/mobileproxy.aar diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/Info.plist b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/Info.plist similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/Info.plist rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/Info.plist diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy diff --git a/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap b/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap similarity index 100% rename from x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap rename to x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap diff --git a/x/examples/outline-pwa/ios/.gitignore b/x/examples/outline-web-wrapper/ios/.gitignore similarity index 100% rename from x/examples/outline-pwa/ios/.gitignore rename to x/examples/outline-web-wrapper/ios/.gitignore diff --git a/x/examples/outline-pwa/ios/App/App.xcodeproj/project.pbxproj b/x/examples/outline-web-wrapper/ios/App/App.xcodeproj/project.pbxproj similarity index 100% rename from x/examples/outline-pwa/ios/App/App.xcodeproj/project.pbxproj rename to x/examples/outline-web-wrapper/ios/App/App.xcodeproj/project.pbxproj diff --git a/x/examples/outline-pwa/ios/App/App.xcworkspace/contents.xcworkspacedata b/x/examples/outline-web-wrapper/ios/App/App.xcworkspace/contents.xcworkspacedata similarity index 100% rename from x/examples/outline-pwa/ios/App/App.xcworkspace/contents.xcworkspacedata rename to x/examples/outline-web-wrapper/ios/App/App.xcworkspace/contents.xcworkspacedata diff --git a/x/examples/outline-pwa/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/x/examples/outline-web-wrapper/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from x/examples/outline-pwa/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to x/examples/outline-web-wrapper/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/x/examples/outline-pwa/ios/App/App/AppDelegate.swift b/x/examples/outline-web-wrapper/ios/App/App/AppDelegate.swift similarity index 100% rename from x/examples/outline-pwa/ios/App/App/AppDelegate.swift rename to x/examples/outline-web-wrapper/ios/App/App/AppDelegate.swift diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Contents.json b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Contents.json similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/Contents.json rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Contents.json diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png diff --git a/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png rename to x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png diff --git a/x/examples/outline-pwa/ios/App/App/Base.lproj/LaunchScreen.storyboard b/x/examples/outline-web-wrapper/ios/App/App/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Base.lproj/LaunchScreen.storyboard rename to x/examples/outline-web-wrapper/ios/App/App/Base.lproj/LaunchScreen.storyboard diff --git a/x/examples/outline-pwa/ios/App/App/Base.lproj/Main.storyboard b/x/examples/outline-web-wrapper/ios/App/App/Base.lproj/Main.storyboard similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Base.lproj/Main.storyboard rename to x/examples/outline-web-wrapper/ios/App/App/Base.lproj/Main.storyboard diff --git a/x/examples/outline-pwa/ios/App/App/Info.plist b/x/examples/outline-web-wrapper/ios/App/App/Info.plist similarity index 100% rename from x/examples/outline-pwa/ios/App/App/Info.plist rename to x/examples/outline-web-wrapper/ios/App/App/Info.plist diff --git a/x/examples/outline-pwa/ios/App/App/OutlineBridgeViewController.swift b/x/examples/outline-web-wrapper/ios/App/App/OutlineBridgeViewController.swift similarity index 100% rename from x/examples/outline-pwa/ios/App/App/OutlineBridgeViewController.swift rename to x/examples/outline-web-wrapper/ios/App/App/OutlineBridgeViewController.swift diff --git a/x/examples/outline-pwa/ios/App/Podfile b/x/examples/outline-web-wrapper/ios/App/Podfile similarity index 100% rename from x/examples/outline-pwa/ios/App/Podfile rename to x/examples/outline-web-wrapper/ios/App/Podfile diff --git a/x/examples/outline-pwa/ios/App/Podfile.lock b/x/examples/outline-web-wrapper/ios/App/Podfile.lock similarity index 100% rename from x/examples/outline-pwa/ios/App/Podfile.lock rename to x/examples/outline-web-wrapper/ios/App/Podfile.lock diff --git a/x/examples/outline-pwa/package-lock.json b/x/examples/outline-web-wrapper/package-lock.json similarity index 100% rename from x/examples/outline-pwa/package-lock.json rename to x/examples/outline-web-wrapper/package-lock.json diff --git a/x/examples/outline-pwa/package.json b/x/examples/outline-web-wrapper/package.json similarity index 100% rename from x/examples/outline-pwa/package.json rename to x/examples/outline-web-wrapper/package.json From b0458744deabf208f0c73ff38dc4976cf841a13c Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:41:15 -0500 Subject: [PATCH 15/15] move this back --- .../{outline-web-wrapper => outline-pwa}/.gitignore | 0 .../{outline-web-wrapper => outline-pwa}/LICENSE | 0 .../{outline-web-wrapper => outline-pwa}/README.md | 0 .../android/.gitignore | 0 .../android/app/.gitignore | 0 .../android/app/build.gradle | 0 .../android/app/capacitor.build.gradle | 0 .../android/app/proguard-rules.pro | 0 .../getcapacitor/myapp/ExampleInstrumentedTest.java | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../main/java/org/getoutline/pwa/MainActivity.kt | 0 .../app/src/main/res/drawable-land-hdpi/splash.png | Bin .../app/src/main/res/drawable-land-mdpi/splash.png | Bin .../app/src/main/res/drawable-land-xhdpi/splash.png | Bin .../src/main/res/drawable-land-xxhdpi/splash.png | Bin .../src/main/res/drawable-land-xxxhdpi/splash.png | Bin .../app/src/main/res/drawable-port-hdpi/splash.png | Bin .../app/src/main/res/drawable-port-mdpi/splash.png | Bin .../app/src/main/res/drawable-port-xhdpi/splash.png | Bin .../src/main/res/drawable-port-xxhdpi/splash.png | Bin .../src/main/res/drawable-port-xxxhdpi/splash.png | Bin .../res/drawable-v24/ic_launcher_foreground.xml | 0 .../main/res/drawable/ic_launcher_background.xml | 0 .../android/app/src/main/res/drawable/splash.png | Bin .../app/src/main/res/layout/activity_main.xml | 0 .../src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../main/res/mipmap-hdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../main/res/mipmap-mdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher_round.png | Bin .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher_round.png | Bin .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../src/main/res/values/ic_launcher_background.xml | 0 .../android/app/src/main/res/values/strings.xml | 0 .../android/app/src/main/res/values/styles.xml | 0 .../android/app/src/main/res/xml/file_paths.xml | 0 .../com/getcapacitor/myapp/ExampleUnitTest.java | 0 .../android/build.gradle | 0 .../android/capacitor.settings.gradle | 0 .../android/gradle.properties | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/gradlew | 0 .../android/gradlew.bat | 0 .../android/settings.gradle | 0 .../android/variables.gradle | 0 .../capacitor.config.json | 0 .../generated/mobileproxy.aar | Bin .../generated/mobileproxy.xcframework/Info.plist | 0 .../Mobileproxy.framework/Headers/Mobileproxy.h | 0 .../Headers/Mobileproxy.objc.h | 0 .../Mobileproxy.framework/Headers/Universe.objc.h | 0 .../ios-arm64/Mobileproxy.framework/Headers/ref.h | 0 .../ios-arm64/Mobileproxy.framework/Info.plist | 0 .../ios-arm64/Mobileproxy.framework/Mobileproxy | Bin .../Mobileproxy.framework/Modules/module.modulemap | 0 .../Mobileproxy.framework/Headers/Mobileproxy.h | 0 .../Headers/Mobileproxy.objc.h | 0 .../Mobileproxy.framework/Headers/Universe.objc.h | 0 .../Mobileproxy.framework/Headers/ref.h | 0 .../Mobileproxy.framework/Info.plist | 0 .../Mobileproxy.framework/Mobileproxy | Bin .../Mobileproxy.framework/Modules/module.modulemap | 0 .../ios/.gitignore | 0 .../ios/App/App.xcodeproj/project.pbxproj | 0 .../App/App.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../ios/App/App/AppDelegate.swift | 0 .../AppIcon.appiconset/AppIcon-512@2x.png | Bin .../AppIcon.appiconset/Contents.json | 0 .../ios/App/App/Assets.xcassets/Contents.json | 0 .../Assets.xcassets/Splash.imageset/Contents.json | 0 .../Splash.imageset/splash-2732x2732-1.png | Bin .../Splash.imageset/splash-2732x2732-2.png | Bin .../Splash.imageset/splash-2732x2732.png | Bin .../ios/App/App/Base.lproj/LaunchScreen.storyboard | 0 .../ios/App/App/Base.lproj/Main.storyboard | 0 .../ios/App/App/Info.plist | 0 .../ios/App/App/OutlineBridgeViewController.swift | 0 .../ios/App/Podfile | 0 .../ios/App/Podfile.lock | 0 .../package-lock.json | 0 .../package.json | 0 93 files changed, 0 insertions(+), 0 deletions(-) rename x/examples/{outline-web-wrapper => outline-pwa}/.gitignore (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/LICENSE (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/README.md (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/.gitignore (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/.gitignore (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/build.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/capacitor.build.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/proguard-rules.pro (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/AndroidManifest.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-land-hdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-land-mdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-land-xhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-land-xxhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-land-xxxhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-port-hdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-port-mdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-port-xhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-port-xxhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-port-xxxhdpi/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable/ic_launcher_background.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/drawable/splash.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/layout/activity_main.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/values/ic_launcher_background.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/values/strings.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/values/styles.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/main/res/xml/file_paths.xml (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/build.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/capacitor.settings.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/gradle.properties (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/gradlew (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/gradlew.bat (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/settings.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/android/variables.gradle (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/capacitor.config.json (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.aar (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/Info.plist (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/.gitignore (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App.xcodeproj/project.pbxproj (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App.xcworkspace/contents.xcworkspacedata (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/AppDelegate.swift (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/Contents.json (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Base.lproj/LaunchScreen.storyboard (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Base.lproj/Main.storyboard (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/Info.plist (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/App/OutlineBridgeViewController.swift (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/Podfile (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/ios/App/Podfile.lock (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/package-lock.json (100%) rename x/examples/{outline-web-wrapper => outline-pwa}/package.json (100%) diff --git a/x/examples/outline-web-wrapper/.gitignore b/x/examples/outline-pwa/.gitignore similarity index 100% rename from x/examples/outline-web-wrapper/.gitignore rename to x/examples/outline-pwa/.gitignore diff --git a/x/examples/outline-web-wrapper/LICENSE b/x/examples/outline-pwa/LICENSE similarity index 100% rename from x/examples/outline-web-wrapper/LICENSE rename to x/examples/outline-pwa/LICENSE diff --git a/x/examples/outline-web-wrapper/README.md b/x/examples/outline-pwa/README.md similarity index 100% rename from x/examples/outline-web-wrapper/README.md rename to x/examples/outline-pwa/README.md diff --git a/x/examples/outline-web-wrapper/android/.gitignore b/x/examples/outline-pwa/android/.gitignore similarity index 100% rename from x/examples/outline-web-wrapper/android/.gitignore rename to x/examples/outline-pwa/android/.gitignore diff --git a/x/examples/outline-web-wrapper/android/app/.gitignore b/x/examples/outline-pwa/android/app/.gitignore similarity index 100% rename from x/examples/outline-web-wrapper/android/app/.gitignore rename to x/examples/outline-pwa/android/app/.gitignore diff --git a/x/examples/outline-web-wrapper/android/app/build.gradle b/x/examples/outline-pwa/android/app/build.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/app/build.gradle rename to x/examples/outline-pwa/android/app/build.gradle diff --git a/x/examples/outline-web-wrapper/android/app/capacitor.build.gradle b/x/examples/outline-pwa/android/app/capacitor.build.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/app/capacitor.build.gradle rename to x/examples/outline-pwa/android/app/capacitor.build.gradle diff --git a/x/examples/outline-web-wrapper/android/app/proguard-rules.pro b/x/examples/outline-pwa/android/app/proguard-rules.pro similarity index 100% rename from x/examples/outline-web-wrapper/android/app/proguard-rules.pro rename to x/examples/outline-pwa/android/app/proguard-rules.pro diff --git a/x/examples/outline-web-wrapper/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/x/examples/outline-pwa/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java rename to x/examples/outline-pwa/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java diff --git a/x/examples/outline-web-wrapper/android/app/src/main/AndroidManifest.xml b/x/examples/outline-pwa/android/app/src/main/AndroidManifest.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/AndroidManifest.xml rename to x/examples/outline-pwa/android/app/src/main/AndroidManifest.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt b/x/examples/outline-pwa/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt rename to x/examples/outline-pwa/android/app/src/main/java/org/getoutline/pwa/MainActivity.kt diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-hdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-land-hdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-hdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-land-hdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-mdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-land-mdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-mdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-land-mdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-land-xhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxxhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-land-xxxhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-land-xxxhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-hdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-port-hdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-hdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-port-hdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-mdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-port-mdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-mdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-port-mdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-port-xhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxxhdpi/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-port-xxxhdpi/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable-port-xxxhdpi/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/x/examples/outline-pwa/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to x/examples/outline-pwa/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable/ic_launcher_background.xml b/x/examples/outline-pwa/android/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable/ic_launcher_background.xml rename to x/examples/outline-pwa/android/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/drawable/splash.png b/x/examples/outline-pwa/android/app/src/main/res/drawable/splash.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/drawable/splash.png rename to x/examples/outline-pwa/android/app/src/main/res/drawable/splash.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/layout/activity_main.xml b/x/examples/outline-pwa/android/app/src/main/res/layout/activity_main.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/layout/activity_main.xml rename to x/examples/outline-pwa/android/app/src/main/res/layout/activity_main.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to x/examples/outline-pwa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/values/ic_launcher_background.xml b/x/examples/outline-pwa/android/app/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/values/ic_launcher_background.xml rename to x/examples/outline-pwa/android/app/src/main/res/values/ic_launcher_background.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/values/strings.xml b/x/examples/outline-pwa/android/app/src/main/res/values/strings.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/values/strings.xml rename to x/examples/outline-pwa/android/app/src/main/res/values/strings.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/values/styles.xml b/x/examples/outline-pwa/android/app/src/main/res/values/styles.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/values/styles.xml rename to x/examples/outline-pwa/android/app/src/main/res/values/styles.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/main/res/xml/file_paths.xml b/x/examples/outline-pwa/android/app/src/main/res/xml/file_paths.xml similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/main/res/xml/file_paths.xml rename to x/examples/outline-pwa/android/app/src/main/res/xml/file_paths.xml diff --git a/x/examples/outline-web-wrapper/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/x/examples/outline-pwa/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java similarity index 100% rename from x/examples/outline-web-wrapper/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java rename to x/examples/outline-pwa/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java diff --git a/x/examples/outline-web-wrapper/android/build.gradle b/x/examples/outline-pwa/android/build.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/build.gradle rename to x/examples/outline-pwa/android/build.gradle diff --git a/x/examples/outline-web-wrapper/android/capacitor.settings.gradle b/x/examples/outline-pwa/android/capacitor.settings.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/capacitor.settings.gradle rename to x/examples/outline-pwa/android/capacitor.settings.gradle diff --git a/x/examples/outline-web-wrapper/android/gradle.properties b/x/examples/outline-pwa/android/gradle.properties similarity index 100% rename from x/examples/outline-web-wrapper/android/gradle.properties rename to x/examples/outline-pwa/android/gradle.properties diff --git a/x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.jar b/x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.jar rename to x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.jar diff --git a/x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.properties b/x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from x/examples/outline-web-wrapper/android/gradle/wrapper/gradle-wrapper.properties rename to x/examples/outline-pwa/android/gradle/wrapper/gradle-wrapper.properties diff --git a/x/examples/outline-web-wrapper/android/gradlew b/x/examples/outline-pwa/android/gradlew similarity index 100% rename from x/examples/outline-web-wrapper/android/gradlew rename to x/examples/outline-pwa/android/gradlew diff --git a/x/examples/outline-web-wrapper/android/gradlew.bat b/x/examples/outline-pwa/android/gradlew.bat similarity index 100% rename from x/examples/outline-web-wrapper/android/gradlew.bat rename to x/examples/outline-pwa/android/gradlew.bat diff --git a/x/examples/outline-web-wrapper/android/settings.gradle b/x/examples/outline-pwa/android/settings.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/settings.gradle rename to x/examples/outline-pwa/android/settings.gradle diff --git a/x/examples/outline-web-wrapper/android/variables.gradle b/x/examples/outline-pwa/android/variables.gradle similarity index 100% rename from x/examples/outline-web-wrapper/android/variables.gradle rename to x/examples/outline-pwa/android/variables.gradle diff --git a/x/examples/outline-web-wrapper/capacitor.config.json b/x/examples/outline-pwa/capacitor.config.json similarity index 100% rename from x/examples/outline-web-wrapper/capacitor.config.json rename to x/examples/outline-pwa/capacitor.config.json diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.aar b/x/examples/outline-pwa/generated/mobileproxy.aar similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.aar rename to x/examples/outline-pwa/generated/mobileproxy.aar diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/Info.plist b/x/examples/outline-pwa/generated/mobileproxy.xcframework/Info.plist similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/Info.plist rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/Info.plist diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Mobileproxy.objc.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/Universe.objc.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Headers/ref.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Info.plist diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Mobileproxy diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64/Mobileproxy.framework/Modules/module.modulemap diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Mobileproxy.objc.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/Universe.objc.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Headers/ref.h diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Info.plist diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Mobileproxy diff --git a/x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap b/x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap similarity index 100% rename from x/examples/outline-web-wrapper/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap rename to x/examples/outline-pwa/generated/mobileproxy.xcframework/ios-arm64_x86_64-simulator/Mobileproxy.framework/Modules/module.modulemap diff --git a/x/examples/outline-web-wrapper/ios/.gitignore b/x/examples/outline-pwa/ios/.gitignore similarity index 100% rename from x/examples/outline-web-wrapper/ios/.gitignore rename to x/examples/outline-pwa/ios/.gitignore diff --git a/x/examples/outline-web-wrapper/ios/App/App.xcodeproj/project.pbxproj b/x/examples/outline-pwa/ios/App/App.xcodeproj/project.pbxproj similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App.xcodeproj/project.pbxproj rename to x/examples/outline-pwa/ios/App/App.xcodeproj/project.pbxproj diff --git a/x/examples/outline-web-wrapper/ios/App/App.xcworkspace/contents.xcworkspacedata b/x/examples/outline-pwa/ios/App/App.xcworkspace/contents.xcworkspacedata similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App.xcworkspace/contents.xcworkspacedata rename to x/examples/outline-pwa/ios/App/App.xcworkspace/contents.xcworkspacedata diff --git a/x/examples/outline-web-wrapper/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/x/examples/outline-pwa/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to x/examples/outline-pwa/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/x/examples/outline-web-wrapper/ios/App/App/AppDelegate.swift b/x/examples/outline-pwa/ios/App/App/AppDelegate.swift similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/AppDelegate.swift rename to x/examples/outline-pwa/ios/App/App/AppDelegate.swift diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Contents.json b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Contents.json similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Contents.json rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/Contents.json diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png diff --git a/x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png rename to x/examples/outline-pwa/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png diff --git a/x/examples/outline-web-wrapper/ios/App/App/Base.lproj/LaunchScreen.storyboard b/x/examples/outline-pwa/ios/App/App/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Base.lproj/LaunchScreen.storyboard rename to x/examples/outline-pwa/ios/App/App/Base.lproj/LaunchScreen.storyboard diff --git a/x/examples/outline-web-wrapper/ios/App/App/Base.lproj/Main.storyboard b/x/examples/outline-pwa/ios/App/App/Base.lproj/Main.storyboard similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Base.lproj/Main.storyboard rename to x/examples/outline-pwa/ios/App/App/Base.lproj/Main.storyboard diff --git a/x/examples/outline-web-wrapper/ios/App/App/Info.plist b/x/examples/outline-pwa/ios/App/App/Info.plist similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/Info.plist rename to x/examples/outline-pwa/ios/App/App/Info.plist diff --git a/x/examples/outline-web-wrapper/ios/App/App/OutlineBridgeViewController.swift b/x/examples/outline-pwa/ios/App/App/OutlineBridgeViewController.swift similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/App/OutlineBridgeViewController.swift rename to x/examples/outline-pwa/ios/App/App/OutlineBridgeViewController.swift diff --git a/x/examples/outline-web-wrapper/ios/App/Podfile b/x/examples/outline-pwa/ios/App/Podfile similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/Podfile rename to x/examples/outline-pwa/ios/App/Podfile diff --git a/x/examples/outline-web-wrapper/ios/App/Podfile.lock b/x/examples/outline-pwa/ios/App/Podfile.lock similarity index 100% rename from x/examples/outline-web-wrapper/ios/App/Podfile.lock rename to x/examples/outline-pwa/ios/App/Podfile.lock diff --git a/x/examples/outline-web-wrapper/package-lock.json b/x/examples/outline-pwa/package-lock.json similarity index 100% rename from x/examples/outline-web-wrapper/package-lock.json rename to x/examples/outline-pwa/package-lock.json diff --git a/x/examples/outline-web-wrapper/package.json b/x/examples/outline-pwa/package.json similarity index 100% rename from x/examples/outline-web-wrapper/package.json rename to x/examples/outline-pwa/package.json