-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Mapbox v10 #5141
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
Mapbox v10 #5141
Conversation
|
Ok @lognaturel @seadowg it's time to decide how to handle Mapbox 10. As we talked recently options are:
I was more in favor of one of two first options because it would be easier. Then if it doesn't work for us or there is any problem with licenses we can add a separate flavor. |
| // the Mapbox native library only for 32-bit and 64-bit ARM devices and | ||
| // omit it for all X86 devices. | ||
| exclude 'lib/x86/libmapbox-gl.so' | ||
| exclude 'lib/x86_64/libmapbox-gl.so' |
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.
I think you are not going to like it. Mapbox 10 has libs with different naming. I could of course update it here and delete too but that causes crashes when the app is started so it looks like we no longer can do tricks like that. That increases the size of our apk of course...
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.
What crash are you seeing? I think we'll want to do as much as can to avoid including unused x86 libs.
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.
Agreed with @seadowg that more details are needed here. What's the binary size without the filtering?
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.
The error is:
Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/org.odk.collect.android-fpP4T_tgz8x_GU7w9kKqvA==/base.apk"],nativeLibraryDirectories=[/data/app/org.odk.collect.android-fpP4T_tgz8x_GU7w9kKqvA==/lib/x86_64, /data/app/org.odk.collect.android-fpP4T_tgz8x_GU7w9kKqvA==/base.apk!/lib/x86_64, /system/lib64, /system/product/lib64]]] couldn't find "libmapbox-maps.so"
at java.lang.Runtime.loadLibrary0(Runtime.java:1067)
at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
at java.lang.System.loadLibrary(System.java:1667)
at com.mapbox.common.loader.MapboxLibraryLoader.load(MapboxLibraryLoader.kt:19)
at com.mapbox.maps.loader.MapboxMapsInitializer.create(MapboxMapsInitializer.java:19)
at com.mapbox.maps.loader.MapboxMapsInitializer.create(MapboxMapsInitializer.java:16)
at androidx.startup.AppInitializer.doInitialize(AppInitializer.java:180)
at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:238)
at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:206)
at androidx.startup.InitializationProvider.onCreate(InitializationProvider.java:45)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2092)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2066)
at android.app.ActivityThread.installProvider(ActivityThread.java:6983)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:6528)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6445)
at android.app.ActivityThread.access$1300(ActivityThread.java:219)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1859)
it takes place immediately when the app is started
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.
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.
If this error is indeed only on architectures with missing libs (which it looks like and hopefully is), then that pushes us more towards not including Mapbox in all builds (using product flavours or my preferred conditional dependency method). That would allow us to continue to exclude x86 libs as we'd just use the "non mapbox" build types on emulators.
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.
If this error is indeed only on architectures with missing libs
Yes the crash occurs only on x86 devices (if I remove those libs).
| proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' | ||
| resValue("string", "GOOGLE_MAPS_API_KEY", googleMapsApiKey) | ||
| buildConfigField 'String', "MAPBOX_ACCESS_TOKEN", '"' + mapboxToken + '"' | ||
| resValue("string", "mapbox_access_token", mapboxAccessToken) |
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.
This works out of the box. We generate a string named mapbox_access_token with a valid access token and mapbox takes it automatically to setup everything that it needs.
|
@grzesiek2010 is the compilation error on CI expected? I'm guessing it's because there isn't a Mapbox API key configured to grab the SDK. |
seadowg
left a comment
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.
Are you able to roll back to Kotlin conversions here? It makes it hard to understand the actual diff. For instance, Git is unable to track the changes you had to make to MapboxInitializationFragment d858661.
We should potentially start doing our Kotlin conversions as follow-up PRs as I'm finding it's making reviews (and reviewing my own changes) much harder.
| MapView(context).onCreate(null) | ||
| } | ||
| Configuration.getInstance().userAgentValue = userAgentProvider.userAgent | ||
| MapboxUtils.initMapbox() |
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.
We just don't need this any longer? Definitely nice if so!
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.
We don not initialize mapbox like that now. That class was also used to handle cases where a user tried to use mapbox on unsupported devices or a user didn't have a valid access token (then null was returned and we were able to handle that cases). Now as all devices are supported because I removed excluding libraries for X86 devices + without a valid downloads token you won't build the app at all. It's still possible to have a supported device + valid downloads token but wrong access token but it doesn't make sense to check such strange cases I think.
I think these feel roughly equivalent: both would work but might result in Mapbox suspending our account/revoking keys, and we'd have to resort to doing something else eventually. An alternative to this that still keeps the build simple would be to require CI (which could be leaked easily unless we stop running CI for PRs) and developer to all have their own Mapbox account/API keys and create their own local
I still think something along these lines is my preferred option. We can add a product flavour for when Mapbox is included and when it isn't and have the former include the real implementation of As I've thought about it more however, I think there might be a "simpler" way to do achieve the same goals: move Mapbox code to a module (we want to do this anyway for all the different if (mapboxAccessToken != null) {
implementation project(':mapbox')
}This would allow us to have a module that would only compile when try {
return this.getClassLoader().loadClass("org.odk.collect.mapbox.MapboxMapFragment").newInstance();
} catch (ClassNotFoundException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (InstantiationException e) {
return null;
}If we do go down this road (which I'm advocating for), we probably want to look at merging #5139 before this as those changes will be needed to allow |
exactly there are no |
I'm not sorry. Generally I always implement changes and once everything is done I add conversion in a separate commit but not this time sorry my mistake. To help you a bit with that class what I changed was:
and that's it. |
|
Just a note about something to keep in mind: Chromebooks/Chrome OS devices are pretty likely to have x86. That's a real world example of x86 devices that we didn't really consider the first time we removed Mapbox's x86 libs (and therefore x86 support). I imagine we can get away with not supporting Mapbox on Chromebooks, but we should make sure the release build can start OK on them. |
|
@seadowg The problem is that we use that new module in more places than only to create
what do you think? |
Yeah, I think we can just dynamically add the Mapbox source option if the configurator is present.
👍
Yes that sounds right. You'll have to add the Fragment with a transaction rather than loading it in XML using
Ooooft yeah that's a problem. I think what we'll have to do there is give the dependency module a constructor with args, and then grab the constructor dynamically. So the module would look like this: @Module
class MapboxDependencyModule(
private val settingsProvider: SettingsProvider,
private val networkStateProvider: NetworkStateProvider,
private val referenceLayerRepository: ReferenceLayerRepository
) {
@Provides
fun providesSettingsProvider(): SettingsProvider {
return settingsProvider
}
@Provides
fun providesNetworkStateProvider(): NetworkStateProvider {
return networkStateProvider
}
@Provides
fun providesReferenceLayerRepository(): ReferenceLayerRepository {
return referenceLayerRepository
}
}I'd been thinking that giving these modules constructors would be nice anyway - it prevents us from having to create this.getClassLoader().loadClass("org.odk.collect.mapbox.MapboxDependencyModule").getConstructors()[0].newInstance(
settingsProvider,
networkStateProvider,
referenceLayerRepository
);Given we're using reflection to load and construct classes in multiple places, it might be a good idea to have a helper object that hides all this work. I could imagine a |
|
I've fixed all steps mentioned above apart from DI. As you know i will be off next two weeks so if this blocks you somehow feel free to take over. What's left:
|
.circleci/test_modules.txt
Outdated
| permissions:testDebug | ||
| settings:testDebug | ||
| maps:testDebug | ||
| mapbox:testDebug |
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.
We'll need to be careful to run tests ourselves when making changes to mapbox
| * [ObjectProviderHost.getMultiClassProvider]. | ||
| */ | ||
|
|
||
| interface ObjectProvider { |
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.
This is a 35 line mini DI framework. I think there are definitely better solutions than this but let's finish the discussion in #4940 before thinking about that.
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.
In the mean time, consider adding to the comment to state it was introduced to solve a specific problem with Mapbox and is not intended to be used elsewhere.
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.
I'm still thinking that if the result of #4940 is "keep Dagger" than I'll just replace this with a shared module for the Dagger setup. I'll leave this right now until we've made some decisions there.
| // Looks for user swipes. If the user has swiped, move to the appropriate screen. | ||
|
|
||
| // For all screens a swipe is left/right of at least .25" and up/down of less than .25" OR left/right of > .5" | ||
| int xpixellimit = (int) (ScreenUtils.xdpi(odkView.getContext()) * .25); |
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.
A few tests were failing here due to odkView being null when we swipe back from the end screen. I'm not sure how we've got away with SwipeHandler not dealing with EmptyView and FormEndView for so long.
| public T clickOption(int option) { | ||
| clickOnString(option); | ||
| return page.assertOnPage(); | ||
| return clickOnButtonInDialog(option, page); |
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.
There's an issue referenced in this method.
| android:exported="false" | ||
| tools:node="merge"> | ||
|
|
||
| <meta-data |
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.
I couldn't find these initializer classes referenced in the Mapbox docs. This meant I had to debug inside Android library code to work out what initializers were being run. @lognaturel are we able to send some feedback to Mapbox about this?
lognaturel
left a comment
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.
I’m delighted to say that now that y’all have done the work, it doesn’t look as scary or complicated as I had feared. I thought I’d want to review this interactively but I’m comfortable with the understanding I got async.
| * [ObjectProviderHost.getMultiClassProvider]. | ||
| */ | ||
|
|
||
| interface ObjectProvider { |
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.
In the mean time, consider adding to the comment to state it was introduced to solve a specific problem with Mapbox and is not intended to be used elsewhere.
You’ve taken this over and taken it out of draft so I assume you’re happy with the whole thing now.
| ``` | ||
| **Mapbox Maps SDK for Android**: When the "Mapbox SDK" option is selected in the "User interface" settings, ODK Collect uses the Mapbox SDK for displaying maps in the geospatial widgets (GeoPoint, GeoTrace, and GeoShape). To enable this API: | ||
| 1. [Create a Mapbox account](https://www.mapbox.com/signup/). Note that signing up with the "Pay-As-You-Go" plan does not require a credit card. Mapbox provides free API usage up to the monthly thresholds documented at [https://www.mapbox.com/pricing](https://www.mapbox.com/pricing). If your usage exceeds these thresholds, you will receive e-mail with instructions on how to add a credit card for payment; services will remain live until the end of the 30-day billing term, after which the account will be deactivated and will require a credit card to reactivate. |
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.
It looks to me like creating Mapbox accounts with @gmail.com addresses requires a credit card for an ID check (but my own domains seem to work fine) and this blocked @kkrawczyk123 from being able to set up an account. I'm hoping @soldevelo emails will work fine, but if not we might want to create an ODK dev account @lognaturel (probably using @getodk.org). For now, I've sent @kkrawczyk123 a secrets.properties file that with tokens generated in my dev account.
|
It feels like there's only a couple of minor problems left (assuming everything other than the last two issues posted by @srujner are fixed). I'm thinking we can go ahead and merge this to unblock other PRs and create issues for the remaining problems. Sound good @grzesiek2010? If so, @srujner could you create issues for your last couple of comments? |
|
I'm not sure I would rather give the QA team some time till Monday at least if there is nothing big then we can file separate issues for everything. |
|
Maybe y’all can check in when @seadowg starts his Monday? QA might have made good progress by then and it’d be great to unblock other map-related things in flight. |
|
@grzesiek2010 just dismissed reviews so that I remember to have another look at changes. Just request a review when you're done. |
|
Tested with Success! Verified cases:
|
|
Tested with Success! |
seadowg
left a comment
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.
It looks like b2cea65 and 34089a4 make changes to SelectionMapFragment, so I'm guessing the issues they address (which I think are #5141 (comment) and #5141 (comment) respectively) are not actually Mapbox specific, and I'd suspect are also existing issues rather than new ones caused by this PR (given that the code being changed isn't Mapbox code). Is that right @grzesiek2010?
If so, I think we should leave those commits out and create issues. Those changes will cause some nasty conflicts with #5164 and the issues being fixed may also have already been fixed by those PRs. Additionally, #5164 introduces a way to test interactions with the BottomSheetBehavior which would be needed to write a test for b2cea65 which is currently has no coverage.
| AppInitializer.getInstance(context) | ||
| .initializeComponent(MapboxMapsInitializer::class.java) | ||
| it.set(KEY_MAPBOX_INITIALIZED, true) | ||
| try { |
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.
I can't work out why this didn't need a try-catch when it was called in MapboxMapFragment?
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.
It wasn't needed because on a device that does not support mapbox choosing mapbox is not possible so that class MapboxMapFragment was never used. However MapBoxInitializationFragment was always used if there were keys provided (what casued crashes on emulators for example). I improved it in 24af451 and now try-catch is not needed indeed because we check both keys and supported abis.
|
34089a4 yes it was an existing issue. the problem was that if we received current location before loading items on the map then other events like zooming to those points were ignored. However it wasn't visible on master at least on my device because somehow the process was a little bit faster (I think the new map fragment loads slower). b2cea65 here again the code was broken but it worked well somehow and only now the bug is visible So both issues existed but both of them were let's say hidden. |
Thanks for the clarification. That makes sense. Can we pull those commits out for now (to a branch so that we don't lose the solutions you came up with), and then we can create issues or @getodk/testers can verify as part of a pass on #5164? That PR reworks |
Those are pretty serious issues to me so I wouldn't release v2022.3 without it but if you want to just exclude it to avoid conflicts and then fix soon - I'm ok with it and I can do that. |
|
@seadowg ok I reverted those two commits and I'm going to file separate issues for them. |
Thanks! Agreed, we need to fix before releasing. |
Closes #4893
Blocked by #5139Most of the discussion around this PR is in the issue or here so no need to write this up really.
Before submitting this PR, please make sure you have:
./gradlew checkAlland confirmed all checks still pass OR confirm CircleCI build passes and run./gradlew connectedDebugAndroidTestlocally.