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

Milestone #84

Merged
merged 9 commits into from
Jun 20, 2017
Merged

Milestone #84

merged 9 commits into from
Jun 20, 2017

Conversation

cammace
Copy link

@cammace cammace commented Jun 15, 2017

This PR removes the alert levels in favor of a more robust system called milestones. Milestones are more abstract, use the dsl pattern, offering some of the features listed below:

  • Easier readability
  • Besides the default, developers can create their own milestones
  • Customize what exactly triggers the milestone/alert

An example of creating a milestone's given below. An identifier's provided to later determine what milestone got triggered in the callback and the triggers. In this case, the milestones triggered only when all 3 of the conditions are true.

addMilestone(new StepMilestone.Builder()
      .setIdentifier(NavigationConstants.URGENT_MILESTONE)
      .setTrigger(
        Trigger.all(
          Trigger.gt(TriggerValue.STEP_DISTANCE, 100d),
          Trigger.lt(TriggerValue.DURATION, 15d),
          Trigger.neq(TriggerValue.STEP_INDEX, 0)
        )
      )
      .build()
    );
  • Add remaining statements
  • Finish adding TriggerProperties
  • Code cleanup
  • Add test
  • Javadoc

cc: @mapbox/navigation

@cammace cammace self-assigned this Jun 15, 2017
@cammace cammace added enhancement ⚠️ DO NOT MERGE PR should not be merged! labels Jun 15, 2017
@cammace cammace added this to the v0.4.0 milestone Jun 15, 2017
@cammace cammace added ready for review and removed ⚠️ DO NOT MERGE PR should not be merged! labels Jun 16, 2017
@1ec5
Copy link
Contributor

1ec5 commented Jun 19, 2017

A few questions to better understand the flexibility being provided here, with an eye toward potentially implementing an analogous system on iOS using predicates or callbacks as part of mapbox/mapbox-navigation-ios#224:

  1. How would a developer create a milestone that triggers 1 mile ahead of an exit ramp maneuver but 5 seconds ahead of any other maneuver?
  2. If the developer would like some of these heuristics to depend on application state, do they need to recreate the milestones each time this state changes? Is it possible to mutate a built-in milestone? I can think of two use cases for milestones that depend on application state:
    • A user setting in the form of a slider that adjusts the application’s verbosity. Perhaps the setting adjusts automatically based on whether music is playing.
    • Milestone durations increase in response to real-time weather data (visibility, precipitation) obtained by the application from a third-party API.
  3. Are there fixed semantics or a fixed ordering to the milestone identifiers provided by NavigationConstants? Is it necessary for a custom milestone’s identifier to fall between 2 and 3 in order to occur between NEW_STEP_MILESTONE and IMMINENT_MILESTONE?
  4. How are you planning to handle transitions around intermediate waypoints? Will the user get ARRIVAL_MILESTONE on each waypoint or only the final one; similarly, will the user get DEPARTURE_MILESTONE multiple times or only once?

@cammace
Copy link
Author

cammace commented Jun 19, 2017

  1. If I understand correctly, it should look like this.
Milestone milestone = new StepMilestone.Builder()
  .setIdentifier(MY_MILESTONE)
  .setTrigger(Trigger.all(
    Trigger.lte(TriggerProperty.STEP_DISTANCE_TOTAL, 1609.34d), // Mile converted to meters
    Trigger.gt(TriggerProperty.NEXT_STEP_DURATION, 5)// NEXT_STEP_DURATION property doesn't exist yet.
  )).build();
  1. Good point, currently the user would have to build a new milestone. I don't see this being a blocker however since the user can remove the old milestone and quickly build a new one with the updated params.

  2. There's no requirement for the ordering of the milestones, for example, you can have two duplicate milestones and the callback will be triggered twice with the respected milestone identifier. Milestones are completely independent of each other and aren't aware of one another, therefore, you can't specify a specific ordering of the milestones but instead, set the triggers at different times so one milestone always occurs before the other.

  3. A new default milestone will be added which will trigger when the user reaches a waypoint, the departure and arrival milestones should only happen when the route (not leg) event occurs.

@1ec5
Copy link
Contributor

1ec5 commented Jun 19, 2017

How would a developer create a milestone that triggers 1 mile ahead of an exit ramp maneuver but 5 seconds ahead of any other maneuver?

If I understand correctly, it should look like this. …

How does that distinguish between exit ramp maneuvers and other maneuver types? Or is that out of scope for this design?

@cammace
Copy link
Author

cammace commented Jun 19, 2017

In the future, we could always add triggers for specific milestones, currently, it is a simple implementation that was designed to match the behavior of the alert levels logic.

@cammace cammace requested a review from zugaldia June 19, 2017 21:24
Copy link
Member

@zugaldia zugaldia left a comment

Choose a reason for hiding this comment

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

This is a fantastic PR, very excited to see this landing soon. Just a few small comments.

Timber.d("Milestone Event Occurred with id: %d", identifier);
switch (identifier) {
case NavigationConstants.URGENT_MILESTONE:
Toast.makeText(this, "High Milestone", Toast.LENGTH_LONG).show();
Copy link
Member

Choose a reason for hiding this comment

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

Toast text doesn't match the milestone constant.

Toast.makeText(this, "High Milestone", Toast.LENGTH_LONG).show();
break;
case NavigationConstants.IMMINENT_MILESTONE:
Toast.makeText(this, "Medium Milestone", Toast.LENGTH_LONG).show();
Copy link
Member

Choose a reason for hiding this comment

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

Toast text doesn't match the milestone constant.

case NavigationConstants.ARRIVAL_MILESTONE:
Toast.makeText(this, "Arrival", Toast.LENGTH_LONG).show();
break;
default:
Copy link
Member

Choose a reason for hiding this comment

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

Can we make the milestone constants above an IntDef enum to make the default case unnecessary? Or is this a possibility? (in which case we'd need to show a Toast too).

Copy link
Author

Choose a reason for hiding this comment

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

I added the default for checkstyle to be happy. Looking back, it makes more sense to show a toast for the cases when the user adds a new milestone but doesn't do anything with it inside the switch statement.

Copy link
Member

Choose a reason for hiding this comment

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

@cammace 👍 let's add a Toast with some generic text then.

Copy link
Author

Choose a reason for hiding this comment

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

Added the toast on line 213, Github's just not showing it in the snippet above.

private void addDefaultMilestones() {
// High milestone
addMilestone(new StepMilestone.Builder()
.setIdentifier(NavigationConstants.URGENT_MILESTONE)
Copy link
Member

Choose a reason for hiding this comment

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

Comment text doesn't match text in constant.


// Medium milestone
addMilestone(new StepMilestone.Builder()
.setIdentifier(NavigationConstants.IMMINENT_MILESTONE)
Copy link
Member

Choose a reason for hiding this comment

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

Comment text doesn't match text in constant.

* @return true if the milestone trigger's valid, else false
* @since 0.4.0
*/
public abstract boolean validate(RouteProgress previousRouteProgress, RouteProgress routeProgress);
Copy link
Member

Choose a reason for hiding this comment

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

What does valid means in this context? That the milestone is properly constructed, or that the milestone is happening? If the latter, I think I'd change validate with something else. Considering it's a boolean, maybe something like milestone.isOccurring?

Copy link
Author

Choose a reason for hiding this comment

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

I like your naming much better 😄

}

static boolean notEqual(Number[] valueOne, Number valueTwo) {

Copy link
Member

Choose a reason for hiding this comment

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

Nit: extra line.

package com.mapbox.services.android.navigation.v5.milestone;


class Operation {
Copy link
Member

Choose a reason for hiding this comment

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

Even if this class isn't public I think it'd benefit from comment code.


@Override
public boolean validate(RouteProgress previousRouteProgress, RouteProgress routeProgress) {

Copy link
Member

Choose a reason for hiding this comment

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

Nit: extra line.


import java.util.Map;

public class Trigger {
Copy link
Member

Choose a reason for hiding this comment

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

Missing Javadoc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants