-
Notifications
You must be signed in to change notification settings - Fork 0
feat(android): update to 5 #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,17 @@ | ||
| package com.expofp | ||
|
|
||
| import com.facebook.react.bridge.NativeModule | ||
| import android.util.Log | ||
| import com.facebook.react.bridge.ReactApplicationContext | ||
| import com.facebook.react.bridge.ReactContext | ||
| import com.facebook.react.bridge.ReactContextBaseJavaModule | ||
| import com.facebook.react.bridge.ReactMethod | ||
| import com.facebook.react.bridge.Promise | ||
| import com.expofp.fplan.SharedFplanView | ||
| import com.expofp.fplan.models.Settings | ||
| import com.facebook.react.bridge.UiThreadUtil | ||
|
|
||
| class ExpofpModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { | ||
| override fun getName() = "ExpofpModule" | ||
|
|
||
| @ReactMethod | ||
| fun preload(url: String, promise: Promise) { | ||
| try { | ||
| val context = this.reactApplicationContext.applicationContext | ||
| UiThreadUtil.runOnUiThread { | ||
| SharedFplanView.preload(url, Settings(), context) | ||
| } | ||
| promise.resolve(null) | ||
| } catch (e: Exception) { | ||
| promise.reject("PRELOAD_ERROR", e.message) | ||
| } | ||
| Log.d("ExpofpModule", "preload: not implemented") | ||
| promise.reject("PRELOAD_UNAVAILABLE", "ExpoFP Android v5 preload is not implemented yet") | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,133 +1,78 @@ | ||
| package com.expofp | ||
|
|
||
| import android.app.Application | ||
| import android.util.Log | ||
| import android.view.View | ||
| import com.expofp.common.GlobalLocationProvider | ||
| import com.expofp.crowdconnected.CrowdConnectedProvider | ||
| import com.expofp.crowdconnected.Mode | ||
| import com.expofp.fplan.FplanView | ||
| import com.expofp.fplan.models.FplanViewState | ||
| import com.expofp.fplan.api.app.ExpoFpPlan | ||
| import com.expofp.fplan.api.app.model.ExpoFpLinkType | ||
| import com.expofp.fplan.api.locationProvider.IExpoFpLocationProvider | ||
| import com.expofp.fplan.ui.ExpoFpView | ||
| import com.facebook.react.bridge.ReadableMap | ||
| import com.facebook.react.uimanager.SimpleViewManager | ||
| import com.facebook.react.uimanager.ThemedReactContext | ||
| import com.facebook.react.uimanager.annotations.ReactProp | ||
| import com.expofp.R | ||
| import com.expofp.fplan.contracts.DownloadOfflinePlanCallback | ||
| import com.expofp.fplan.models.OfflinePlanInfo | ||
| import com.expofp.crowdconnected.ExpoFpCrowdConnectedLocationProvider | ||
| // import com.expofp.crowdconnectedbackground.ExpoFpCrowdConnectedBackgroundLocationProvider | ||
| import com.expofp.crowdconnected.ExpoFpCrowdConnectedLocationProviderSettings | ||
| import com.expofp.crowdconnected.ExpoFpCrowdConnectedNavigationType | ||
|
|
||
| class ExpofpViewManager : SimpleViewManager<View>() { | ||
| class ExpofpViewManager : SimpleViewManager<ExpoFpView>() { | ||
| private var reactContext: ThemedReactContext? = null | ||
|
|
||
| override fun getName() = "ExpofpView" | ||
|
|
||
| override fun createViewInstance(reactContext: ThemedReactContext): View { | ||
| override fun createViewInstance(reactContext: ThemedReactContext): ExpoFpView { | ||
| this.reactContext = reactContext | ||
| var view = FplanView(reactContext) | ||
|
|
||
| return view; | ||
| ExpoFpPlan.initialize(reactContext.applicationContext) | ||
| return ExpoFpView(reactContext) | ||
| } | ||
|
|
||
| override fun onDropViewInstance(view: View) { | ||
| (view as? FplanView)?.destroy() | ||
| override fun onDropViewInstance(view: ExpoFpView) { | ||
| super.onDropViewInstance(view) | ||
| } | ||
|
|
||
| private fun getExpoKeyFromUrl(url: String): String { | ||
| return url.substringAfter("https://").substringBefore(".expofp.com") | ||
| } | ||
|
|
||
| private fun openMapForUrl(view: FplanView, url: String) { | ||
| val expoKey = getExpoKeyFromUrl(url) | ||
| val settings = com.expofp.fplan.models.Settings().withGlobalLocationProvider() | ||
|
|
||
| val offlinePlanManager = FplanView.getOfflinePlanManager(reactContext) | ||
| val latestOfflinePlan = offlinePlanManager.allOfflinePlansFromCache | ||
| .filter { offlinePlanInfo -> offlinePlanInfo.expoKey == expoKey } | ||
| .maxByOrNull { offlinePlanInfo -> offlinePlanInfo.version } | ||
|
|
||
| if (latestOfflinePlan != null) { | ||
| Log.d("ExpofpModule", latestOfflinePlan.expoKey) | ||
| view.openOfflinePlan(latestOfflinePlan, "", settings) | ||
| return | ||
| } | ||
|
|
||
| val ctx = this.reactContext ?: run { | ||
| view.load(url, settings) | ||
| return | ||
| } | ||
|
|
||
| val am = ctx.assets | ||
| val cachePlanExists = try { | ||
| am.open("${expoKey}.zip").close() | ||
| true | ||
| } catch (e: Exception) { | ||
| false | ||
| } | ||
|
|
||
| if (cachePlanExists) { | ||
| try { | ||
| Log.d("ExpofpModule", "openZipFromAssets: ${expoKey}.zip") | ||
| view.openZipFromAssets("${expoKey}.zip", "", settings, ctx) | ||
| return | ||
| } catch (e: Exception) { | ||
| Log.d("ExpofpModule", "failed to open asset zip, loading url: $url") | ||
| view.load(url, settings) | ||
| return | ||
| } | ||
| } | ||
|
|
||
| Log.d("ExpofpModule", "asset zip not found, loading url: $url") | ||
| view.load(url, settings) | ||
| } | ||
|
|
||
| private fun triggerOfflinePlanDownload(expoKey: String) { | ||
| val offlinePlanManager = FplanView.getOfflinePlanManager(reactContext) | ||
| offlinePlanManager.downloadOfflinePlanToCache(expoKey, object : DownloadOfflinePlanCallback { | ||
| override fun onCompleted(offlinePlanInfo: OfflinePlanInfo) { | ||
| Log.d("ExpofpModule", "downloaded offline plan: ${offlinePlanInfo.expoKey} v${offlinePlanInfo.version}") | ||
| } | ||
|
|
||
| override fun onError(message: String) { | ||
| Log.e("ExpofpModule", "offline plan download failed: $message") | ||
| } | ||
| }) | ||
| private fun createCrowdConnectedProvider(settingsMap: ReadableMap): IExpoFpLocationProvider? { | ||
| val context = reactContext?.applicationContext ?: return null | ||
| val application = (context as? Application) ?: return null | ||
|
|
||
| val cc = if (settingsMap.hasKey("crowdConnected")) settingsMap.getMap("crowdConnected") else settingsMap | ||
| if (cc == null) return null | ||
|
|
||
| val appKey = if (cc.hasKey("appKey")) cc.getString("appKey") else null | ||
| val token = if (cc.hasKey("token")) cc.getString("token") else null | ||
| val secret = if (cc.hasKey("secret")) cc.getString("secret") else null | ||
| if (appKey.isNullOrEmpty() || token.isNullOrEmpty() || secret.isNullOrEmpty()) return null | ||
| val aliases = mutableMapOf<String, String>() | ||
| aliases["onesignal_user_id"] = cc.getString("oneSignalUserId") ?: "" | ||
| val settings = ExpoFpCrowdConnectedLocationProviderSettings( | ||
| appKey = appKey, | ||
| token = token, | ||
| secret = secret, | ||
| navigationType = ExpoFpCrowdConnectedNavigationType.ALL, | ||
| isAllowedInBackground = false, | ||
| isHeadingEnabled = true, | ||
| aliases = aliases, | ||
| notificationText = "Indoor navigation is active", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: not localized |
||
| serviceIcon = R.drawable.placeholder_icon | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace the placeholder icon with a production asset. Using 🤖 Prompt for AI Agents |
||
| ) | ||
|
|
||
| // ExpoFpCrowdConnectedBackgroundLocationProvider(application, settings) | ||
| return ExpoFpCrowdConnectedLocationProvider(application, settings) | ||
| } | ||
|
|
||
| @ReactProp(name = "settings") | ||
| fun setSettings(view: FplanView, settingsMap: ReadableMap?) { | ||
| println("setSettings: $settingsMap") | ||
| settingsMap?.let { | ||
| var appKey = settingsMap.getString("appKey") | ||
| val token = settingsMap.getString("token") | ||
| val secret = settingsMap.getString("secret") | ||
| if (appKey != null && token != null && secret != null) { | ||
| val context = reactContext?.applicationContext ?: return | ||
| val application = context as? Application ?: return | ||
| val aliases = mutableMapOf<String, String>() | ||
| aliases["onesignal_user_id"] = it.getString("oneSignalUserId") ?: "" | ||
| val lpSettings = com.expofp.crowdconnected.Settings( | ||
| settingsMap.getString("appKey") ?: "", | ||
| settingsMap.getString("token") ?: "", | ||
| settingsMap.getString("secret") ?: "", | ||
| Mode.IPS_AND_GPS, | ||
| true, | ||
| aliases | ||
| ) | ||
| lpSettings.setServiceNotificationInfo("Background Location is running", R.drawable.placeholder_icon); | ||
|
|
||
| val locationProvider = CrowdConnectedProvider(application, lpSettings) | ||
| // val locationProvider = CrowdConnectedBackgroundProvider(application, lpSettings) | ||
| GlobalLocationProvider.init(locationProvider) | ||
| GlobalLocationProvider.start() | ||
| } | ||
| if (view.state.equals(FplanViewState.Created)) { | ||
| val url = it.getString("url") ?: "" | ||
| val expoKey = getExpoKeyFromUrl(url) | ||
| fun setSettings(view: ExpoFpView, settingsMap: ReadableMap?) { | ||
| if (settingsMap == null) return | ||
| val url = settingsMap.getString("url") ?: return | ||
| val context = reactContext?.applicationContext ?: return | ||
| val expoKey = getExpoKeyFromUrl(url) | ||
|
|
||
| openMapForUrl(view, url) | ||
| triggerOfflinePlanDownload(expoKey) | ||
| } | ||
| } | ||
| ExpoFpPlan.initialize(context) | ||
| val p = ExpoFpPlan.createPlanPresenter(planLink = ExpoFpLinkType.ExpoKey(expoKey)) | ||
| val ccProvider = createCrowdConnectedProvider(settingsMap) | ||
| if (ccProvider != null) p.setLocationProvider(ccProvider) | ||
| view.attachPresenter(p) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make background mode configurable from JavaScript.
The
isAllowedInBackgroundproperty is hardcoded tofalse, preventing JavaScript code from controlling background location tracking. This limits flexibility and may break use cases that require background positioning.Consider adding a React prop and backing field to expose this setting:
class ExpofpViewManager : SimpleViewManager<ExpoFpView>() { private var reactContext: ThemedReactContext? = null + private var enableBackground: Boolean = falseThen update the provider creation:
val settings = ExpoFpCrowdConnectedLocationProviderSettings( appKey = appKey, token = token, secret = secret, navigationType = ExpoFpCrowdConnectedNavigationType.ALL, - isAllowedInBackground = false, + isAllowedInBackground = enableBackground, isHeadingEnabled = true, aliases = aliases, notificationText = "Indoor navigation is active", serviceIcon = R.drawable.placeholder_icon )And add a setter:
🤖 Prompt for AI Agents