Skip to content
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

Location View plugin #22

Merged
merged 2 commits into from
Jul 14, 2017
Merged

Location View plugin #22

merged 2 commits into from
Jul 14, 2017

Conversation

cammace
Copy link
Contributor

@cammace cammace commented Apr 24, 2017

This PR adds a location view plugin that makes use of runtime styling instead of drawing a view on top of the map as discussed in this issue and older PR.

@cammace cammace added enhancement location-layer-plugin Issues that deal with the location layer module ⚠️ DO NOT MERGE labels Apr 24, 2017
@cammace cammace self-assigned this Apr 24, 2017
@cammace
Copy link
Contributor Author

cammace commented May 10, 2017

Basic user location icon using the optional LocationTextAnnotation to display users location:

ezgif com-video-to-gif

Using mode Navigation causes the location to use Location.getBearing, change new location to a linear interpolator with duration set to how frequently new locations come in:

Compass bearing is also supported.

  • Add street name below navigation puck
  • Move TurfCircle to MAS
  • Create testapp activities for testing

Note that the circle-pitch-alignment still hasn't been added to runtime styling, therefore I cannot animate the accuracy circle to new locations (currently i'm just moving it to the new location). Tracking this style feature in mapbox/mapbox-gl-js#4120

cc: @mapbox/android

@zugaldia zugaldia requested a review from tobrun May 10, 2017 14:15
@zugaldia
Copy link
Member

@cammace Love seeing this effort back in motion! Let's build an MVP that we can merge on the earlier side. We can always iterate later to make it more complete.

@cammace cammace added ready for review When your PR has been personally reviewed, its time for an external contributors to approve and removed ⚠️ DO NOT MERGE labels May 11, 2017
@cammace
Copy link
Contributor Author

cammace commented May 12, 2017

I had to add an API for manually controlling the user location since in the Navigation SDK, snapping to route provides a location object which currently will need to be passed to the location layer to actually snap the location.

@tobrun
Copy link
Member

tobrun commented May 12, 2017

Tried running this on a Nexus 5 running KitKat, crashes at startup:

05-12 18:35:34.896 6813-6813/com.mapbox.mapboxsdk.plugins.testapp E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                    Process: com.mapbox.mapboxsdk.plugins.testapp, PID: 6813
                                                                                    android.content.res.Resources$NotFoundException: File res/drawable/mapbox_user_icon.xml from drawable resource ID #0x7f02006f. If the resource you are trying to use is a vector resource, you may be referencing it in an unsupported way. See AppCompatDelegate.setCompatVectorFromResourcesEnabled() for more info.
                                                                                        at android.content.res.Resources.loadDrawable(Resources.java:2096)
                                                                                        at android.content.res.Resources.getDrawable(Resources.java:700)
                                                                                        at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:374)
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerOptions.initialize(MyLocationLayerOptions.java:103)
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerOptions.<init>(MyLocationLayerOptions.java:77)
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerPlugin.<init>(MyLocationLayerPlugin.java:97)
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerPlugin.<init>(MyLocationLayerPlugin.java:79)
                                                                                        at com.mapbox.mapboxsdk.plugins.testapp.activity.location.MyLocationOptionsActivity.onMapReady(MyLocationOptionsActivity.java:93)
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback.onMapReady(MapView.java:959)
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback.access$1000(MapView.java:924)
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback$1.run(MapView.java:942)
                                                                                        at android.os.Handler.handleCallback(Handler.java:733)
                                                                                        at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                        at android.os.Looper.loop(Looper.java:137)
                                                                                        at android.app.ActivityThread.main(ActivityThread.java:4998)
                                                                                        at java.lang.reflect.Method.invokeNative(Native Method)
                                                                                        at java.lang.reflect.Method.invoke(Method.java:515)
                                                                                        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
                                                                                        at dalvik.system.NativeStart.main(Native Method)
                                                                                     Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #1: invalid drawable tag vector
                                                                                        at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:933)
                                                                                        at android.graphics.drawable.Drawable.createFromXml(Drawable.java:877)
                                                                                        at android.content.res.Resources.loadDrawable(Resources.java:2092)
                                                                                        at android.content.res.Resources.getDrawable(Resources.java:700) 
                                                                                        at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:374) 
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerOptions.initialize(MyLocationLayerOptions.java:103) 
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerOptions.<init>(MyLocationLayerOptions.java:77) 
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerPlugin.<init>(MyLocationLayerPlugin.java:97) 
                                                                                        at com.mapbox.mapboxsdk.plugins.mylocationlayer.MyLocationLayerPlugin.<init>(MyLocationLayerPlugin.java:79) 
                                                                                        at com.mapbox.mapboxsdk.plugins.testapp.activity.location.MyLocationOptionsActivity.onMapReady(MyLocationOptionsActivity.java:93) 
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback.onMapReady(MapView.java:959) 
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback.access$1000(MapView.java:924) 
                                                                                        at com.mapbox.mapboxsdk.maps.MapView$MapCallback$1.run(MapView.java:942) 
                                                                                        at android.os.Handler.handleCallback(Handler.java:733) 
                                                                                        at android.os.Handler.dispatchMessage(Handler.java:95) 
                                                                                        at android.os.Looper.loop(Looper.java:137) 
                                                                                        at android.app.ActivityThread.main(ActivityThread.java:4998) 
                                                                                        at java.lang.reflect.Method.invokeNative(Native Method) 
                                                                                        at java.lang.reflect.Method.invoke(Method.java:515) 
                                                                                        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777) 
                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593) 
                                                                                        at dalvik.system.NativeStart.main(Native Method) 

@tobrun
Copy link
Member

tobrun commented May 12, 2017

Not handling any runtime permissions

Copy link
Member

@tobrun tobrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR needs so more fine tuning for a couple of edge cases. Will do a thorough review when fixed.

@BindView(R.id.button_location_mode_navigation)
Button locationModeNavigationButton;

private MyLocationLayerOptions options;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want an options object to configure a plugin? The options paradigm, that is used in the main SDK was introduced to separate setters/getters from MapboxMap and inspired by similar constructs in the Gmaps SDK. Since the Location plugin is standalone. I don't feel we need a separate options object to make it more complicated.

@@ -27,6 +29,30 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>

<activity
android:name=".activity.location.MyLocationModesActivity"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used the My prefix for API compability, with this plugin this isn't a requirement anymore. I would suggest naming all things just Location instead of MyLocation.

Copy link
Member

@tobrun tobrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start @cammace! A bit of refactoring and restructuring and we can 🚢 would also love to touch base on the exposed API.

@BindView(R.id.button_location_mode_tracking)
Button locationModeTrackingButton;
@BindView(R.id.button_location_mode_navigation)
Button locationModeNavigationButton;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you bind these buttons? Just having the @OnClick is sufficient.

private CompassListener compassListener;

private Sensor rotationVectorSensor;
float[] matrix = new float[9];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing private keyword

*
* @since 0.1.0
*/
class MyLocationLayerConstants {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these constants used all around the codebase?
If not, these should be encapsulated by the respective classes themselves.

<resources>

<color name="mapbox_plugin_blue">#4882C6</color>
<color name="mapbox_plugin_gray">#263D57</color>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this resource is placed in a specific plugin folder, thus the name should be more unique to avoid id conflicts.

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Location View</string>
</resources>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file can be removed

*
* @since 0.1.0
*/
@SuppressWarnings( {"MissingPermission"})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ignoring, enforcing runtime permissions with the correct annotations on the public API would be cleaner.

options.removeLayerAndSources();
}
}
this.myLocationLayerMode = myLocationLayerMode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of if/else checking done in this method, this could re factored with using design pattern

* @param location where you'd like the location icon to be placed on the map
* @since 0.1.0
*/
public void setMyLocation(Location location) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is null allowed here? Either annotate public api with Nullable or NonNull to make the purpose clear for the end user

* @since 0.1.0
*/
@SuppressWarnings( {"MissingPermission"})
public class MyLocationLayerPlugin implements LocationEngineListener, CompassListener {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class could be decoupled into more smaller focused classes, as a result this would shrink most methods to a couple of lines.

private MapboxMap mapboxMap;
private MapView mapView;

// Enabled booleans
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I understand what Enabled booleans means

@cammace
Copy link
Contributor Author

cammace commented Jun 10, 2017

Much of the changes have been implemented, the code still needs a cleanup and I could use some help debugging a native crash. I'm trying to implement onMapChange event to handle when the map style changes but I receive a crash immediately when the style changes and a new location update occurs.

I've checked that all my sources and layers are getting created and added again to the new styles. @tobrun would you have any idea on resolving this?

06-09 18:52:21.296 7160-7160/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-09 18:52:21.296 7160-7160/? A/DEBUG: Build fingerprint: 'google/marlin/marlin:O/OPP2.170420.019/4021594:user/release-keys'
06-09 18:52:21.296 7160-7160/? A/DEBUG: Revision: '0'
06-09 18:52:21.296 7160-7160/? A/DEBUG: ABI: 'arm64'
06-09 18:52:21.296 7160-7160/? A/DEBUG: pid: 6945, tid: 6945, name: plugins.testapp  >>> com.mapbox.mapboxsdk.plugins.testapp <<<
06-09 18:52:21.296 7160-7160/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x18
06-09 18:52:21.296 7160-7160/? A/DEBUG: Cause: null pointer dereference
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x0   0000000000000000  x1   0000007fee8ce7f0  x2   0000007cc7a00000  x3   0000000000000003
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x4   00000000000000f5  x5   00000000000000ff  x6   0000000000000000  x7   0080808080808080
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x8   0000000000000001  x9   1b6b61b4fd30b014  x10  00000000000000f5  x11  0000000000000000
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x12  0101010101010101  x13  0000000000000008  x14  ffffffffffffffff  x15  000bd95c838413c4
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x16  0000007ce4750cc8  x17  0000007ce46ef51c  x18  000000000000000a  x19  0000007cc7d68740
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x20  1b6b61b4fd30b014  x21  0000000000000000  x22  0000007fee8ceb6c  x23  0000007ccaf10527
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x24  0000000000000008  x25  0000007ce26bea98  x26  0000000000000000  x27  0000000000000002
06-09 18:52:21.296 7160-7160/? A/DEBUG:     x28  0000000000000002  x29  0000007fee8ce890  x30  0000007cca3f60a8
06-09 18:52:21.296 7160-7160/? A/DEBUG:     sp   0000007fee8ce7b0  pc   0000007cca4addec  pstate 0000000080000000
06-09 18:52:21.299 7160-7160/? A/DEBUG: backtrace:
06-09 18:52:21.299 7160-7160/? A/DEBUG:     #00 pc 000000000026ddec  /data/app/com.mapbox.mapboxsdk.plugins.testapp-oJjyHUy2OFdXpJvFtqdfyg==/lib/arm64/libmapbox-gl.so
06-09 18:52:21.299 7160-7160/? A/DEBUG:     #01 pc 00000000001b60a4  /data/app/com.mapbox.mapboxsdk.plugins.testapp-oJjyHUy2OFdXpJvFtqdfyg==/lib/arm64/libmapbox-gl.so
06-09 18:52:21.299 7160-7160/? A/DEBUG:     #02 pc 00000000001b7b0c  /data/app/com.mapbox.mapboxsdk.plugins.testapp-oJjyHUy2OFdXpJvFtqdfyg==/lib/arm64/libmapbox-gl.so
06-09 18:52:21.299 7160-7160/? A/DEBUG:     #03 pc 000000000001f738  /data/app/com.mapbox.mapboxsdk.plugins.testapp-oJjyHUy2OFdXpJvFtqdfyg==/oat/arm64/base.odex (offset 0x11000)

@Grsmto
Copy link

Grsmto commented Jun 13, 2017

Hey!
I'm using this plugin on my project and I'm happy with it, it's working well!
As this is still under PR I'm providing 2 ideas of improvements for future release:

  • Give the ability to the user to customise the assets used for the icons (even tho they fit well with my app, we shouldn't be limited to the design provided in the plugin).
  • Being able to provide a custom location engine (or retrieve the currently used location engine) even tho this can be workarounded with setMyLocation)
    Thanks!

@cammace
Copy link
Contributor Author

cammace commented Jun 13, 2017

Hey @Grsmto! Glad to hear this plugin's working for you. If you pull down the latest code, you should be able to change the location layer drawables through your app's style.xml file:

<style name="CustomLocationLayer" parent="LocationLayer">
    <item name="backgroundDrawable">@drawable/custom_user_stroke_icon</item>
    <item name="foregroundDrawable">@drawable/custom_user_icon</item>
</style>

For setting the custom location engine, this should be done through the maps LocationSource prior to enabling the location layer plugin. So if you wanted to use a custom locationEngine, you create a new instance, set it to the map SDK using mapboxMap.setLocationSource(); and finally, set the location layer plugin mode using setMyLocationEnabled(). If this isn't working or doesn't seem right, perhaps we can expose a setLocationEngine() API.

@cammace
Copy link
Contributor Author

cammace commented Jun 17, 2017

I symbolicated (with little success) the crash log I posted above:

********** Crash dump: **********
Build fingerprint: 'Android/sdk_google_phone_x86_64/generic_x86_64:7.1.1/NYC/3756122:userdebug/test-keys'
pid: 13109, tid: 13109, name: plugins.testapp  >>> com.mapbox.mapboxsdk.plugins.testapp <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x30
Stack frame #00 pc 000000000017e424  /data/app/com.mapbox.mapboxsdk.plugins.testapp-2/lib/x86_64/libmapbox-gl.so
Stack frame #01 pc 000000000011f7ba  /data/app/com.mapbox.mapboxsdk.plugins.testapp-2/lib/x86_64/libmapbox-gl.so
Stack frame #02 pc 0000000000121bb2  /data/app/com.mapbox.mapboxsdk.plugins.testapp-2/lib/x86_64/libmapbox-gl.so
Stack frame #03 pc 0000000000121bf0  /data/app/com.mapbox.mapboxsdk.plugins.testapp-2/lib/x86_64/libmapbox-gl.so
Stack frame #04 pc 0000000000525d54  /data/app/com.mapbox.mapboxsdk.plugins.testapp-2/oat/x86_64/base.odex (offset 0x499000)
Stack frame #05 pc 00000000007f958f  <anonymous:00007fffe140f000>
Stack frame #06 pc 00000000002ea16e  /system/lib64/libart.so (_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameEPNS_6JValueE+190)
Crash dump is completed

I eventually found that an animation update listener was causing the crash and now remove it right before the style begins to get loaded which fixes the issue.

I'm now running into an issue with duplicate location events causing the animation to abruptly end after ~15 ms and then interpolates the same point for the actual time since the events happen right after another. I remember this being an issue in LOST.

I'd also like to add the Lifecycle callbacks to make this plugin even easier to use.

@cammace
Copy link
Contributor Author

cammace commented Jun 19, 2017

I've added the lifecycle observers and had to upgrade to the 5.1.0 beta since 5.0.2 was producing duplicate locationChange events. This PR's not ready for your eyes @tobrun @Guardiola31337 @zugaldia

@cammace
Copy link
Contributor Author

cammace commented Jun 23, 2017

This PR's not ready for your eyes @tobrun @Guardiola31337 @zugaldia

I meant to say it is now ready for your eyes 😄

@tobrun
Copy link
Member

tobrun commented Jul 6, 2017

Bearing not correctly restored when toggling styles:

device-2017-07-06-165227
device-2017-07-06-165237

Copy link
Member

@tobrun tobrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting to look 👍

Makefile Outdated

javadoc:
# Android modules
# Output is ./mapbox/*/build/docs/javadoc/release
cd plugins; ./gradlew :traffic:javadocrelease
cd plugins; ./gradlew :locationlayer:javadocrelease
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit white spacing

mapView.onLowMemory();
}

private static class StyleCycle {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now duplicated between activities, maybe extract in an utils instead?

public void onMapReady(MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
locationLayerPlugin = new LocationLayerPlugin(mapView, mapboxMap);
getLifecycle().addObserver(locationLayerPlugin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

mapSdk : 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.1.0',
mapboxServices : 'com.mapbox.mapboxsdk:mapbox-android-services:2.1.3',

// lifecycle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit white spacing

mapboxServices : 'com.mapbox.mapboxsdk:mapbox-android-services:2.1.3',

// lifecycle
lifecycleRuntime : 'android.arch.lifecycle:runtime:1.0.0-alpha3',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit white spacing

this.compassListener = compassListener;
}

void onStart() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we leverage constructs from the new architecture libs to avoid having methods as onStart/onPause?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onStart and onStop still need to be called from LocationLayerPlugin since an observer is only optional. If I added a lifecycle annotation here, it wouldn't resolve me having to call the onStart methods unless you had something else in mind and I misunderstood.

* @since 0.1.0
*/
@RequiresPermission(anyOf = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})
public void setLocationLayerEnabled(@LocationLayerMode.Mode int locationLayerMode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code in this method could be refactored into smaller parts


@SuppressWarnings( {"MissingPermission"})
@Override
public void onMapChanged(int change) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code below could be refactored into smaller parts

* @param bearingEnabled boolean true if you'd like to enable the user location bearing, otherwise, false will disable
* @since 0.1.0
*/
private void setMyBearingEnabled(boolean bearingEnabled) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code below could be simplified and lower the amounts of if/else blocks

* @param magneticHeading the raw compass heading
* @since 0.1.0
*/
private void bearingChangeAnimate(float magneticHeading) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code below could be refactored into smaller parts

@cammace
Copy link
Contributor Author

cammace commented Jul 8, 2017

The compass orientation matrix now supports remapping the axes when the device is rotated past 90 degrees in the y-axis. Previously with this plugin or the original myLocationView in the map SDK, if you were to rotate the phone on its y-axis, the compass icon would flip 180 at certain points. Remapping the matrix allows for this rotation to occur without the icon flipping and instead, remains in the same accurate orientation.

@cammace
Copy link
Contributor Author

cammace commented Jul 12, 2017

@tobrun @zugaldia ready for another review (hopefully final).

@zugaldia
Copy link
Member

@cammace CI is complaining about some NPE on the LineLayer, could you 👀 that before merging? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement location-layer-plugin Issues that deal with the location layer module ready for review When your PR has been personally reviewed, its time for an external contributors to approve
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants