Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
90ae9e3
Refactored to make InstructionLoader more modular in preparation for …
Aug 9, 2018
166228c
Added tests and cleanup
Jan 2, 2019
d51cb32
Added exit signs to the instruction loader
Jan 7, 2019
b443608
Got abbreviations and exit signs working in landscape orientation
Jan 7, 2019
ce4afa3
Refactored InstructionTextView into TextView and added support for le…
Jan 17, 2019
49e2464
Merge branch 'master' into devota-add-master-coordinator
Jan 17, 2019
491312f
Cleanup and fixed tests. Also fixed bug where abbreviations and highw…
Jan 17, 2019
ee21049
Merge branch 'master' into devota-add-master-coordinator
Jan 17, 2019
6ccd5d3
Fixed bug where secondary instructions were not being displayed
Jan 17, 2019
c4352f6
Merge branch 'master' into devota-add-master-coordinator
Jan 18, 2019
fb9c88e
Cleanup including changing access modifiers, and hopefully a fix for …
Jan 22, 2019
6283b28
Reverted InstructionTarget to previous implementation
Jan 22, 2019
4eec682
Cleanup, refactored some logic from InstructionTarget back into TextV…
Jan 23, 2019
21ae19b
Updated public API for InstructionLoader and fixed abbreviations so t…
Jan 23, 2019
a44030f
Merge branch 'master' into devota-add-master-coordinator
Jan 27, 2019
de9a31a
Changed outdated abstract class to interface and fixed instructionVie…
Jan 30, 2019
d28ba0b
Merge branch 'master' into devota-add-master-coordinator
Jan 30, 2019
4c2f6d4
Merge branch 'master' into devota-add-master-coordinator
Jan 30, 2019
92bc66a
Added magic numbers
Jan 30, 2019
7be09d2
Rolled back abbreviations fix for landscape
Jan 30, 2019
f625092
Updated left exit arrow so that it doesn't break on android 6.0
Jan 30, 2019
5f477ae
Changed access modifiers and made ImageCreator.getInstance synchronized
Jan 30, 2019
931a69e
Merge branch 'master' into devota-add-master-coordinator
Jan 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.services.android.navigation.ui.v5.camera.NavigationCamera;
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCoordinator;
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator;
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView;
import com.mapbox.services.android.navigation.ui.v5.instruction.NavigationAlertView;
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMap;
Expand Down Expand Up @@ -712,7 +712,7 @@ private void shutdown() {
navigationViewEventDispatcher.onDestroy(navigationViewModel.retrieveNavigation());
mapView.onDestroy();
navigationViewModel.onDestroy(isChangingConfigurations());
ImageCoordinator.getInstance().shutdown();
ImageCreator.getInstance().shutdown();
navigationMap = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import android.widget.TextView;

import com.mapbox.api.directions.v5.models.BannerComponents;
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionLoader.BannerComponentNode;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -17,18 +16,24 @@
* BannerComponents containing abbreviation information and given a list of BannerComponentNodes,
* constructed by InstructionLoader.
*/
class AbbreviationCoordinator {
class AbbreviationCreator extends NodeCreator<AbbreviationCreator.AbbreviationNode, AbbreviationVerifier> {
private static final String SINGLE_SPACE = " ";
private Map<Integer, List<Integer>> abbreviations;
private TextViewUtils textViewUtils;

AbbreviationCoordinator(TextViewUtils textViewUtils) {
this.abbreviations = new HashMap<>();
AbbreviationCreator(AbbreviationVerifier abbreviationVerifier, HashMap abbreviations,
TextViewUtils textViewUtils) {
super(abbreviationVerifier);
this.abbreviations = abbreviations;
this.textViewUtils = textViewUtils;
}

AbbreviationCoordinator() {
this(new TextViewUtils());
AbbreviationCreator(AbbreviationVerifier abbreviationVerifier) {
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed internally, should we remove this constructor? IMO it's not adding any value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Guardiola31337 What discussion? Are you talking about the discussion about Dan's PR? If you are, you said that you didn't feel strongly about that either way. I think this is adding the value of not making the InstructionLoader instantiate the hashmap and text utils while still making this testable. It's basically acting as a dependency injection utility method

this(abbreviationVerifier, new HashMap(), new TextViewUtils());
}

AbbreviationCreator() {
this(new AbbreviationVerifier());
}

/**
Expand All @@ -39,7 +44,7 @@ class AbbreviationCoordinator {
* @param bannerComponents object holding the abbreviation information
* @param index in the list of BannerComponentNodes
*/
void addPriorityInfo(BannerComponents bannerComponents, int index) {
private void addPriorityInfo(BannerComponents bannerComponents, int index) {
Integer abbreviationPriority = bannerComponents.abbreviationPriority();
if (abbreviations.get(abbreviationPriority) == null) {
abbreviations.put(abbreviationPriority, new ArrayList<Integer>());
Expand All @@ -51,11 +56,12 @@ void addPriorityInfo(BannerComponents bannerComponents, int index) {
* Using the abbreviations HashMap which should already be populated, abbreviates the text in the
* bannerComponentNodes until the text fits the given TextView.
*
* @param bannerComponentNodes containing the text to construct
* @param textView to check the text fits
* @param bannerComponentNodes containing the text to construct
* @return the properly abbreviated string that will fit in the TextView
*/
String abbreviateBannerText(List<BannerComponentNode> bannerComponentNodes, TextView textView) {
private String abbreviateBannerText(TextView textView, List<BannerComponentNode>
bannerComponentNodes) {
String bannerText = join(bannerComponentNodes);

if (abbreviations.isEmpty()) {
Expand Down Expand Up @@ -89,12 +95,15 @@ private String abbreviateUntilTextFits(TextView textView, String startingText,

private boolean shouldKeepAbbreviating(TextView textView, String bannerText,
int currAbbreviationPriority, int maxAbbreviationPriority) {
return !textViewUtils.textFits(textView, bannerText) && currAbbreviationPriority <= maxAbbreviationPriority;

boolean textFits = textViewUtils.textFits(textView, bannerText);
boolean abbreviationPrioritiesLeft = currAbbreviationPriority <= maxAbbreviationPriority;
return !textFits && abbreviationPrioritiesLeft;
}

private boolean abbreviateAtAbbreviationPriority(List<BannerComponentNode> bannerComponentNodes,
List<Integer> indices) {
if (indices == null) {
if (indices == null || indices.isEmpty()) {
return false;
}

Expand Down Expand Up @@ -130,11 +139,24 @@ private String join(List<BannerComponentNode> tokens) {
return stringBuilder.toString();
}

@Override
AbbreviationNode setupNode(BannerComponents components, int index, int startIndex, String
modifier) {
addPriorityInfo(components, index);
return new AbbreviationCreator.AbbreviationNode(components, startIndex);
}

@Override
void preProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
String text = abbreviateBannerText(textView, bannerComponentNodes);
textView.setText(text);
}

/**
* Class used by InstructionLoader to determine that a BannerComponent contains an abbreviation
*/
static class AbbreviationNode extends BannerComponentNode {
boolean abbreviate;
private boolean abbreviate;

AbbreviationNode(BannerComponents bannerComponents, int startIndex) {
super(bannerComponents, startIndex);
Expand All @@ -148,5 +170,9 @@ public String toString() {
void setAbbreviate(boolean abbreviate) {
this.abbreviate = abbreviate;
}

boolean getAbbreviate() {
return abbreviate;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.mapbox.services.android.navigation.ui.v5.instruction;

import com.mapbox.api.directions.v5.models.BannerComponents;
import com.mapbox.core.utils.TextUtils;

class AbbreviationVerifier extends NodeVerifier {
@Override
boolean isNodeType(BannerComponents bannerComponents) {
return hasAbbreviation(bannerComponents);
}

private boolean hasAbbreviation(BannerComponents components) {
return !TextUtils.isEmpty(components.abbreviation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.mapbox.services.android.navigation.ui.v5.instruction;

import com.mapbox.api.directions.v5.models.BannerComponents;

/**
* Class used to construct a list of BannerComponents to be populated into a TextView
*/
class BannerComponentNode {
BannerComponents bannerComponents;
int startIndex;

BannerComponentNode(BannerComponents bannerComponents, int startIndex) {
this.bannerComponents = bannerComponents;
this.startIndex = startIndex;
}

@Override
public String toString() {
return bannerComponents.text();
}

void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.mapbox.services.android.navigation.ui.v5.instruction;

import android.support.annotation.NonNull;
import android.widget.TextView;

import com.mapbox.api.directions.v5.models.BannerComponents;
import com.mapbox.api.directions.v5.models.BannerText;

import java.util.ArrayList;
import java.util.List;

class BannerComponentTree {
private final NodeCreator[] nodeCreators;
private final List<BannerComponentNode> bannerComponentNodes;

/**
* Creates a master coordinator to make sure the coordinators passed in are used appropriately
*
* @param nodeCreators coordinators in the order that they should process banner components
*/
BannerComponentTree(@NonNull BannerText bannerText, NodeCreator... nodeCreators) {
this.nodeCreators = nodeCreators;
bannerComponentNodes = parseBannerComponents(bannerText);
}

/**
* Parses the banner components and processes them using the nodeCreators in the order they
* were originally passed
*
* @param bannerText to parse
* @return the list of nodes representing the bannerComponents
*/
private List<BannerComponentNode> parseBannerComponents(BannerText bannerText) {
int length = 0;
List<BannerComponentNode> bannerComponentNodes = new ArrayList<>();

for (BannerComponents components : bannerText.components()) {
BannerComponentNode node = null;
for (NodeCreator nodeCreator : nodeCreators) {
if (nodeCreator.isNodeType(components)) {
node = nodeCreator.setupNode(components, bannerComponentNodes.size(), length,
bannerText.modifier());
break;
}
}

if (node != null) {
bannerComponentNodes.add(node);
length += components.text().length();
}
}

return bannerComponentNodes;
}

/**
* Loads the instruction into the given text view. If things have to be done in a particular order,
* the coordinator methods preProcess and postProcess can be used. PreProcess should be used to
* load text into the textView (so there should only be one coordinator calling this method), and
* postProcess should be used to make changes to that text, i.e., to load images into the textView.
*
* @param textView in which to load text and images
*/
void loadInstruction(TextView textView) {
for (NodeCreator nodeCreator : nodeCreators) {
nodeCreator.preProcess(textView, bannerComponentNodes);
}

for (NodeCreator nodeCreator : nodeCreators) {
nodeCreator.postProcess(textView, bannerComponentNodes);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.mapbox.services.android.navigation.ui.v5.instruction;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;

import com.mapbox.api.directions.v5.models.BannerComponents;
import com.mapbox.services.android.navigation.ui.v5.R;

import java.util.List;

class ExitSignCreator extends NodeCreator<BannerComponentNode, ExitSignVerifier> {
private String exitNumber;
private int startIndex;
private TextViewUtils textViewUtils;
private String modifier;

ExitSignCreator() {
super(new ExitSignVerifier());
textViewUtils = new TextViewUtils();
}

@Override
BannerComponentNode setupNode(BannerComponents components, int index, int startIndex, String
modifier) {
if (components.type().equals("exit")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Have we thought about extracting these magic numbers i.e. "exit", "exit-number" and "left" into constants?

return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we return null here? Not the same data as "exit-number"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@danesfeder this contains the text in the native language representing "exit", but for parity with iOS we are dropping this node (See screenshots, it doesn't say exit, it just says the exit number)

} else if (components.type().equals("exit-number")) {
exitNumber = components.text();
this.startIndex = startIndex;
this.modifier = modifier;
}

return new BannerComponentNode(components, startIndex);
}

/**
* One coordinator should override this method, and this should be the coordinator which populates
* the textView with text.
*
* @param textView to populate
* @param bannerComponentNodes containing instructions
*/
@Override
void postProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
if (exitNumber != null) {
LayoutInflater inflater = (LayoutInflater) textView.getContext().getSystemService(Context
.LAYOUT_INFLATER_SERVICE);

ViewGroup root = (ViewGroup) textView.getParent();

TextView exitSignView;

if (modifier.equals("left")) {
exitSignView = (TextView) inflater.inflate(R.layout.exit_sign_view_left, root, false);
} else {
exitSignView = (TextView) inflater.inflate(R.layout.exit_sign_view_right, root, false);
}

exitSignView.setText(exitNumber);

textViewUtils.setImageSpan(textView, exitSignView, startIndex, startIndex + exitNumber
.length());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mapbox.services.android.navigation.ui.v5.instruction;

import com.mapbox.api.directions.v5.models.BannerComponents;

class ExitSignVerifier extends NodeVerifier {

@Override
boolean isNodeType(BannerComponents bannerComponents) {
return bannerComponents.type().equals("exit") || bannerComponents.type().equals("exit-number");
}
}
Loading