From 894cd8414b9b74398f0d4067e5d614f31684e3a7 Mon Sep 17 00:00:00 2001 From: Trevor Lambert <78672774+trevor-lambert@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:06:41 -0500 Subject: [PATCH 1/5] feat: add live update provider support --- .github/workflows/ci.yml | 46 +---- .github/workflows/docs-preview.yml | 10 +- .github/workflows/docs-production.yml | 12 +- .github/workflows/publish-android.yml | 7 +- .github/workflows/ui-tests.yml | 15 +- IonicPortals/build.gradle.kts | 1 - IonicPortals/consumer-rules.pro | 0 IonicPortals/proguard-rules.pro | 21 --- .../kotlin/io/ionic/portals/PortalFragment.kt | 173 ++++++++---------- .../kotlin/io/ionic/portals/PortalManager.kt | 128 +------------ .../src/main/res/drawable/ic_portals_logo.xml | 54 ------ .../main/res/layout/fragment_unregistered.xml | 63 ------- IonicPortals/src/main/res/values/strings.xml | 8 - .../java/io/ionic/portals/ExampleUnitTest.kt | 17 -- LICENSE.md | 28 +-- README.md | 14 +- TestApp/build.gradle.kts | 28 --- TestApp/proguard-rules.pro | 21 --- .../portals/testapp/InitialContextTests.kt | 12 +- .../portals/testapp/UnregisteredTests.kt | 49 ----- .../ionic/portals/testapp/TestApplication.kt | 3 +- TestAppCompose/build.gradle.kts | 30 --- TestAppCompose/proguard-rules.pro | 21 --- .../composetestapp/InitialContextTests.kt | 12 +- .../composetestapp/UnregisteredTests.kt | 61 ------ TestAppCompose/src/main/AndroidManifest.xml | 3 +- .../portals/composetestapp/TestApplication.kt | 13 -- package.json | 16 +- scripts/get-latest-capacitor-version.mjs | 28 --- 29 files changed, 136 insertions(+), 758 deletions(-) delete mode 100644 IonicPortals/consumer-rules.pro delete mode 100644 IonicPortals/proguard-rules.pro delete mode 100644 IonicPortals/src/main/res/drawable/ic_portals_logo.xml delete mode 100644 IonicPortals/src/main/res/layout/fragment_unregistered.xml delete mode 100644 IonicPortals/src/main/res/values/strings.xml delete mode 100644 IonicPortals/src/test/java/io/ionic/portals/ExampleUnitTest.kt delete mode 100644 TestApp/proguard-rules.pro delete mode 100644 TestApp/src/androidTest/java/io/ionic/portals/testapp/UnregisteredTests.kt delete mode 100644 TestAppCompose/proguard-rules.pro delete mode 100644 TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/UnregisteredTests.kt delete mode 100644 TestAppCompose/src/main/java/io/ionic/portals/composetestapp/TestApplication.kt delete mode 100644 scripts/get-latest-capacitor-version.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 938c54e..78c84ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,51 +7,21 @@ on: pull_request: branches: - '**' -# on: workflow_dispatch + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - setup: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@ce177499ccf9fd2aded3b0426c97e5434c2e8a73 - with: - access_token: ${{ secrets.GITHUB_TOKEN }} - - name: Get Latest - uses: actions/setup-node@v4 - with: - node-version: 22.x - - uses: actions/checkout@v2 - - name: Restore Dependency Cache - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} - - name: Get Package Version - id: package-version - uses: martinbeentjes/npm-get-version-action@master - with: - path: core/ - - name: set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' verify-android: runs-on: ubuntu-latest timeout-minutes: 30 - needs: - - setup steps: - - uses: actions/checkout@v2 - - name: create local.properties file for app creds - env: - PORTALS_KEY: ${{ secrets.portals_key }} - run: echo portals_key=\"$PORTALS_KEY\" > ./local.properties + - uses: actions/checkout@v7 - name: set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - run: npm run verify - working-directory: ./ \ No newline at end of file + working-directory: ./ diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index 2e64e60..24f7858 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -21,13 +21,9 @@ jobs: name: preview-${{ github.event.number }} url: https://${{ env.SLUG }}.ionicpreview.com steps: - - uses: actions/checkout@v2 - - name: create local.properties file for app creds - env: - PORTALS_KEY: ${{ secrets.portals_key }} - run: echo portals_key=\"$PORTALS_KEY\" > ./local.properties + - uses: actions/checkout@v7 - name: set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -35,7 +31,7 @@ jobs: run: chmod +x ./gradlew - name: Generate Docs run: ./gradlew dokkaHtml - - uses: aws-actions/configure-aws-credentials@v1 + - uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::319312831725:role/github-docs aws-region: us-east-1 diff --git a/.github/workflows/docs-production.yml b/.github/workflows/docs-production.yml index 342a706..98a4a96 100644 --- a/.github/workflows/docs-production.yml +++ b/.github/workflows/docs-production.yml @@ -21,13 +21,9 @@ jobs: name: production url: https://ionic.io/docs/${{ env.SLUG }} steps: - - uses: actions/checkout@v2 - - name: create local.properties file for app creds - env: - PORTALS_KEY: ${{ secrets.portals_key }} - run: echo portals_key=\"$PORTALS_KEY\" > ./local.properties + - uses: actions/checkout@v7 - name: set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -35,11 +31,11 @@ jobs: run: chmod +x ./gradlew - name: Generate Docs run: ./gradlew dokkaHtml - - uses: aws-actions/configure-aws-credentials@v1 + - uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::319312831725:role/github-docs aws-region: us-east-1 - name: Deploy run: | aws s3 sync IonicPortals/build/dokka/html/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*.html' --cache-control max-age=31536000 --only-show-errors - aws s3 sync IonicPortals/build/dokka/html/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors \ No newline at end of file + aws s3 sync IonicPortals/build/dokka/html/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index fac336c..b2d1f62 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest permissions: write-all steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - name: set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -29,7 +29,6 @@ jobs: signing.keyId=${{ secrets.ANDROID_SIGNING_KEY_ID }} signing.password=${{ secrets.ANDROID_SIGNING_PASSWORD }} signing.key=${{ secrets.ANDROID_SIGNING_KEY }} - portals_key="${{ secrets.portals_key }}" EOF echo "local.properties file has been created successfully." - name: Assemble release and add artifact to GH Release @@ -48,4 +47,4 @@ jobs: ANDROID_SIGNING_PASSWORD: ${{ secrets.ANDROID_SIGNING_PASSWORD }} ANDROID_SIGNING_KEY: ${{ secrets.ANDROID_SIGNING_KEY }} ANDROID_SONATYPE_STAGING_PROFILE_ID: ${{ secrets.ANDROID_SONATYPE_STAGING_PROFILE_ID }} - run: ./publish-android.sh \ No newline at end of file + run: ./publish-android.sh diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index c12b0e9..15a8490 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -18,15 +18,10 @@ jobs: runs-on: macos-latest steps: - name: checkout - uses: actions/checkout@v3 - - - name: create local.properties file for app creds - env: - PORTALS_KEY: ${{ secrets.portals_key }} - run: echo portals_key=\"$PORTALS_KEY\" > ./local.properties + uses: actions/checkout@v7 - name: set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -35,13 +30,13 @@ jobs: run: chmod +x ./gradlew - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v6 - name: Run build for testing run: ./gradlew assembleDebug assembleAndroidTest - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-west-2 @@ -56,4 +51,4 @@ jobs: app_type: ANDROID_APP test_type: INSTRUMENTATION test_package_file: ./TestApp/build/outputs/apk/androidTest/debug/TestApp-debug-androidTest.apk - test_package_type: INSTRUMENTATION_TEST_PACKAGE \ No newline at end of file + test_package_type: INSTRUMENTATION_TEST_PACKAGE diff --git a/IonicPortals/build.gradle.kts b/IonicPortals/build.gradle.kts index 119a5e6..37c5207 100644 --- a/IonicPortals/build.gradle.kts +++ b/IonicPortals/build.gradle.kts @@ -28,7 +28,6 @@ android { buildTypes { getByName("release") { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } compileOptions { diff --git a/IonicPortals/consumer-rules.pro b/IonicPortals/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/IonicPortals/proguard-rules.pro b/IonicPortals/proguard-rules.pro deleted file mode 100644 index ff59496..0000000 --- a/IonicPortals/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt index 2b73fdb..f539674 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt @@ -1,7 +1,6 @@ package io.ionic.portals import android.annotation.SuppressLint -import android.app.AlertDialog import android.content.pm.ApplicationInfo import android.content.res.Configuration import android.os.Bundle @@ -82,8 +81,7 @@ open class PortalFragment : Fragment { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - val layout = if(PortalManager.isRegistered()) R.layout.fragment_portal else R.layout.fragment_unregistered - return inflater.inflate(layout, container, false) + return inflater.inflate(R.layout.fragment_portal, container, false) } /** @@ -294,59 +292,48 @@ open class PortalFragment : Fragment { * Load the WebView and create the Bridge */ private fun load(savedInstanceState: Bundle?) { - if (PortalManager.isRegistered()) { - if (bridge == null) { - Logger.debug("Loading Bridge with Portal") - - val existingPortalName = savedInstanceState?.getString(PORTAL_NAME, null) - if (existingPortalName != null && portal == null) { - try { - portal = PortalManager.getPortal(existingPortalName) - } catch (e: Exception) { - Logger.warn("Attempted to reload PortalFragment from App restore but portal not found.") - Logger.warn("No portal named $existingPortalName found in PortalManager to use.") - Logger.warn("Portal reload is unsuccessful. This is likely okay and safe to ignore if your app is returning from a force quit state.") - } + if (bridge == null) { + Logger.debug("Loading Bridge with Portal") + + val existingPortalName = savedInstanceState?.getString(PORTAL_NAME, null) + if (existingPortalName != null && portal == null) { + try { + portal = PortalManager.getPortal(existingPortalName) + } catch (e: Exception) { + Logger.warn("Attempted to reload PortalFragment from App restore but portal not found.") + Logger.warn("No portal named $existingPortalName found in PortalManager to use.") + Logger.warn("Portal reload is unsuccessful. This is likely okay and safe to ignore if your app is returning from a force quit state.") } + } - if (portal != null) { - val startDir: String = portal?.startDir!! - initialPlugins.addAll(portal?.plugins!!) - initialPluginInstances.addAll(portal?.pluginInstances!!) + if (portal != null) { + val startDir: String = portal?.startDir!! + initialPlugins.addAll(portal?.plugins!!) + initialPluginInstances.addAll(portal?.pluginInstances!!) - var configToUse : CapConfig? = null - if(config != null) { - // If application is provided a programmatic config, opt to use that above all other options - configToUse = config - } + var configToUse : CapConfig? = null + if(config != null) { + // If application is provided a programmatic config, opt to use that above all other options + configToUse = config + } - var bridgeBuilder = Bridge.Builder(this) - .setInstanceState(savedInstanceState) - .setPlugins(initialPlugins) - .addPluginInstances(initialPluginInstances) - .addWebViewListeners(webViewListeners) - - if (portal?.liveUpdateConfig != null) { - liveUpdateFiles = LiveUpdateManager.getLatestAppDirectory(requireContext(), portal?.liveUpdateConfig?.appId!!) - bridgeBuilder = if (liveUpdateFiles != null) { - if (config == null) { - val configFile = File(liveUpdateFiles!!.path + "/capacitor.config.json") - if(configFile.exists()) { - configToUse = CapConfig.loadFromFile(requireContext(), liveUpdateFiles!!.path) - } - } + var bridgeBuilder = Bridge.Builder(this) + .setInstanceState(savedInstanceState) + .setPlugins(initialPlugins) + .addPluginInstances(initialPluginInstances) + .addWebViewListeners(webViewListeners) - bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.BASE_PATH, liveUpdateFiles!!.path)) - } else { - if (config == null) { - try { - val configFile = requireContext().assets.open("$startDir/capacitor.config.json") - configToUse = CapConfig.loadFromAssets(requireContext(), startDir) - } catch (_: Exception) {} + if (portal?.liveUpdateConfig != null) { + liveUpdateFiles = LiveUpdateManager.getLatestAppDirectory(requireContext(), portal?.liveUpdateConfig?.appId!!) + bridgeBuilder = if (liveUpdateFiles != null) { + if (config == null) { + val configFile = File(liveUpdateFiles!!.path + "/capacitor.config.json") + if(configFile.exists()) { + configToUse = CapConfig.loadFromFile(requireContext(), liveUpdateFiles!!.path) } - - bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.ASSET_PATH, startDir)) } + + bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.BASE_PATH, liveUpdateFiles!!.path)) } else { if (config == null) { try { @@ -355,61 +342,61 @@ open class PortalFragment : Fragment { } catch (_: Exception) {} } - bridgeBuilder = bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.ASSET_PATH, startDir)) + bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.ASSET_PATH, startDir)) } - - portal?.assetMaps?.let { - if (it.isNotEmpty()) { - bridgeBuilder = bridgeBuilder.setRouteProcessor(PortalsRouteProcessor(requireContext(),it)) - } + } else { + if (config == null) { + try { + val configFile = requireContext().assets.open("$startDir/capacitor.config.json") + configToUse = CapConfig.loadFromAssets(requireContext(), startDir) + } catch (_: Exception) {} } - if(configToUse == null) { - configToUse = CapConfig.Builder(requireContext()).setInitialFocus(false).create() + bridgeBuilder = bridgeBuilder.setServerPath(ServerPath(ServerPath.PathType.ASSET_PATH, startDir)) + } + + portal?.assetMaps?.let { + if (it.isNotEmpty()) { + bridgeBuilder = bridgeBuilder.setRouteProcessor(PortalsRouteProcessor(requireContext(),it)) } + } - // Dev mode - val isDebuggable = 0 != requireContext().applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE - if (isDebuggable && portal?.devMode == true) { - val devConfig = DevConfiguration.getCapacitorConfig(requireContext(), portal?.name!!) - if (devConfig != null) { - configToUse = devConfig - } else { - Logger.debug("No dev config set by Portals CLI for portal ${portal?.name}, loading the non-debug config") - } + if(configToUse == null) { + configToUse = CapConfig.Builder(requireContext()).setInitialFocus(false).create() + } + + // Dev mode + val isDebuggable = 0 != requireContext().applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE + if (isDebuggable && portal?.devMode == true) { + val devConfig = DevConfiguration.getCapacitorConfig(requireContext(), portal?.name!!) + if (devConfig != null) { + configToUse = devConfig + } else { + Logger.debug("No dev config set by Portals CLI for portal ${portal?.name}, loading the non-debug config") + } - val devUrl = DevConfiguration.getServerUrl(requireContext(), portal?.name!!) - if (devUrl != null && configToUse != null) { - val devModeField: Field = configToUse.javaClass.getDeclaredField("serverUrl") - devModeField.isAccessible = true - devModeField.set(configToUse, devUrl) + val devUrl = DevConfiguration.getServerUrl(requireContext(), portal?.name!!) + if (devUrl != null && configToUse != null) { + val devModeField: Field = configToUse.javaClass.getDeclaredField("serverUrl") + devModeField.isAccessible = true + devModeField.set(configToUse, devUrl) + } else { + val noDevUrlMsg = "No dev URL set by Portals CLI for portal ${portal?.name}" + if (devConfig != null && devConfig.serverUrl != null) { + Logger.debug("$noDevUrlMsg, using URL from dev config") } else { - val noDevUrlMsg = "No dev URL set by Portals CLI for portal ${portal?.name}" - if (devConfig != null && devConfig.serverUrl != null) { - Logger.debug("$noDevUrlMsg, using URL from dev config") - } else { - Logger.debug("$noDevUrlMsg, loading Portal from assets") - } + Logger.debug("$noDevUrlMsg, loading Portal from assets") } } + } - bridgeBuilder = bridgeBuilder.setConfig(configToUse) - bridge = bridgeBuilder.create() + bridgeBuilder = bridgeBuilder.setConfig(configToUse) + bridge = bridgeBuilder.create() - setupPortalsJS() - keepRunning = bridge?.shouldKeepRunning()!! + setupPortalsJS() + keepRunning = bridge?.shouldKeepRunning()!! - onBridgeAvailable?.let { onBridgeAvailable -> bridge?.let { bridge -> onBridgeAvailable(bridge)} } - } - } - } else if (PortalManager.isRegisteredError()) { - if(activity != null) { - val alert = AlertDialog.Builder(activity) - alert.setMessage(getString(R.string.invalid_portals_key)) - alert.setPositiveButton("OK") { dialog, _ -> - dialog.dismiss() - } - alert.show() + onBridgeAvailable?.let { onBridgeAvailable -> bridge?.let { bridge -> onBridgeAvailable(bridge)} } } } } @@ -516,4 +503,4 @@ open class PortalFragment : Fragment { } } } -} \ No newline at end of file +} diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt index 58a2969..c296d31 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt @@ -1,12 +1,5 @@ package io.ionic.portals -import android.util.Base64 -import android.util.Log -import java.security.KeyFactory -import java.security.PublicKey -import java.security.Signature -import java.security.spec.X509EncodedKeySpec - /** * A class used to create and manage [Portal] instances. It follows a [Singleton Pattern](https://en.wikipedia.org/wiki/Singleton_pattern) * to allow access to any [Portal](./portal) from anywhere in the application. @@ -34,12 +27,6 @@ import java.security.spec.X509EncodedKeySpec object PortalManager { @JvmStatic private val portals: MutableMap = mutableMapOf() - @JvmStatic - private var registered: Boolean = false - @JvmStatic - private var unregisteredMessageShown: Boolean = false - @JvmStatic - private var registeredError: Boolean = false /** * Adds a Portal to the Portal Manager. This is not necessary if the Portal is created using @@ -50,10 +37,6 @@ object PortalManager { @JvmStatic fun addPortal(portal: Portal) { portals[portal.name] = portal - - if (!registered && !unregisteredMessageShown) { - displayUnregisteredMessage() - } } /** @@ -74,10 +57,6 @@ object PortalManager { */ @JvmStatic fun getPortal(name: String): Portal { - if (registeredError) { - registrationError() - } - return portals[name] ?: throw IllegalStateException("Portal with portalId $name not found in PortalManager") } @@ -114,34 +93,24 @@ object PortalManager { } /** - * Validate this copy of Portals with an API key. This function works offline and only needs to - * be run once before creating your first [Portal]. - * - * Example usage (kotlin): - * ```kotlin - * PortalManager.register("YOUR_PORTALS_KEY") - * ``` + * Portals registration is no longer required. This function is retained for source + * compatibility and has no effect. * - * Example usage (java): - * ```java - * PortalManager.register("YOUR_PORTALS_KEY"); - * ``` - * - * @param key The key for Portals provided by the Ionic dashboard + * @param key A previously required Portals registration key. */ + @Deprecated("Portals registration is no longer required. This method has no effect.") @JvmStatic - fun register(key: String) { - registered = verify(key) - } + fun register(key: String) {} /** - * Check if Portals has been successfully registered with a valid key. + * Portals registration is no longer required. * - * @return true if Portals is successfully registered + * @return true. */ + @Deprecated("Portals registration is no longer required. This method always returns true.") @JvmStatic fun isRegistered(): Boolean { - return registered + return true } /** @@ -169,81 +138,4 @@ object PortalManager { this.addPortal(portal) }) } - - /** - * Verifies the provided registration key string against the Portals public key. - * - * @param key: The Portals registration key to validate - * @return True if validation was successful, false if not. - */ - private fun verify(key: String): Boolean { - val jwtDelimiter = '.' - val PUBLIC_KEY = - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+gMC3aJVGX4ha5asmEF" + - "TfP0FTFQlCD8d/J+dhp5dpx3ErqSReru0QSUaCRCEGV/ZK3Vp5lnv1cREQDG5H/t" + - "Xm9Ao06b0QJYtsYhcPgRUU9awDI7jRKueXyAq4zAx0RHZlmOsTf/cNwRnmRnkyJP" + - "a21mLNClmdPlhWjS6AHjaYe79ieAsftFA+QodtzoCo+w9A9YCvc6ngGOFoLIIbzs" + - "jv6h9ES27mi5BUqhoHsetS4u3/pCbsV2U3z255gtjANtdIX/c5inepLuAjyc1aPz" + - "2eu4TbzabvJnmNStje82NW36Qij1mupc4e7dYaq0aMNQyHSWk1/CuIcqEYlnK1mb" + - "kQIDAQAB" - - try { - val publicBytes: ByteArray = Base64.decode(PUBLIC_KEY, Base64.DEFAULT) - val keySpec = X509EncodedKeySpec(publicBytes) - val keyFactory: KeyFactory = KeyFactory.getInstance("RSA") - val pubKey: PublicKey = keyFactory.generatePublic(keySpec) - - val parts = key.trim().split(jwtDelimiter) - return if (parts.size == 3) { - val header = parts[0].toByteArray(Charsets.UTF_8) - val payload = parts[1].toByteArray(Charsets.UTF_8) - val tokenSignature = Base64.decode(parts[2], Base64.URL_SAFE) - - val rsaSignature = Signature.getInstance("SHA256withRSA") - rsaSignature.initVerify(pubKey) - rsaSignature.update(header) - rsaSignature.update(jwtDelimiter.code.toByte()) - rsaSignature.update(payload) - - val result = rsaSignature.verify(tokenSignature) - if (!result) { - registrationError() - } - - result - } else { - registrationError() - false - } - } catch (_: Exception) { - registrationError() - } - - return false - } - - /** - * Display an error log to warn the developer that Portals is unregistered. - */ - private fun displayUnregisteredMessage() { - unregisteredMessageShown = true - Log.e("Portals", "Don't forget to register your copy of portals! Register at: ionic.io/register-portals") - } - - /** - * Display an error log to warn the developer that Portals registration failed. - */ - private fun registrationError() { - registeredError = true - Log.e("Portals", "Error validating your key for Ionic Portals. Check your key and try again.") - } - - /** - * Check if there is a Portals registration issue. - * - * @return true if there is a Portals registration error - */ - internal fun isRegisteredError(): Boolean { - return registeredError - } -} \ No newline at end of file +} diff --git a/IonicPortals/src/main/res/drawable/ic_portals_logo.xml b/IonicPortals/src/main/res/drawable/ic_portals_logo.xml deleted file mode 100644 index 03d1388..0000000 --- a/IonicPortals/src/main/res/drawable/ic_portals_logo.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/IonicPortals/src/main/res/layout/fragment_unregistered.xml b/IonicPortals/src/main/res/layout/fragment_unregistered.xml deleted file mode 100644 index bd87489..0000000 --- a/IonicPortals/src/main/res/layout/fragment_unregistered.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/IonicPortals/src/main/res/values/strings.xml b/IonicPortals/src/main/res/values/strings.xml deleted file mode 100644 index 7ac3796..0000000 --- a/IonicPortals/src/main/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Register Product - Please register in order to use Ionic Portals. You can do so at: - ionic.io/register-portals - Ionic Portals Logo - Error validating your key for Ionic Portals. Check your key and try again. - \ No newline at end of file diff --git a/IonicPortals/src/test/java/io/ionic/portals/ExampleUnitTest.kt b/IonicPortals/src/test/java/io/ionic/portals/ExampleUnitTest.kt deleted file mode 100644 index 6ccd803..0000000 --- a/IonicPortals/src/test/java/io/ionic/portals/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.ionic.portals - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index aad03b6..0d5a273 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ -# Ionic Portals License 1.0.0 +# Ionic Portals License 2.0.0 -Based on the text of the licenses in the Polyform Project with new account registration conditions. +Based on the text of the licenses in the Polyform Project with new conditions requiring a separate commercial agreement. ## Acceptance @@ -8,25 +8,25 @@ In order to get any license under these terms, you must agree to them as both st ## Copyright License -The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it, but only so long as you comply with the Account Registration Feature requirement below. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license). +The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it, but only so long as you comply with the Separate Commercial Agreement requirement below. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license). -## Account Registration Feature +## Separate Commercial Agreement -You must preserve, and you must not remove or modify, the Account Registration Feature in the software. +You must have a valid and active Separate Commercial Agreement in place with Drifty Co., d/b/a Ionic, under which you have purchased or otherwise received rights to use Ionic Portals. ## Distribution License -The licensor grants you an additional copyright license to distribute copies of the software, but only so long as you comply with the Account Registration Feature requirement above and the Notice requirement below. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license). +The licensor grants you an additional copyright license to distribute copies of the software, but only so long as you comply with the Separate Commercial Agreement requirement above and the Notice requirement below. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license). ## Notices -You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example: +You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example: > Required Notice: Copyright Yoyodyne, Inc. (http://example.com) ## Changes and New Works License -The licensor grants you an additional copyright license to make changes and new works based on the software, but only so long as you comply with the Account Registration Feature requirement above. +The licensor grants you an additional copyright license to make changes and new works based on the software, but only so long as you comply with the Separate Commercial Agreement requirement above. ## Patent License @@ -38,7 +38,7 @@ You may have "fair use" rights for the software under the law. These terms do no ## No Other Rights -These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses. +These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses. ## Patent Defense @@ -46,22 +46,24 @@ If you make any written claim that the software infringes or contributes to infr ## Violations -The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 30 days of receiving notice. Otherwise, all your licenses end immediately. +The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 30 days of receiving notice. Otherwise, all your licenses end immediately. ## No Liability ***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.*** +## Order of Precedence + +To the extent there is a conflict between the scope of license rights defined in the Separate Commercial Agreement and in this license, the Separate Commercial Agreement shall take precedence. + ## Definitions The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms. **You** refers to the individual or entity agreeing to these terms. -**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect. +**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect. **Your licenses** are all the licenses granted to you for the software under these terms. **Use** means anything you do with the software requiring one of your licenses. - -**Account Registration Feature** is the portion of the software marked by the licensor as the Account Registration Feature in the software. The Account Registration Feature may be a license key, token, or any other such mechanism designated by the licensor. diff --git a/README.md b/README.md index 0eeceef..f4507ae 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Ionic Portals is a supercharged native Web View component for iOS and Android th See our docs to [get started with Portals](https://ionic.io/docs/portals/getting-started/guide). -## Registration +## License -The Ionic Portals library for Android and iOS requires a license key to use. Once you have integrated Portals into your project, login to your ionic account to get a key. See our doc on [how to register and get your Portals license key](https://ionic.io/docs/portals/how-to/get-a-product-key) and refer to the [Android](https://ionic.io/docs/portals/getting-started/android) or [iOS](https://ionic.io/docs/portals/getting-started/iOS) getting started guides to see where to add your key. +Use of Ionic Portals requires a valid and active commercial agreement with Ionic. ## FAQ @@ -48,13 +48,3 @@ See our [license](https://github.com/ionic-team/ionic-portals/blob/main/LICENSE. Ionic Portals is a solution that lets you add web-based experiences to your native mobile apps. Portals uses [Capacitor](https://capacitorjs.com) as a bridge between the native code and the web code to allow for cross-communication between the two layers. Because Portals uses Capacitor under the hood, you are able to use any existing [Capacitor Plugins](https://capacitorjs.com/docs/plugins) while continuing to use your existing native workflow. [Ionic Framework](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to build top quality native and progressive web apps with web technologies. Your web experiences can be developed with Ionic, but it is not necessary to use Portals. - -## Testing - -The test projects within the repository will only work with a valid Portals key. Add the following new line to the `local.properties` file in the project root and enter your Portals key. - -``` -portals_key=YOUR_PORTALS_KEY -``` - -Note: This file is in the `.gitignore` and is not committed to repos by default. \ No newline at end of file diff --git a/TestApp/build.gradle.kts b/TestApp/build.gradle.kts index 886004a..c018d05 100644 --- a/TestApp/build.gradle.kts +++ b/TestApp/build.gradle.kts @@ -1,7 +1,4 @@ -import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import java.io.FileInputStream -import com.android.build.api.variant.BuildConfigField plugins { id("com.android.application") @@ -12,10 +9,6 @@ android { namespace = "io.ionic.portals.testapp" compileSdk = 36 - buildFeatures { - buildConfig = true - } - defaultConfig { applicationId = "io.ionic.portals.testapp" minSdk = 24 @@ -29,7 +22,6 @@ android { buildTypes { release { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } @@ -45,12 +37,6 @@ kotlin { } } -androidComponents { - onVariants { - it.buildConfigFields?.put("PORTALS_KEY", BuildConfigField("String", getPortalsKey(), "portals registration key")) - } -} - dependencies { implementation(project(":IonicPortals")) implementation("androidx.core:core-ktx:1.12.0") @@ -65,17 +51,3 @@ dependencies { androidTestImplementation("androidx.test:runner:1.5.2") androidTestImplementation("androidx.test:rules:1.5.0") } - -fun getPortalsKey(): String { - val propFile = rootProject.file("local.properties") - val properties = Properties() - properties.load(FileInputStream(propFile)) - val raw = properties.getProperty("portals_key") ?: "" - val normalized = if (raw.length >= 2 && raw.first() == '"' && raw.last() == '"') { - raw.substring(1, raw.length - 1) - } else { - raw - } - val escaped = normalized.replace("\\", "\\\\").replace("\"", "\\\"") - return "\"$escaped\"" -} diff --git a/TestApp/proguard-rules.pro b/TestApp/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/TestApp/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/TestApp/src/androidTest/java/io/ionic/portals/testapp/InitialContextTests.kt b/TestApp/src/androidTest/java/io/ionic/portals/testapp/InitialContextTests.kt index d060a95..4f44a6c 100644 --- a/TestApp/src/androidTest/java/io/ionic/portals/testapp/InitialContextTests.kt +++ b/TestApp/src/androidTest/java/io/ionic/portals/testapp/InitialContextTests.kt @@ -11,10 +11,8 @@ import androidx.test.espresso.web.webdriver.DriverAtoms.findElement import androidx.test.espresso.web.webdriver.DriverAtoms.getText import androidx.test.espresso.web.webdriver.Locator import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.ionic.portals.PortalManager import org.hamcrest.CoreMatchers.containsString import org.junit.Before -import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith @@ -44,12 +42,4 @@ class InitialContextTests { val script = script("return window.AndroidInitialContext.initialContext();", castOrDie(String::class.java)) onWebView().check(webMatches(script, containsString("testportal"))) } - - companion object { - @JvmStatic - @BeforeClass - fun classSetUp() { - PortalManager.register(BuildConfig.PORTALS_KEY) - } - } -} \ No newline at end of file +} diff --git a/TestApp/src/androidTest/java/io/ionic/portals/testapp/UnregisteredTests.kt b/TestApp/src/androidTest/java/io/ionic/portals/testapp/UnregisteredTests.kt deleted file mode 100644 index 65b794a..0000000 --- a/TestApp/src/androidTest/java/io/ionic/portals/testapp/UnregisteredTests.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.ionic.portals.testapp - -import androidx.test.core.app.ActivityScenario -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.matcher.RootMatchers.isDialog -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.ionic.portals.PortalManager -import org.junit.Before -import org.junit.BeforeClass -import org.junit.Test -import org.junit.runner.RunWith - - -@RunWith(AndroidJUnit4::class) -class UnregisteredTests { - private lateinit var scenario: ActivityScenario - - @Before - fun setUp() { - scenario = ActivityScenario.launch(MainActivity::class.java) - } - - @Test - fun when_portals_is_not_registered__display_unregistered_view() { - onView(withText("OK")).inRoot(isDialog()).perform(click()) - - // Verify that the unregistered view is displayed - onView(withText(io.ionic.portals.R.string.unregistered_text)).check(matches(isDisplayed())) - } - - @Test - fun when_portals_is_registered_with_bad_key__display_error_dialog() { - onView(withText(io.ionic.portals.R.string.invalid_portals_key)) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - } - - companion object { - @JvmStatic - @BeforeClass - fun classSetUp() { - PortalManager.register("this is a bad key") - } - } -} diff --git a/TestApp/src/main/java/io/ionic/portals/testapp/TestApplication.kt b/TestApp/src/main/java/io/ionic/portals/testapp/TestApplication.kt index a3a206e..cb844ef 100644 --- a/TestApp/src/main/java/io/ionic/portals/testapp/TestApplication.kt +++ b/TestApp/src/main/java/io/ionic/portals/testapp/TestApplication.kt @@ -8,7 +8,6 @@ class TestApplication: Application() { override fun onCreate() { super.onCreate() - PortalManager.register(BuildConfig.PORTALS_KEY) PortalManager.newPortal("testportal").create() } -} \ No newline at end of file +} diff --git a/TestAppCompose/build.gradle.kts b/TestAppCompose/build.gradle.kts index 58535ca..2746a56 100644 --- a/TestAppCompose/build.gradle.kts +++ b/TestAppCompose/build.gradle.kts @@ -1,7 +1,4 @@ -import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import java.io.FileInputStream -import com.android.build.api.variant.BuildConfigField plugins { id("com.android.application") @@ -13,10 +10,6 @@ android { namespace = "io.ionic.portals.composetestapp" compileSdk = 36 - buildFeatures { - buildConfig = true - } - defaultConfig { applicationId = "io.ionic.portals.composetestapp" minSdk = 24 @@ -33,7 +26,6 @@ android { buildTypes { release { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } compileOptions { @@ -56,14 +48,6 @@ kotlin { } } -androidComponents { - onVariants { - it.buildConfigFields?.put("PORTALS_KEY", - BuildConfigField("String", getPortalsKey(), "portals registration key") - ) - } -} - dependencies { implementation(project(":IonicPortals")) implementation("androidx.core:core-ktx:1.12.0") @@ -84,17 +68,3 @@ dependencies { debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") } - -fun getPortalsKey(): String { - val propFile = rootProject.file("local.properties") - val properties = Properties() - properties.load(FileInputStream(propFile)) - val raw = properties.getProperty("portals_key") ?: "" - val normalized = if (raw.length >= 2 && raw.first() == '"' && raw.last() == '"') { - raw.substring(1, raw.length - 1) - } else { - raw - } - val escaped = normalized.replace("\\", "\\\\").replace("\"", "\\\"") - return "\"$escaped\"" -} diff --git a/TestAppCompose/proguard-rules.pro b/TestAppCompose/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/TestAppCompose/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/InitialContextTests.kt b/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/InitialContextTests.kt index 156c970..d95ea94 100644 --- a/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/InitialContextTests.kt +++ b/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/InitialContextTests.kt @@ -11,10 +11,8 @@ import androidx.test.espresso.web.webdriver.DriverAtoms.findElement import androidx.test.espresso.web.webdriver.DriverAtoms.getText import androidx.test.espresso.web.webdriver.Locator import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.ionic.portals.PortalManager import org.hamcrest.CoreMatchers.containsString import org.junit.Before -import org.junit.BeforeClass import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -50,12 +48,4 @@ class InitialContextTests { val script = script("return window.AndroidInitialContext.initialContext();", castOrDie(String::class.java)) onWebView().check(webMatches(script, containsString("testportal"))) } - - companion object { - @JvmStatic - @BeforeClass - fun classSetUp() { - PortalManager.register(BuildConfig.PORTALS_KEY) - } - } -} \ No newline at end of file +} diff --git a/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/UnregisteredTests.kt b/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/UnregisteredTests.kt deleted file mode 100644 index cbb522c..0000000 --- a/TestAppCompose/src/androidTest/java/io/ionic/portals/composetestapp/UnregisteredTests.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.ionic.portals.composetestapp - -import android.content.Intent -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.test.core.app.ActivityScenario -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.ionic.portals.PortalManager -import org.junit.Before -import org.junit.BeforeClass -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - - -@RunWith(AndroidJUnit4::class) -class UnregisteredTests { - - @get:Rule - val composeTestRule = createAndroidComposeRule() - - @Before - fun setUp() { - ActivityScenario.launch(MainActivity::class.java).onActivity { activity -> - activity.sendBroadcast( - Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - ) - }; - } - - @Test - fun when_portals_is_not_registered__display_unregistered_view() { - composeTestRule.waitForIdle() - - // Dismiss the invalid key dialog when it is displayed - onView(withText("OK")).perform(click()) - - // Verify that the unregistered view is displayed - onView(withText(io.ionic.portals.R.string.unregistered_text)).check(matches(isDisplayed())) - } - - @Test - fun when_portals_is_registered_with_bad_key__display_error_dialog() { - composeTestRule.waitForIdle() - - // Recreate the activity to trigger the dialog - onView(withText(io.ionic.portals.R.string.invalid_portals_key)).check(matches(isDisplayed())) - } - - companion object { - @JvmStatic - @BeforeClass - fun classSetUp() { - PortalManager.register("this is a bad key") - } - } -} \ No newline at end of file diff --git a/TestAppCompose/src/main/AndroidManifest.xml b/TestAppCompose/src/main/AndroidManifest.xml index 142da2b..f6d3ada 100644 --- a/TestAppCompose/src/main/AndroidManifest.xml +++ b/TestAppCompose/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:name=".TestApplication" android:theme="@style/Theme.IonicPortals"> - \ No newline at end of file + diff --git a/TestAppCompose/src/main/java/io/ionic/portals/composetestapp/TestApplication.kt b/TestAppCompose/src/main/java/io/ionic/portals/composetestapp/TestApplication.kt deleted file mode 100644 index 7d1635d..0000000 --- a/TestAppCompose/src/main/java/io/ionic/portals/composetestapp/TestApplication.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ionic.portals.composetestapp - -import android.app.Application -import io.ionic.portals.PortalManager - -class TestApplication: Application() { - - override fun onCreate() { - super.onCreate() - - PortalManager.register(BuildConfig.PORTALS_KEY) - } -} \ No newline at end of file diff --git a/package.json b/package.json index 28dd58f..9000081 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,8 @@ { "name": "ionic-portals-android", "version": "0.13.0", - "description": "Ionic Portals", - "homepage": "https://ionic.io/portals", - "author": "Ionic Team (https://ionic.io)", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/ionic-team/ionic-portals.git" - }, - "bugs": { - "url": "https://github.com/ionic-team/ionic-portals/issues" - }, - "files": [], + "private": true, "scripts": { "verify": "./gradlew :IonicPortals:clean :IonicPortals:build :IonicPortals:test" - }, - "private": true + } } diff --git a/scripts/get-latest-capacitor-version.mjs b/scripts/get-latest-capacitor-version.mjs deleted file mode 100644 index e19b225..0000000 --- a/scripts/get-latest-capacitor-version.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import https from 'https' -import fs from 'fs' - -const options = { - hostname: 'registry.npmjs.org', - path: '/@capacitor/core/', - method: 'GET' -} - -const req = https.request(options, res => { - let json = '' - res.on('data', d => { - json += d - }) - - res.on('close', () => { - const latestVersion = JSON.parse(json)['dist-tags'].latest; - const lernaConfig = JSON.parse(fs.readFileSync('./lerna.json', 'utf-8')) - lernaConfig.capacitorVersion = latestVersion - fs.writeFileSync('./lerna.json', JSON.stringify(lernaConfig, null, 2) + '\n') - }) -}) - -req.on('error', error => { - console.error(error) -}) - -req.end() From 974ed20d24d4881aa149bd06a66c12b4cc0dc0de Mon Sep 17 00:00:00 2001 From: Trevor Lambert <78672774+trevor-lambert@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:15:16 -0500 Subject: [PATCH 2/5] chore: update ci --- .github/workflows/ci.yml | 6 ++---- .github/workflows/publish-android.yml | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78c84ea..936de7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,10 @@ name: CI on: - push: - branches: - - '**' pull_request: + push: branches: - - '**' + - main concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index b2d1f62..6ba2223 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -8,7 +8,8 @@ on: jobs: publish-android: runs-on: ubuntu-latest - permissions: write-all + permissions: + contents: write steps: - uses: actions/checkout@v7 - name: set up JDK 17 From 2efeef4c42430d5a200276341dd3e1f5426c221e Mon Sep 17 00:00:00 2001 From: Trevor Lambert <78672774+trevor-lambert@users.noreply.github.com> Date: Mon, 22 Jun 2026 11:08:35 -0500 Subject: [PATCH 3/5] chore: upgrade to dokka v2 --- .github/workflows/docs-preview.yml | 2 +- .github/workflows/docs-production.yml | 2 +- build.gradle.kts | 5 ----- gradle.properties | 4 +++- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index 24f7858..ddf70a3 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -30,7 +30,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x ./gradlew - name: Generate Docs - run: ./gradlew dokkaHtml + run: ./gradlew :IonicPortals:dokkaGeneratePublicationHtml - uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::319312831725:role/github-docs diff --git a/.github/workflows/docs-production.yml b/.github/workflows/docs-production.yml index 98a4a96..ad86abc 100644 --- a/.github/workflows/docs-production.yml +++ b/.github/workflows/docs-production.yml @@ -30,7 +30,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x ./gradlew - name: Generate Docs - run: ./gradlew dokkaHtml + run: ./gradlew :IonicPortals:dokkaGeneratePublicationHtml - uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: arn:aws:iam::319312831725:role/github-docs diff --git a/build.gradle.kts b/build.gradle.kts index 6e199b8..f6fd25a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,8 +41,3 @@ allprojects { apply(plugin = "org.jetbrains.dokka") } - -// register Clean task -tasks.register("clean").configure { - delete("build") -} diff --git a/gradle.properties b/gradle.properties index e2dd8f6..3ab174c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx2048m +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -14,4 +16,4 @@ org.gradle.jvmargs=-Xmx2048m # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true From 60853a63addc93726bef432155aee2f545663ed8 Mon Sep 17 00:00:00 2001 From: Trevor Lambert <78672774+trevor-lambert@users.noreply.github.com> Date: Mon, 22 Jun 2026 11:18:57 -0500 Subject: [PATCH 4/5] fix: docs preview --- .github/workflows/docs-preview.yml | 3 ++- .github/workflows/docs-production.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index ddf70a3..22eb65b 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -12,6 +12,7 @@ permissions: env: SLUG: portals-android-api-ref-${{ github.event.number }}-${{ github.run_id }} + DOCS_PATH: portals-android-api-ref jobs: deploy: @@ -19,7 +20,7 @@ jobs: timeout-minutes: 15 environment: name: preview-${{ github.event.number }} - url: https://${{ env.SLUG }}.ionicpreview.com + url: https://${{ env.SLUG }}.ionicpreview.com/docs/${{ env.DOCS_PATH }}/ steps: - uses: actions/checkout@v7 - name: set up JDK 17 diff --git a/.github/workflows/docs-production.yml b/.github/workflows/docs-production.yml index ad86abc..8a13a41 100644 --- a/.github/workflows/docs-production.yml +++ b/.github/workflows/docs-production.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 15 environment: name: production - url: https://ionic.io/docs/${{ env.SLUG }} + url: https://ionic.io/docs/${{ env.SLUG }}/ steps: - uses: actions/checkout@v7 - name: set up JDK 17 From 1fdc14f43584f8c32ac737ace44f0af4ebefa57f Mon Sep 17 00:00:00 2001 From: Trevor Lambert <78672774+trevor-lambert@users.noreply.github.com> Date: Mon, 22 Jun 2026 11:22:53 -0500 Subject: [PATCH 5/5] chore: update ci --- .github/workflows/docs-preview.yml | 1 + .github/workflows/ui-tests.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index 22eb65b..3ad1ce1 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -3,6 +3,7 @@ name: Deploy docs PR preview on: pull_request: paths: + - '.github/workflows/docs-preview.yml' - 'IonicPortals/src/main/**' permissions: diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 15a8490..2356547 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -3,7 +3,7 @@ name: Run UI Tests on: pull_request: paths: - - './github/workflows/ui-tests.yml' + - '.github/workflows/ui-tests.yml' - 'IonicPortals/**' - 'TestApp/**' - 'TestAppCompose/**'