Skip to content

Commit f73829c

Browse files
author
Devota Aabel
committed
Refactored to make InstructionLoader more modular in preparation for adding exit signs
1 parent d56e07d commit f73829c

File tree

17 files changed

+363
-208
lines changed

17 files changed

+363
-208
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
@@ -24,7 +24,7 @@
2424
import com.mapbox.mapboxsdk.maps.MapView;
2525
import com.mapbox.mapboxsdk.maps.MapboxMap;
2626
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
27-
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCoordinator;
27+
import com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator;
2828
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView;
2929
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMap;
3030
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMapInstanceState;
@@ -608,7 +608,7 @@ private void subscribeViewModels() {
608608
private void shutdown() {
609609
mapView.onDestroy();
610610
navigationViewModel.onDestroy(isChangingConfigurations());
611-
ImageCoordinator.getInstance().shutdown();
611+
ImageCreator.getInstance().shutdown();
612612
navigationMap = null;
613613
}
614614
}
Lines changed: 19 additions & 7 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,19 @@
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) {
24+
AbbreviationCreator(TextViewUtils textViewUtils, AbbreviationVerifier abbreviationVerifier) {
25+
super(abbreviationVerifier);
2626
this.abbreviations = new HashMap<>();
2727
this.textViewUtils = textViewUtils;
2828
}
2929

30-
AbbreviationCoordinator() {
31-
this(new TextViewUtils());
30+
AbbreviationCreator() {
31+
this(new TextViewUtils(), new AbbreviationVerifier());
3232
}
3333

3434
/**
@@ -51,11 +51,11 @@ void addPriorityInfo(BannerComponents bannerComponents, int index) {
5151
* Using the abbreviations HashMap which should already be populated, abbreviates the text in the
5252
* bannerComponentNodes until the text fits the given TextView.
5353
*
54-
* @param bannerComponentNodes containing the text to construct
5554
* @param textView to check the text fits
55+
* @param bannerComponentNodes containing the text to construct
5656
* @return the properly abbreviated string that will fit in the TextView
5757
*/
58-
String abbreviateBannerText(List<BannerComponentNode> bannerComponentNodes, TextView textView) {
58+
String abbreviateBannerText(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
5959
String bannerText = join(bannerComponentNodes);
6060

6161
if (abbreviations.isEmpty()) {
@@ -130,6 +130,18 @@ private String join(List<BannerComponentNode> tokens) {
130130
return stringBuilder.toString();
131131
}
132132

133+
@Override
134+
AbbreviationNode setupNode(BannerComponents components, int index, int startIndex) {
135+
addPriorityInfo(components, index);
136+
return new AbbreviationCreator.AbbreviationNode(components, startIndex);
137+
}
138+
139+
@Override
140+
void preProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
141+
String text = abbreviateBannerText(textView, bannerComponentNodes);
142+
textView.setText(text);
143+
}
144+
133145
/**
134146
* Class used by InstructionLoader to determine that a BannerComponent contains an abbreviation
135147
*/
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 extends NodeVerifier {
7+
@Override
8+
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
public class BannerComponentTree {
12+
private final NodeCreator[] nodeCreators;
13+
private List<BannerComponentNode> bannerComponentNodes;
14+
15+
/**
16+
* Creates a master coordinator to make sure the coordinators passed in are used appropriately
17+
*
18+
* @param nodeCreators coordinators in the order that they should process banner components
19+
*/
20+
BannerComponentTree(@NonNull List<BannerComponents> bannerComponents, NodeCreator... nodeCreators) {
21+
this.nodeCreators = nodeCreators;
22+
bannerComponentNodes = parseBannerComponents(bannerComponents);
23+
}
24+
25+
/**
26+
* Parses the banner components and processes them using the nodeCreators in the order they
27+
* were originally passed
28+
*
29+
* @param bannerComponents to parse
30+
* @return the list of nodes representing the bannerComponents
31+
*/
32+
List<BannerComponentNode> parseBannerComponents(List<BannerComponents> bannerComponents) {
33+
int length = 0;
34+
List<BannerComponentNode> bannerComponentNodes = new ArrayList<>();
35+
36+
for (BannerComponents components : bannerComponents) {
37+
BannerComponentNode node = null;
38+
39+
for (NodeCreator nodeCreator : nodeCreators) {
40+
if (nodeCreator.isNodeType(components)) {
41+
node = nodeCreator.setupNode(components, bannerComponentNodes.size(), length - 1);
42+
break;
43+
}
44+
}
45+
46+
if (node != null) {
47+
bannerComponentNodes.add(node);
48+
length += components.text().length() + 1;
49+
}
50+
}
51+
52+
return bannerComponentNodes;
53+
}
54+
55+
/**
56+
* Loads the instruction into the given text view. If things have to be done in a particular order,
57+
* the coordinator methods preProcess and postProcess can be used. PreProcess should be used to
58+
* load text into the textView (so there should only be one coordinator calling this method), and
59+
* postProcess should be used to make changes to that text, i.e., to load images into the textView.
60+
*
61+
* @param textView in which to load text and images
62+
*/
63+
void loadInstruction(TextView textView) {
64+
for (NodeCreator nodeCreator : nodeCreators) {
65+
nodeCreator.preProcess(textView, bannerComponentNodes);
66+
}
67+
68+
for (NodeCreator nodeCreator : nodeCreators) {
69+
nodeCreator.postProcess(textView, bannerComponentNodes);
70+
}
71+
}
72+
}
Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
import android.content.Context;
44
import android.text.Spannable;
55
import android.text.SpannableString;
6-
import android.text.TextUtils;
76
import android.text.style.ImageSpan;
87
import android.widget.TextView;
98

109
import com.mapbox.api.directions.v5.models.BannerComponents;
1110
import com.mapbox.api.directions.v5.models.BannerInstructions;
1211
import com.mapbox.api.directions.v5.models.BannerText;
1312
import com.mapbox.api.directions.v5.models.LegStep;
14-
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionLoader.BannerComponentNode;
1513
import com.squareup.picasso.Picasso;
1614

1715
import java.util.ArrayList;
@@ -27,26 +25,55 @@
2725
* If a shield URL is found, {@link Picasso} is used to load the image. Then, once the image is loaded,
2826
* a new {@link ImageSpan} is created and set to the appropriate position of the {@link Spannable}
2927
*/
30-
public class ImageCoordinator {
28+
public class ImageCreator extends NodeCreator<ImageCreator.ImageNode, ImageVerifier> {
3129

32-
private static ImageCoordinator instance;
30+
private static ImageCreator instance;
3331
private boolean isInitialized;
3432
private Picasso picassoImageLoader;
3533
private List<InstructionTarget> targets;
3634
private UrlDensityMap urlDensityMap;
3735
private List<BannerShield> bannerShieldList;
3836

39-
private ImageCoordinator() {
37+
private ImageCreator(ImageVerifier imageVerifier) {
38+
super(imageVerifier);
39+
}
40+
41+
@Override
42+
ImageCreator.ImageNode setupNode(BannerComponents components, int index, int startIndex) {
43+
addShieldInfo(components, index);
44+
return new ImageCreator.ImageNode(components, startIndex);
45+
}
46+
47+
/**
48+
* Uses the given BannerComponents object to construct a BannerShield object containing the
49+
* information needed to load the proper image into the TextView where appropriate.
50+
*
51+
* @param bannerComponents containing image info
52+
* @param index of the BannerComponentNode which refers to the given BannerComponents
53+
*/
54+
public void addShieldInfo(BannerComponents bannerComponents, int index) {
55+
bannerShieldList.add(new BannerShield(bannerComponents, index));
56+
}
57+
58+
static class ImageNode extends BannerComponentNode {
59+
60+
ImageNode(BannerComponents bannerComponents, int startIndex) {
61+
super(bannerComponents, startIndex);
62+
}
4063
}
4164

4265
/**
4366
* Primary access method (using singleton pattern)
4467
*
4568
* @return InstructionLoader
4669
*/
47-
public static synchronized ImageCoordinator getInstance() {
70+
public static ImageCreator getInstance() {
71+
return getInstance(new ImageVerifier());
72+
}
73+
74+
public static synchronized ImageCreator getInstance(ImageVerifier imageVerifier) {
4875
if (instance == null) {
49-
instance = new ImageCoordinator();
76+
instance = new ImageCreator(imageVerifier);
5077
}
5178

5279
return instance;
@@ -68,16 +95,7 @@ public void initialize(Context context) {
6895
}
6996
}
7097

71-
/**
72-
* Uses the given BannerComponents object to construct a BannerShield object containing the
73-
* information needed to load the proper image into the TextView where appropriate.
74-
*
75-
* @param bannerComponents containing image info
76-
* @param index of the BannerComponentNode which refers to the given BannerComponents
77-
*/
78-
public void addShieldInfo(BannerComponents bannerComponents, int index) {
79-
bannerShieldList.add(new BannerShield(bannerComponents, index));
80-
}
98+
8199

82100
/**
83101
* Will pre-fetch images for a given {@link LegStep}.
@@ -164,16 +182,12 @@ private boolean hasImages() {
164182
*/
165183
private void fetchImageBaseUrls(BannerText bannerText) {
166184
for (BannerComponents components : bannerText.components()) {
167-
if (hasImageUrl(components)) {
185+
if (nodeVerifier.hasImageUrl(components)) {
168186
picassoImageLoader.load(urlDensityMap.get(components.imageBaseUrl())).fetch();
169187
}
170188
}
171189
}
172190

173-
private boolean hasImageUrl(BannerComponents components) {
174-
return !TextUtils.isEmpty(components.imageBaseUrl());
175-
}
176-
177191
private void createTargets(TextView textView) {
178192
Spannable instructionSpannable = new SpannableString(textView.getText());
179193
for (final BannerShield bannerShield : bannerShieldList) {
@@ -201,10 +215,8 @@ private void checkIsInitialized() {
201215
}
202216
}
203217

204-
static class ImageNode extends BannerComponentNode {
205-
206-
ImageNode(BannerComponents bannerComponents, int startIndex) {
207-
super(bannerComponents, startIndex);
208-
}
218+
@Override
219+
void postProcess(TextView textView, List<BannerComponentNode> bannerComponentNodes) {
220+
loadImages(textView, bannerComponentNodes);
209221
}
210222
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.mapbox.services.android.navigation.ui.v5.instruction;
2+
3+
import android.text.TextUtils;
4+
5+
import com.mapbox.api.directions.v5.models.BannerComponents;
6+
7+
public class ImageVerifier extends NodeVerifier {
8+
9+
@Override
10+
boolean isNodeType(BannerComponents bannerComponents) {
11+
return hasImageUrl(bannerComponents);
12+
}
13+
14+
protected boolean hasImageUrl(BannerComponents components) {
15+
return !TextUtils.isEmpty(components.imageBaseUrl());
16+
}
17+
}

0 commit comments

Comments
 (0)