Skip to content

Commit 82e63fd

Browse files
author
Devota Aabel
authored
Added exit signs to the instruction banner and refactored instruction loader (#1195)
1 parent 34c538b commit 82e63fd

27 files changed

+685
-310
lines changed

libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/NavigationView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
2828
import com.mapbox.mapboxsdk.maps.Style;
2929
import com.mapbox.services.android.navigation.ui.v5.camera.NavigationCamera;
30-
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCoordinator;
30+
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator;
3131
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView;
3232
import com.mapbox.services.android.navigation.ui.v5.instruction.NavigationAlertView;
3333
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMap;
@@ -712,7 +712,7 @@ private void shutdown() {
712712
navigationViewEventDispatcher.onDestroy(navigationViewModel.retrieveNavigation());
713713
mapView.onDestroy();
714714
navigationViewModel.onDestroy(isChangingConfigurations());
715-
ImageCoordinator.getInstance().shutdown();
715+
ImageCreator.getInstance().shutdown();
716716
navigationMap = null;
717717
}
718718
}
Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import android.widget.TextView;
44

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

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

25-
AbbreviationCoordinator(TextViewUtils textViewUtils) {
26-
this.abbreviations = new HashMap<>();
24+
AbbreviationCreator(AbbreviationVerifier abbreviationVerifier, HashMap abbreviations,
25+
TextViewUtils textViewUtils) {
26+
super(abbreviationVerifier);
27+
this.abbreviations = abbreviations;
2728
this.textViewUtils = textViewUtils;
2829
}
2930

30-
AbbreviationCoordinator() {
31-
this(new TextViewUtils());
31+
AbbreviationCreator(AbbreviationVerifier abbreviationVerifier) {
32+
this(abbreviationVerifier, new HashMap(), new TextViewUtils());
33+
}
34+
35+
AbbreviationCreator() {
36+
this(new AbbreviationVerifier());
3237
}
3338

3439
/**
@@ -39,7 +44,7 @@ class AbbreviationCoordinator {
3944
* @param bannerComponents object holding the abbreviation information
4045
* @param index in the list of BannerComponentNodes
4146
*/
42-
void addPriorityInfo(BannerComponents bannerComponents, int index) {
47+
private void addPriorityInfo(BannerComponents bannerComponents, int index) {
4348
Integer abbreviationPriority = bannerComponents.abbreviationPriority();
4449
if (abbreviations.get(abbreviationPriority) == null) {
4550
abbreviations.put(abbreviationPriority, new ArrayList<Integer>());
@@ -51,11 +56,12 @@ void addPriorityInfo(BannerComponents bannerComponents, int index) {
5156
* Using the abbreviations HashMap which should already be populated, abbreviates the text in the
5257
* bannerComponentNodes until the text fits the given TextView.
5358
*
54-
* @param bannerComponentNodes containing the text to construct
5559
* @param textView to check the text fits
60+
* @param bannerComponentNodes containing the text to construct
5661
* @return the properly abbreviated string that will fit in the TextView
5762
*/
58-
String abbreviateBannerText(List<BannerComponentNode> bannerComponentNodes, TextView textView) {
63+
private String abbreviateBannerText(TextView textView, List<BannerComponentNode>
64+
bannerComponentNodes) {
5965
String bannerText = join(bannerComponentNodes);
6066

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

9096
private boolean shouldKeepAbbreviating(TextView textView, String bannerText,
9197
int currAbbreviationPriority, int maxAbbreviationPriority) {
92-
return !textViewUtils.textFits(textView, bannerText) && currAbbreviationPriority <= maxAbbreviationPriority;
98+
99+
boolean textFits = textViewUtils.textFits(textView, bannerText);
100+
boolean abbreviationPrioritiesLeft = currAbbreviationPriority <= maxAbbreviationPriority;
101+
return !textFits && abbreviationPrioritiesLeft;
93102
}
94103

95104
private boolean abbreviateAtAbbreviationPriority(List<BannerComponentNode> bannerComponentNodes,
96105
List<Integer> indices) {
97-
if (indices == null) {
106+
if (indices == null || indices.isEmpty()) {
98107
return false;
99108
}
100109

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

142+
@Override
143+
AbbreviationNode setupNode(BannerComponents components, int index, int startIndex, String
144+
modifier) {
145+
addPriorityInfo(components, index);
146+
return new AbbreviationCreator.AbbreviationNode(components, startIndex);
147+
}
148+
149+
@Override
150+
void preProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
151+
String text = abbreviateBannerText(textView, bannerComponentNodes);
152+
textView.setText(text);
153+
}
154+
133155
/**
134156
* Class used by InstructionLoader to determine that a BannerComponent contains an abbreviation
135157
*/
136158
static class AbbreviationNode extends BannerComponentNode {
137-
boolean abbreviate;
159+
private boolean abbreviate;
138160

139161
AbbreviationNode(BannerComponents bannerComponents, int startIndex) {
140162
super(bannerComponents, startIndex);
@@ -148,5 +170,9 @@ public String toString() {
148170
void setAbbreviate(boolean abbreviate) {
149171
this.abbreviate = abbreviate;
150172
}
173+
174+
boolean getAbbreviate() {
175+
return abbreviate;
176+
}
151177
}
152178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import com.mapbox.api.directions.v5.models.BannerComponents;
4+
import com.mapbox.core.utils.TextUtils;
5+
6+
class AbbreviationVerifier implements NodeVerifier {
7+
@Override
8+
public boolean isNodeType(BannerComponents bannerComponents) {
9+
return hasAbbreviation(bannerComponents);
10+
}
11+
12+
private boolean hasAbbreviation(BannerComponents components) {
13+
return !TextUtils.isEmpty(components.abbreviation());
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import com.mapbox.api.directions.v5.models.BannerComponents;
4+
5+
/**
6+
* Class used to construct a list of BannerComponents to be populated into a TextView
7+
*/
8+
class BannerComponentNode {
9+
BannerComponents bannerComponents;
10+
int startIndex;
11+
12+
BannerComponentNode(BannerComponents bannerComponents, int startIndex) {
13+
this.bannerComponents = bannerComponents;
14+
this.startIndex = startIndex;
15+
}
16+
17+
@Override
18+
public String toString() {
19+
return bannerComponents.text();
20+
}
21+
22+
void setStartIndex(int startIndex) {
23+
this.startIndex = startIndex;
24+
}
25+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import android.support.annotation.NonNull;
4+
import android.widget.TextView;
5+
6+
import com.mapbox.api.directions.v5.models.BannerComponents;
7+
import com.mapbox.api.directions.v5.models.BannerText;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
class BannerComponentTree {
13+
private final NodeCreator[] nodeCreators;
14+
private final List<BannerComponentNode> bannerComponentNodes;
15+
16+
/**
17+
* Creates a master coordinator to make sure the coordinators passed in are used appropriately
18+
*
19+
* @param nodeCreators coordinators in the order that they should process banner components
20+
*/
21+
BannerComponentTree(@NonNull BannerText bannerText, NodeCreator... nodeCreators) {
22+
this.nodeCreators = nodeCreators;
23+
bannerComponentNodes = parseBannerComponents(bannerText);
24+
}
25+
26+
/**
27+
* Parses the banner components and processes them using the nodeCreators in the order they
28+
* were originally passed
29+
*
30+
* @param bannerText to parse
31+
* @return the list of nodes representing the bannerComponents
32+
*/
33+
private List<BannerComponentNode> parseBannerComponents(BannerText bannerText) {
34+
int length = 0;
35+
List<BannerComponentNode> bannerComponentNodes = new ArrayList<>();
36+
37+
for (BannerComponents components : bannerText.components()) {
38+
BannerComponentNode node = null;
39+
for (NodeCreator nodeCreator : nodeCreators) {
40+
if (nodeCreator.isNodeType(components)) {
41+
node = nodeCreator.setupNode(components, bannerComponentNodes.size(), length,
42+
bannerText.modifier());
43+
break;
44+
}
45+
}
46+
47+
if (node != null) {
48+
bannerComponentNodes.add(node);
49+
length += components.text().length();
50+
}
51+
}
52+
53+
return bannerComponentNodes;
54+
}
55+
56+
/**
57+
* Loads the instruction into the given text view. If things have to be done in a particular order,
58+
* the coordinator methods preProcess and postProcess can be used. PreProcess should be used to
59+
* load text into the textView (so there should only be one coordinator calling this method), and
60+
* postProcess should be used to make changes to that text, i.e., to load images into the textView.
61+
*
62+
* @param textView in which to load text and images
63+
*/
64+
void loadInstruction(TextView textView) {
65+
for (NodeCreator nodeCreator : nodeCreators) {
66+
nodeCreator.preProcess(textView, bannerComponentNodes);
67+
}
68+
69+
for (NodeCreator nodeCreator : nodeCreators) {
70+
nodeCreator.postProcess(textView, bannerComponentNodes);
71+
}
72+
}
73+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import android.content.Context;
4+
import android.view.LayoutInflater;
5+
import android.view.ViewGroup;
6+
import android.widget.TextView;
7+
8+
import com.mapbox.api.directions.v5.models.BannerComponents;
9+
import com.mapbox.services.android.navigation.ui.v5.R;
10+
11+
import java.util.List;
12+
13+
class ExitSignCreator extends NodeCreator<BannerComponentNode, ExitSignVerifier> {
14+
private String exitNumber;
15+
private int startIndex;
16+
private TextViewUtils textViewUtils;
17+
private String modifier;
18+
private static final String EXIT = "exit";
19+
private static final String EXIT_NUMBER = "exit-number";
20+
private static final String LEFT = "left";
21+
22+
ExitSignCreator() {
23+
super(new ExitSignVerifier());
24+
textViewUtils = new TextViewUtils();
25+
}
26+
27+
@Override
28+
BannerComponentNode setupNode(BannerComponents components, int index, int startIndex,
29+
String modifier) {
30+
if (components.type().equals(EXIT)) {
31+
return null;
32+
} else if (components.type().equals(EXIT_NUMBER)) {
33+
exitNumber = components.text();
34+
this.startIndex = startIndex;
35+
this.modifier = modifier;
36+
}
37+
38+
return new BannerComponentNode(components, startIndex);
39+
}
40+
41+
/**
42+
* One coordinator should override this method, and this should be the coordinator which populates
43+
* the textView with text.
44+
*
45+
* @param textView to populate
46+
* @param bannerComponentNodes containing instructions
47+
*/
48+
@Override
49+
void postProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
50+
if (exitNumber != null) {
51+
LayoutInflater inflater = (LayoutInflater) textView.getContext().getSystemService(Context
52+
.LAYOUT_INFLATER_SERVICE);
53+
54+
ViewGroup root = (ViewGroup) textView.getParent();
55+
56+
TextView exitSignView;
57+
58+
if (modifier.equals(LEFT)) {
59+
exitSignView = (TextView) inflater.inflate(R.layout.exit_sign_view_left, root, false);
60+
} else {
61+
exitSignView = (TextView) inflater.inflate(R.layout.exit_sign_view_right, root, false);
62+
}
63+
64+
exitSignView.setText(exitNumber);
65+
66+
textViewUtils.setImageSpan(textView, exitSignView, startIndex, startIndex + exitNumber
67+
.length());
68+
}
69+
}
70+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import com.mapbox.api.directions.v5.models.BannerComponents;
4+
5+
class ExitSignVerifier implements NodeVerifier {
6+
7+
@Override
8+
public boolean isNodeType(BannerComponents bannerComponents) {
9+
return bannerComponents.type().equals("exit") || bannerComponents.type().equals("exit-number");
10+
}
11+
}

0 commit comments

Comments
 (0)