diff --git a/IonicPortals/build.gradle.kts b/IonicPortals/build.gradle.kts index 37c5207..8364e1c 100644 --- a/IonicPortals/build.gradle.kts +++ b/IonicPortals/build.gradle.kts @@ -49,6 +49,7 @@ dependencies { implementation(kotlin("reflect")) api("com.capacitorjs:core:[8.0.0,9.0.0)") + api("io.ionic:liveupdateprovider:0.1.0") compileOnly("io.ionic:liveupdates:0.5.5") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/Portal.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/Portal.kt index f5f7abe..4a04416 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/Portal.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/Portal.kt @@ -1,9 +1,15 @@ package io.ionic.portals import android.content.Context +import android.util.Log import com.getcapacitor.Plugin +import io.ionic.liveupdateprovider.LiveUpdateProviderError +import io.ionic.liveupdateprovider.LiveUpdateProviderManager +import io.ionic.liveupdateprovider.LiveUpdateProviderSyncCallback +import io.ionic.liveupdateprovider.LiveUpdateProviderSyncResult import io.ionic.liveupdates.LiveUpdate import io.ionic.liveupdates.LiveUpdateManager +import java.io.File /** * A class representing a Portal that contains information about the web content to load and any @@ -25,6 +31,21 @@ import io.ionic.liveupdates.LiveUpdateManager * @property name the name of the Portal */ class Portal(val name: String) { + /** + * The live update source for a [Portal]. + */ + sealed class LiveUpdateSource { + /** + * Uses Ionic Live Updates to sync and locate the latest web application assets. + */ + data class Ionic(val liveUpdateConfig: LiveUpdate) : LiveUpdateSource() + + /** + * Uses a [LiveUpdateProviderManager] to sync and locate the latest web application assets. + */ + data class Provider(val manager: LiveUpdateProviderManager) : LiveUpdateSource() + } + /** * Capacitor [Plugin] registered with the Portal. */ @@ -73,22 +94,30 @@ class Portal(val name: String) { var devMode: Boolean = true /** - * A LiveUpdate config, if live updates is being used. + * The live update source for this Portal. + * + * Use [LiveUpdateSource.Ionic] for Ionic Live Updates, or [LiveUpdateSource.Provider] for + * an external provider built with the Live Update Provider SDK. */ - var liveUpdateConfig: LiveUpdate? = null + var liveUpdateSource: LiveUpdateSource? = null set(value) { field = value - if (value != null) { - if(value.assetPath == null) { - value.assetPath = this.startDir - } + if (value is LiveUpdateSource.Ionic && value.liveUpdateConfig.assetPath == null) { + value.liveUpdateConfig.assetPath = this.startDir } } /** - * Whether to run a live update sync when the portal is added to the manager. + * The directory of the latest synced web application assets for this Portal. + * Returns null when no live update source is configured or no sync has completed. */ - var liveUpdateOnAppLoad: Boolean = true + fun latestAppDirectory(context: Context): File? { + return when (val source = liveUpdateSource) { + is LiveUpdateSource.Ionic -> LiveUpdateManager.getLatestAppDirectory(context, source.liveUpdateConfig.appId) + is LiveUpdateSource.Provider -> source.manager.latestAppDirectory + null -> null + } + } /** * Add a Capacitor [Plugin] to be loaded with this Portal. @@ -308,7 +337,7 @@ class PortalBuilder(val name: String) { private var initialContext: Any? = null private var portalFragmentType: Class = PortalFragment::class.java private var onCreate: (portal: Portal) -> Unit = {} - private var liveUpdateConfig: LiveUpdate? = null + private var liveUpdateSource: Portal.LiveUpdateSource? = null private var devMode: Boolean = true internal constructor(name: String, onCreate: (portal: Portal) -> Unit) : this(name) { @@ -526,31 +555,32 @@ class PortalBuilder(val name: String) { } /** - * Set the [LiveUpdate] config if using the Live Updates SDK with Portals. + * Set the [LiveUpdate] config if using Ionic Live Updates with Portals. * * Example usage (kotlin): * ```kotlin * val liveUpdateConfig = LiveUpdate("appId", "production") - * builder = builder.setLiveUpdateConfig(liveUpdateConfig) + * builder = builder.setLiveUpdateConfig(context, liveUpdateConfig) * ``` * * Example usage (java): * ```java * LiveUpdate liveUpdateConfig = new LiveUpdate("appId", "production"); - * builder = builder.setLiveUpdateConfig(liveUpdateConfig); + * builder = builder.setLiveUpdateConfig(context, liveUpdateConfig); * ``` * - * @param context the Android [Context] used with Live Update configuration - * @param liveUpdateConfig the Live Update config object - * @param updateOnAppLoad if a Live Update sync should occur as soon as the Portal loads - * @return the instance of the PortalBuilder with the Live Update config set + * @param context the Android [Context] used with Ionic Live Updates configuration. + * @param liveUpdateConfig the Ionic Live Updates config object. + * @param updateOnAppLoad whether to start an Ionic Live Updates sync when the Portal is configured. + * @return the instance of the PortalBuilder with the Ionic Live Updates config set. */ @JvmOverloads fun setLiveUpdateConfig(context: Context, liveUpdateConfig: LiveUpdate, updateOnAppLoad: Boolean = true): PortalBuilder { - this.liveUpdateConfig = liveUpdateConfig + requireNoLiveUpdateSource() if(liveUpdateConfig.assetPath == null) { liveUpdateConfig.assetPath = this._startDir ?: this.name } + this.liveUpdateSource = Portal.LiveUpdateSource.Ionic(liveUpdateConfig) LiveUpdateManager.initialize(context) LiveUpdateManager.cleanVersions(context, liveUpdateConfig.appId) @@ -561,6 +591,57 @@ class PortalBuilder(val name: String) { return this } + /** + * Set a live update provider manager to be used with the Portal. + * + * Example usage (kotlin): + * ```kotlin + * builder = builder.setLiveUpdateProviderManager(providerManager) + * ``` + * + * Example usage (java): + * ```java + * builder = builder.setLiveUpdateProviderManager(providerManager); + * ``` + * + * To trigger a provider sync after the Portal is created, use the provider manager directly. + * + * @param liveUpdateProviderManager the external live update provider manager. + * @param updateOnAppLoad whether to start an external provider sync when the Portal is configured. + * @return the instance of the PortalBuilder with the external live update provider manager set. + */ + @JvmOverloads + fun setLiveUpdateProviderManager( + liveUpdateProviderManager: LiveUpdateProviderManager, + updateOnAppLoad: Boolean = true + ): PortalBuilder { + requireNoLiveUpdateSource() + this.liveUpdateSource = Portal.LiveUpdateSource.Provider(liveUpdateProviderManager) + if (updateOnAppLoad) { + liveUpdateProviderManager.sync( + callback = object : LiveUpdateProviderSyncCallback { + override fun onSuccess(result: LiveUpdateProviderSyncResult) { + Log.d( + "PortalBuilder", + "Live Update sync complete. Latest app dir: ${liveUpdateProviderManager.latestAppDirectory}" + ) + } + + override fun onFailure(error: LiveUpdateProviderError.SyncFailed) { + Log.e("PortalBuilder", "Live Update sync failed: ${error.message}") + } + } + ) + } + return this + } + + private fun requireNoLiveUpdateSource() { + check(liveUpdateSource == null) { + "A live update source is already configured for this Portal." + } + } + /** * Set development mode on the Portal which will look for a server URL set by the Portals CLI. * This is set to true by default but can be turned off manually if desired. @@ -597,7 +678,7 @@ class PortalBuilder(val name: String) { portal.addAssetMaps(assetMaps) portal.initialContext = this.initialContext portal.portalFragmentType = this.portalFragmentType - portal.liveUpdateConfig = this.liveUpdateConfig + portal.liveUpdateSource = this.liveUpdateSource portal.devMode = this.devMode onCreate(portal) return portal diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt index f539674..df9ebe5 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalFragment.kt @@ -12,7 +12,6 @@ import androidx.annotation.NonNull import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.getcapacitor.* -import io.ionic.liveupdates.LiveUpdateManager import org.json.JSONException import org.json.JSONObject import java.io.File @@ -265,11 +264,11 @@ open class PortalFragment : Fragment { /** * Reloads the Portal. - * If Live Updates is used and the web content was updated, the new content will be loaded. + * If a live update source is configured and the web content was updated, the new content will be loaded. */ fun reload() { - if(portal?.liveUpdateConfig != null) { - val latestLiveUpdateFiles = LiveUpdateManager.getLatestAppDirectory(requireContext(), portal?.liveUpdateConfig?.appId!!) + if(portal?.liveUpdateSource != null) { + val latestLiveUpdateFiles = portal?.latestAppDirectory(requireContext()) if (latestLiveUpdateFiles != null) { if (liveUpdateFiles == null || liveUpdateFiles!!.path != latestLiveUpdateFiles.path) { liveUpdateFiles = latestLiveUpdateFiles @@ -323,8 +322,8 @@ open class PortalFragment : Fragment { .addPluginInstances(initialPluginInstances) .addWebViewListeners(webViewListeners) - if (portal?.liveUpdateConfig != null) { - liveUpdateFiles = LiveUpdateManager.getLatestAppDirectory(requireContext(), portal?.liveUpdateConfig?.appId!!) + if (portal?.liveUpdateSource != null) { + liveUpdateFiles = portal?.latestAppDirectory(requireContext()) bridgeBuilder = if (liveUpdateFiles != null) { if (config == null) { val configFile = File(liveUpdateFiles!!.path + "/capacitor.config.json") diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt index c296d31..407af5f 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalManager.kt @@ -53,7 +53,7 @@ object PortalManager { * ``` * * @param name the portal name - * @throws NoSuchElementException throws this exception if the Portal does not exist + * @throws IllegalStateException throws this exception if the Portal does not exist */ @JvmStatic fun getPortal(name: String): Portal { @@ -62,8 +62,8 @@ object PortalManager { /** * Removes the Portal from the Portal Manager. The Portal will be returned if it was present. If not, null is returned. - * Note: if the Portal uses Live Updates and registered an instance on creation, the Live Update instance for the app - * is not removed. + * Note: removing a Portal does not remove its Ionic Live Updates app instance from the + * Ionic Live Updates manager. * * @param name the name of the Portal to remove */ @@ -92,27 +92,6 @@ object PortalManager { return portals.size } - /** - * Portals registration is no longer required. This function is retained for source - * compatibility and has no effect. - * - * @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) {} - - /** - * Portals registration is no longer required. - * - * @return true. - */ - @Deprecated("Portals registration is no longer required. This method always returns true.") - @JvmStatic - fun isRegistered(): Boolean { - return true - } - /** * A helper function to build portal classes and add them to the manager. * Classes built with newPortal are added to the PortalManager automatically. diff --git a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalView.kt b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalView.kt index 6121816..9f4ec9a 100644 --- a/IonicPortals/src/main/kotlin/io/ionic/portals/PortalView.kt +++ b/IonicPortals/src/main/kotlin/io/ionic/portals/PortalView.kt @@ -40,7 +40,7 @@ import java.util.ArrayList * * ``` * - * Jetpack Composd example usage: + * Jetpack Compose example usage: * ```kotlin * @Composable * fun loadPortal(portalId: String) { @@ -361,4 +361,4 @@ class PortalView : FrameLayout { mDisappearingFragmentChildren!!.add(v) } } -} \ No newline at end of file +}