Download Plan¶
Save plans into mobile storage and use them later without internet connection.
Overview¶
- Use
ExpoFpPlan.downloaderto download, unzip, and manage offline plans. - A downloaded plan is represented by
ExpoFpDownloadedPlanInfo. - You can create a presenter from a downloaded plan the same way as from an online plan.
- Recommended: Use the coroutine-based API. Callback API is available for compatibility (e.g. Java).
Step 1. Download a Plan from the Internet¶
Coroutine version (recommended)¶
lifecycleScope.launch {
val result = ExpoFpPlan.downloader.downloadPlan("demo")
val info = result.getOrNull()
Log.i("ExpoTest", "Downloaded (suspend): ${info ?: result.errorOrNull()}")
}
Callback version¶
ExpoFpPlan.downloader.downloadPlan("demo") { result ->
Log.i("ExpoTest", "Downloaded (callback): ${result.getOrNull() ?: result.errorOrNull()}")
}
Step 2. Load a Plan from a ZIP Archive¶
Important: Archive must be named
<expoKey>_<version>.zip.
Example:demo_42.zip
Coroutine version (recommended)¶
val zipFile = File(context.filesDir, "<expoKey>_<version>.zip")
lifecycleScope.launch {
val result = ExpoFpPlan.downloader.downloadPlanFromZip(zipFile.absolutePath)
Log.i("ExpoTest", "Unzipped (suspend): ${result.getOrNull() ?: result.errorOrNull()}")
}
Callback version¶
val zipFile = File(context.filesDir, "<expoKey>_<version>.zip")
ExpoFpPlan.downloader.downloadPlanFromZip(zipFile.absolutePath) { result ->
Log.i("ExpoTest", "Unzipped (callback): ${result.getOrNull() ?: result.errorOrNull()}")
}
Step 3. Manage Downloaded Plans¶
Get list of downloaded plans¶
Coroutine version (recommended):
lifecycleScope.launch {
val downloadedPlans: List<ExpoFpDownloadedPlanInfo> =
ExpoFpPlan.downloader.getDownloadedPlansInfo() // or getDownloadedPlansInfo("demo")
}
Callback version:
ExpoFpPlan.downloader.getDownloadedPlansInfo { plans ->
Log.i("ExpoTest", "Downloaded plans: $plans")
}
Remove old versions¶
Best practice: Always remove old versions after downloading a new one.
Remove all plans:
lifecycleScope.launch {
ExpoFpPlan.downloader.removeOldVersionsOfDownloadedPlans()
}
Remove by Expo Key:
lifecycleScope.launch {
ExpoFpPlan.downloader.removeOldVersionsOfDownloadedPlans("demo")
}
Delete downloaded plans¶
Remove specific plan:
lifecycleScope.launch {
ExpoFpPlan.downloader.removeDownloadedPlan(downloadedPlanInfo)
}
Remove all:
lifecycleScope.launch {
ExpoFpPlan.downloader.removeAllDownloadedPlans()
}
Step 4. Create Presenter from Downloaded Plan¶
val presenter = ExpoFpPlan.createPlanPresenter(
planLink = ExpoFpLinkType.DownloadedPlanInfo(downloadedPlanInfo)
)
With additional parameters¶
val additionalParams = listOf(ExpoFpPlanParameter.NoOverlay(true), ExpoFpPlanParameter.HideHeaderLogo(true))
val locationProvider: IExpoFpLocationProvider = YourLocationProvider()
val messageListener: IExpoFpPlanMessageListener = YourMessageListener()
val presenter = ExpoFpPlan.createPlanPresenter(
planLink = ExpoFpLinkType.DownloadedPlanInfo(downloadedPlanInfo),
additionalParams = additionalParams,
locationProvider = locationProvider,
messageListener = messageListener
)
Step 5. Show Downloaded Plan in UI¶
View-based UI¶
val planView = presenter.getView()
yourContainer.addView(planView)
Jetpack Compose¶
AndroidView(factory = { presenter.getView() })
Technical Notes¶
How Downloaded Plans Are Loaded¶
Downloaded plans are stored in app cache storage and loaded via Android's WebViewAssetLoader:
Storage structure:
{app_cache_dir}/expoFpPlan/archives/
├── demo_123/
│ ├── index.html
│ └── data/
│ └── data.js
└── expo2024_456/
├── index.html
└── assets/
Loading mechanism:
- All plans load from the same URL: https://localhost/index.html
- The WebViewAssetLoader resolves this URL to the correct plan directory
- Plan-specific directory is configured in the asset loader's base path
Why this matters:
- Plan HTML files use absolute resource paths (e.g., /data/data.js)
- This architecture ensures these paths resolve correctly without modifying plan files
- Each plan is isolated in its own directory but appears at the same URL to the WebView
This design allows downloaded plans to work identically to online plans from the JavaScript perspective, while being served from local storage.