Skip to content

Migration Guide: v4 to v5

How to migrate your Android integration from ExpoFP SDK v4 to v5 and start using the new presenter-based architecture.

Summary v5 replaces direct view control with a presenter, streamlines initialization, adds coroutine-first APIs, and formalizes offline/preload flows.


Key Changes at a Glance

Area v4 v5
Rendering FplanView / SharedFplanView ExpoFpView + IExpoFpPlanPresenter
Initialization load(url, Settings) ExpoFpPlan.initialize(...) + createPlanPresenter(planLink, ...)
State & errors View callbacks (onReady, onError) planStatusFlow (Flow<ExpoFpPlanStatus>)
Offline OfflinePlanManager ExpoFpPlan.downloader
Preload SharedFplanView.preload(...) ExpoFpPlan.preloader (multiple preloads)
Events Settings.withEventsListener(...) IExpoFpPlanMessageListener
Global location GlobalLocationProvider ExpoFpPlan.globalLocationProvider
Cleanup Manual destroy() Not required (managed by view/presenter)

Step 1. Initialization

View-based apps (v4 to v5)

Version 4 (FplanView / SharedFplanView):

public class YourActivity extends AppCompatActivity {
    private FplanView fplanView;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        fplanView = new FplanView(this);
        setContentView(fplanView);
        fplanView.load("https://demo.expofp.com", new Settings());
    }

    @Override protected void onDestroy() {
        fplanView.destroy();
        super.onDestroy();
    }
}

Version 5 (Presenter + ExpoFpView):

// Application.onCreate (recommended once)
ExpoFpPlan.initialize(this)

// Before first use (allowed)
ExpoFpPlan.initialize(context)

val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.RawLink("https://demo.expofp.com")
)

val expoView = ExpoFpView(this).apply { attachPresenter(presenter) }
setContentView(expoView) // No manual destroy() needed

Jetpack Compose (new in v5)

ExpoFpPlan.initialize(this)

val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.RawLink("https://demo.expofp.com")
)

setContent {
    AndroidView(factory = { presenter.getView() })
}

Step 2. Settings and Parameters

In v4, many options were passed via the Settings object (focus behaviors, events, location). In v5, you configure the presenter:

  • Initial link via planLink
  • Optional additionalParams (URL query items)
  • Optional locationProvider
  • Optional messageListener

v4 flags like focusOnLocation / focusOnFirstLocation were part of Settings; in v5 the map handles focus/blue-dot behavior internally.

Per-presenter location provider (v4 to v5)

v4

Settings settings = new Settings()
        .withLocationProvider(new YourProvider(getApplication()));
// fplanView.load("https://demo.expofp.com", settings);
v5
val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.ExpoKey("demo"),
    locationProvider = YourLocationProvider()
)

Global location provider (shared)

v5

ExpoFpPlan.globalLocationProvider.sharedProvider = YourLocationProvider()

val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.ExpoKey("demo"),
    locationProvider = ExpoFpPlan.globalLocationProvider
)


Step 3. Plan Status and Errors

In v4 you subscribed to readiness/error callbacks on the view. In v5 observe a Kotlin Flow from the presenter.

lifecycleScope.launchWhenStarted {
    presenter.planStatusFlow.collect { status ->
        when (status) {
            is ExpoFpPlanStatus.Initialization -> { /* starting */ }
            is ExpoFpPlanStatus.Loading -> { /* status.percentage */ }
            is ExpoFpPlanStatus.Ready -> { /* ready */ }
            is ExpoFpPlanStatus.Error -> { /* status.error */ }
        }
    }
}

Step 4. Offline Downloaded Plans

v4: OfflinePlanManager downloaded to cache; you managed old versions yourself.
v5: use ExpoFpPlan.downloader.

// Download and save
val result = ExpoFpPlan.downloader.downloadPlan("demo")
val info = result.getOrNull() ?: return

// Open downloaded plan
val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.DownloadedPlanInfo(info)
)

You can also unzip a bundled file named <expoKey>_<version>.zip with downloadPlanFromZip(filePath).


Step 5. Preloaded Plans

v4: SharedFplanView.preload(...) (single shared plan instance).
v5: ExpoFpPlan.preloader supports multiple preloads and retrieving presenters as needed.

val preloaded = ExpoFpPlan.preloader.preloadPlan(
    planLink = ExpoFpLinkType.ExpoKey("demo")
)
val presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloaded)

If a preloaded plan is reloaded with a new planLink, the link is updated in ExpoFpPreloadedPlanInfo.


Step 6. Commands (v4 to v5)

In v4 you called methods on the view; in v5 you call them on the presenter. Some names changed for clarity; others were replaced by richer APIs.

v4 (view) v5 (presenter) Notes
zoomIn() / zoomOut() zoomIn() / zoomOut() Same behavior.
fitBounds() fitBounds() Same.
selectBooth(String) selectBooth(String) Same.
selectExhibitor(String or String[]) selectExhibitor(String) / highlightExhibitors(List) Split responsibilities.
selectCategory(String) selectCategory(String) Same.
selectRoute(...) selectRoute(waypoints, onlyAccessible) Waypoints API in v5.
selectCurrentPosition(Location, focus) selectCurrentPosition(ExpoFpPosition, focus) v5 unified model.
updateLayerVisibility(layer, visible) activateFloor(floor) / setElementsVisibility(...) Use floors or elements visibility.
getVisibility(...) / setVisibility(...) getElementsVisibility() / setElementsVisibility(...) Renamed.
clear() / destroy() (not required) Managed by presenter/view.

v5 examples:

presenter.fitBounds()
presenter.selectBooth("Lounge 2 | Hall 3.1")
presenter.setElementsVisibility(ExpoFpElementsVisibility(controls=false, levels=false, header=false, overlay=false))

val floors = presenter.getFloors()
presenter.activateFloor(floor)

More commands are covered in Manage Plan.


Step 7. Events

v4: events were assigned via Settings.withEventsListener(...).
v5: implement IExpoFpPlanMessageListener and pass it to the presenter (during creation is preferred).

class MyListener : IExpoFpPlanMessageListener {
    override fun boothDidClick(e: ExpoFpResult<ExpoFpBoothClickEvent>, from: ExpoFpLinkType) { /*...*/ }
    override fun directionDidBuild(e: ExpoFpResult<ExpoFpDirection?>, from: ExpoFpLinkType) { /*...*/ }
    override fun detailsDidClick(e: ExpoFpResult<ExpoFpDetails?>, from: ExpoFpLinkType) { /*...*/ }
    override fun exhibitorCustomButtonDidClick(e: ExpoFpResult<ExpoFpCustomButtonEvent>, from: ExpoFpLinkType) { /*...*/ }
    override fun bookmarkDidClick(e: ExpoFpResult<ExpoFpBookmark>, from: ExpoFpLinkType) { /*...*/ }
    override fun categoryDidClick(e: ExpoFpResult<ExpoFpCategory>, from: ExpoFpLinkType) { /*...*/ }
    override fun currentPositionDidChange(e: ExpoFpResult<ExpoFpPosition>, from: ExpoFpLinkType) { /*...*/ }
    override fun currentFloorDidChange(e: ExpoFpResult<ExpoFpFloor>, from: ExpoFpLinkType) { /*...*/ }
}

val presenter = ExpoFpPlan.createPlanPresenter(
    planLink = ExpoFpLinkType.ExpoKey("demo"),
    messageListener = MyListener()
)

See the full list in Listen Plan Events.


Step 8. Navigation (Location Providers)

  • v4 configured providers via Settings.withLocationProvider(...) or used a global provider.
  • v5 attaches a provider directly to the presenter or uses ExpoFpPlan.globalLocationProvider.

Refer to Setup Navigation for permissions and partner wrappers (CrowdConnected, IndoorAtlas).


Step 9. Debugging

v5 provides a debug mode and a way to evaluate custom scripts on the plan during development.

ExpoFpPlan.isDebugModeEnabled = true
val js: ExpoFpResult<Any?> = presenter.evaluateCustomScript("console.log('ping')")


Migration Checklist

  • [ ] Replace FplanView / SharedFplanView with ExpoFpView + IExpoFpPlanPresenter.
  • [ ] Initialize once with ExpoFpPlan.initialize(...).
  • [ ] Move Settings options to presenter creation (planLink, additionalParams, locationProvider, messageListener).
  • [ ] Replace v4 callbacks with planStatusFlow.
  • [ ] Switch offline operations from OfflinePlanManager to ExpoFpPlan.downloader.
  • [ ] Replace SharedFplanView.preload(...) with ExpoFpPlan.preloader.
  • [ ] Migrate view-level commands to presenter methods; update visibility/floor APIs.
  • [ ] Migrate event listeners to IExpoFpPlanMessageListener.
  • [ ] Review navigation setup (providers and permissions) per Setup Navigation.
  • [ ] Remove any manual destroy() calls.