diff --git a/app/src/main/java/com/mapbox/services/android/navigation/testapp/activity/navigationui/ComponentNavigationActivity.java b/app/src/main/java/com/mapbox/services/android/navigation/testapp/activity/navigationui/ComponentNavigationActivity.java index 49c1117d8c4..8fa35f72d5a 100644 --- a/app/src/main/java/com/mapbox/services/android/navigation/testapp/activity/navigationui/ComponentNavigationActivity.java +++ b/app/src/main/java/com/mapbox/services/android/navigation/testapp/activity/navigationui/ComponentNavigationActivity.java @@ -238,12 +238,15 @@ public void onProgressChange(Location location, RouteProgress routeProgress) { updateLocation(location); // Update InstructionView data from RouteProgress - instructionView.update(routeProgress); + instructionView.updateDistanceWith(routeProgress); } @Override public void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) { playAnnouncement(milestone); + + // Update InstructionView banner instructions + instructionView.updateBannerInstructionsWith(milestone); } @Override diff --git a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/BannerInstructionModel.java b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/BannerInstructionModel.java index 9a4010ceaaf..1ca9aeef322 100644 --- a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/BannerInstructionModel.java +++ b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/BannerInstructionModel.java @@ -1,25 +1,48 @@ package com.mapbox.services.android.navigation.ui.v5.instruction; +import android.support.annotation.Nullable; + import com.mapbox.api.directions.v5.models.BannerInstructions; +import com.mapbox.api.directions.v5.models.BannerText; import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress; import com.mapbox.services.android.navigation.v5.utils.DistanceFormatter; public class BannerInstructionModel extends InstructionModel { + private final BannerText primaryBannerText; + private final BannerText secondaryBannerText; + private final BannerText subBannerText; + public BannerInstructionModel(DistanceFormatter distanceFormatter, RouteProgress progress, BannerInstructions instructions) { super(distanceFormatter, progress); primaryBannerText = instructions.primary(); secondaryBannerText = instructions.secondary(); + subBannerText = instructions.sub(); + } + + BannerText retrievePrimaryBannerText() { + return primaryBannerText; + } + + BannerText retrieveSecondaryBannerText() { + return secondaryBannerText; + } + + BannerText retrieveSubBannerText() { + return subBannerText; } - @Override - String getManeuverType() { + String retrievePrimaryManeuverType() { return primaryBannerText.type(); } - @Override - String getManeuverModifier() { + String retrieveSecondaryManeuverModifier() { return primaryBannerText.modifier(); } + + @Nullable + Double retrievePrimaryRoundaboutAngle() { + return primaryBannerText.degrees(); + } } diff --git a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionModel.java b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionModel.java index 21f6c270b93..fcfa0bcda8e 100644 --- a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionModel.java +++ b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionModel.java @@ -1,83 +1,81 @@ package com.mapbox.services.android.navigation.ui.v5.instruction; -import android.support.annotation.Nullable; +import android.text.SpannableString; -import com.mapbox.api.directions.v5.models.BannerText; +import com.mapbox.api.directions.v5.models.IntersectionLanes; import com.mapbox.api.directions.v5.models.LegStep; -import com.mapbox.services.android.navigation.v5.routeprogress.RouteLegProgress; +import com.mapbox.api.directions.v5.models.StepIntersection; import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress; import com.mapbox.services.android.navigation.v5.utils.DistanceFormatter; -import com.mapbox.services.android.navigation.v5.utils.RouteUtils; + +import java.util.List; public class InstructionModel { - BannerText primaryBannerText; - BannerText secondaryBannerText; - private BannerText thenBannerText; - private Float roundaboutAngle = null; - private InstructionStepResources stepResources; private RouteProgress progress; - private RouteUtils routeUtils; + private SpannableString stepDistanceRemaining; + private List turnLanes; + private String upcomingManeuverModifier; public InstructionModel(DistanceFormatter distanceFormatter, RouteProgress progress) { this.progress = progress; - routeUtils = new RouteUtils(); - buildInstructionModel(distanceFormatter, progress); - } - - BannerText getPrimaryBannerText() { - return primaryBannerText; - } - - BannerText getSecondaryBannerText() { - return secondaryBannerText; - } - - BannerText getThenBannerText() { - return thenBannerText; + double distanceRemaining = progress.currentLegProgress().currentStepProgress().distanceRemaining(); + stepDistanceRemaining = distanceFormatter.formatDistance(distanceRemaining); + extractStepResources(progress); } - @Nullable - Float getRoundaboutAngle() { - return roundaboutAngle; + RouteProgress retrieveProgress() { + return progress; } - InstructionStepResources getStepResources() { - return stepResources; + SpannableString retrieveStepDistanceRemaining() { + return stepDistanceRemaining; } - String getManeuverType() { - return stepResources.getManeuverViewType(); + String retrieveUpcomingManeuverModifier() { + return upcomingManeuverModifier; } - String getManeuverModifier() { - return stepResources.getManeuverViewModifier(); + List retrieveTurnLanes() { + return turnLanes; } - RouteProgress getProgress() { - return progress; + private void extractStepResources(RouteProgress progress) { + LegStep upcomingStep = progress.currentLegProgress().upComingStep(); + if (upcomingStep != null) { + if (hasIntersections(upcomingStep)) { + intersectionTurnLanes(upcomingStep); + } + upcomingManeuverModifier = upcomingStep.maneuver().modifier(); + } } - private void buildInstructionModel(DistanceFormatter distanceFormatter, RouteProgress progress) { - stepResources = new InstructionStepResources(distanceFormatter, progress); - extractStepInstructions(progress); + private void intersectionTurnLanes(LegStep step) { + StepIntersection intersection = step.intersections().get(0); + List lanes = intersection.lanes(); + if (checkForNoneIndications(lanes)) { + turnLanes = null; + return; + } + turnLanes = lanes; } - private void extractStepInstructions(RouteProgress progress) { - RouteLegProgress legProgress = progress.currentLegProgress(); - LegStep currentStep = progress.currentLegProgress().currentStep(); - LegStep upComingStep = legProgress.upComingStep(); - int stepDistanceRemaining = (int) legProgress.currentStepProgress().distanceRemaining(); - - primaryBannerText = routeUtils.findCurrentBannerText(currentStep, stepDistanceRemaining, true); - secondaryBannerText = routeUtils.findCurrentBannerText(currentStep, stepDistanceRemaining, false); - - if (upComingStep != null) { - thenBannerText = routeUtils.findCurrentBannerText(upComingStep, upComingStep.distance(), true); + private boolean checkForNoneIndications(List lanes) { + if (lanes == null) { + return true; } - - if (primaryBannerText != null && primaryBannerText.degrees() != null) { - roundaboutAngle = primaryBannerText.degrees().floatValue(); + for (IntersectionLanes lane : lanes) { + for (String indication : lane.indications()) { + if (indication.contains("none")) { + return true; + } + } } + return false; + } + + private boolean hasIntersections(LegStep step) { + return step.intersections() != null + && step.intersections().get(0) != null; } } diff --git a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionStepResources.java b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionStepResources.java deleted file mode 100644 index 4ab244a26f3..00000000000 --- a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionStepResources.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.mapbox.services.android.navigation.ui.v5.instruction; - -import android.support.annotation.Nullable; -import android.text.SpannableString; - -import com.mapbox.api.directions.v5.models.BannerInstructions; -import com.mapbox.api.directions.v5.models.BannerText; -import com.mapbox.api.directions.v5.models.IntersectionLanes; -import com.mapbox.api.directions.v5.models.LegStep; -import com.mapbox.api.directions.v5.models.StepIntersection; -import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress; -import com.mapbox.services.android.navigation.v5.utils.DistanceFormatter; - -import java.util.List; - -class InstructionStepResources { - - private static final double VALID_UPCOMING_DURATION = 25d * 1.2d; - private static final double VALID_CURRENT_DURATION = 70d; - private static final int FIRST_INSTRUCTION = 0; - - private SpannableString stepDistanceRemaining; - private String maneuverViewModifier; - private String maneuverViewType; - private String thenStepManeuverModifier; - private String thenStepManeuverType; - private Float thenStepRoundaboutDegrees; - private List turnLanes; - private boolean shouldShowThenStep; - - InstructionStepResources(DistanceFormatter distanceFormatter, RouteProgress progress) { - double distanceRemaining = progress.currentLegProgress().currentStepProgress().distanceRemaining(); - stepDistanceRemaining = distanceFormatter.formatDistance(distanceRemaining); - extractStepResources(progress); - } - - SpannableString getStepDistanceRemaining() { - return stepDistanceRemaining; - } - - String getManeuverViewModifier() { - return maneuverViewModifier; - } - - String getManeuverViewType() { - return maneuverViewType; - } - - String getThenStepManeuverModifier() { - return thenStepManeuverModifier; - } - - String getThenStepManeuverType() { - return thenStepManeuverType; - } - - @Nullable - Float getThenStepRoundaboutDegrees() { - return thenStepRoundaboutDegrees; - } - - boolean shouldShowThenStep() { - return shouldShowThenStep; - } - - List getTurnLanes() { - return turnLanes; - } - - private void extractStepResources(RouteProgress progress) { - LegStep currentStep = progress.currentLegProgress().currentStep(); - LegStep upcomingStep = progress.currentLegProgress().upComingStep(); - LegStep followOnStep = progress.currentLegProgress().followOnStep(); - - // Type / Modifier / Text - if (upcomingStep != null) { - maneuverViewType = upcomingStep.maneuver().type(); - maneuverViewModifier = upcomingStep.maneuver().modifier(); - - // Then step (step after upcoming) - if (followOnStep != null) { - thenStep(upcomingStep, followOnStep, progress.currentLegProgress().currentStepProgress().durationRemaining()); - } - - // Turn lane data - if (hasIntersections(upcomingStep)) { - intersectionTurnLanes(upcomingStep); - } - } else { - maneuverViewType = currentStep.maneuver().type(); - maneuverViewModifier = currentStep.maneuver().modifier(); - } - } - - private void intersectionTurnLanes(LegStep step) { - StepIntersection intersection = step.intersections().get(0); - List lanes = intersection.lanes(); - if (checkForNoneIndications(lanes)) { - turnLanes = null; - return; - } - turnLanes = lanes; - } - - private void thenStep(LegStep upcomingStep, LegStep followOnStep, double currentDurationRemaining) { - List bannerInstructions = followOnStep.bannerInstructions(); - if (bannerInstructions == null || bannerInstructions.isEmpty()) { - return; - } - BannerText primaryText = bannerInstructions.get(FIRST_INSTRUCTION).primary(); - thenStepManeuverType = primaryText.type(); - thenStepManeuverModifier = primaryText.modifier(); - if (primaryText.degrees() != null) { - thenStepRoundaboutDegrees = primaryText.degrees().floatValue(); - } - shouldShowThenStep = isValidStepDuration(upcomingStep, currentDurationRemaining); - } - - private boolean isValidStepDuration(LegStep upcomingStep, double currentDurationRemaining) { - return upcomingStep.duration() <= VALID_UPCOMING_DURATION - && currentDurationRemaining <= VALID_CURRENT_DURATION; - } - - private boolean checkForNoneIndications(List lanes) { - if (lanes == null) { - return true; - } - for (IntersectionLanes lane : lanes) { - for (String indication : lane.indications()) { - if (indication.contains("none")) { - return true; - } - } - } - return false; - } - - private boolean hasIntersections(LegStep step) { - return step.intersections() != null - && step.intersections().get(0) != null; - } -} diff --git a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionView.java b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionView.java index 9b15b3b14fd..90b2a728920 100644 --- a/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionView.java +++ b/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionView.java @@ -82,12 +82,12 @@ public class InstructionView extends RelativeLayout implements FeedbackBottomShe private TextView upcomingDistanceText; private TextView upcomingPrimaryText; private TextView upcomingSecondaryText; - private ManeuverView thenManeuverView; - private TextView thenStepText; + private ManeuverView subManeuverView; + private TextView subStepText; private NavigationAlertView alertView; private View rerouteLayout; private View turnLaneLayout; - private View thenStepLayout; + private View subStepLayout; private RecyclerView rvTurnLanes; private RecyclerView rvInstructions; private TurnLaneAdapter turnLaneAdapter; @@ -189,8 +189,10 @@ public void onChanged(@Nullable InstructionModel model) { @Override public void onChanged(@Nullable BannerInstructionModel model) { if (model != null) { - updateManeuverView(model.getManeuverType(), model.getManeuverModifier(), model.getRoundaboutAngle()); - updateDataFromBannerText(model.getPrimaryBannerText(), model.getSecondaryBannerText()); + updateManeuverView(model.retrievePrimaryManeuverType(), model.retrieveSecondaryManeuverModifier(), + model.retrievePrimaryRoundaboutAngle()); + updateDataFromBannerText(model.retrievePrimaryBannerText(), model.retrieveSecondaryBannerText()); + updateSubStep(model.retrieveSubBannerText()); } } }); @@ -213,25 +215,6 @@ public void onChanged(@Nullable Boolean isOffRoute) { showButtons(); } - /** - * Called in {@link ProgressChangeListener}, creates a new model and then - * uses it to update the views. - * - * @param routeProgress used to provide navigation / routeProgress data - * @since 0.6.2 - * @deprecated As of 0.20.0, use a combination of {@link InstructionView#updateDistanceWith(RouteProgress)} and - * {@link InstructionView#updateBannerInstructionsWith(Milestone)} to achieve the same behavior. - */ - @Deprecated - public void update(RouteProgress routeProgress) { - if (routeProgress != null && !isRerouting) { - InstructionModel model = new InstructionModel(distanceFormatter, routeProgress); - updateDataFromInstruction(model); - updateManeuverView(model.getManeuverType(), model.getManeuverModifier(), model.getRoundaboutAngle()); - updateDataFromBannerText(model.getPrimaryBannerText(), model.getSecondaryBannerText()); - } - } - /** * Use this method inside a {@link ProgressChangeListener} to update this view with all other information * that is not updated by the {@link InstructionView#updateBannerInstructionsWith(Milestone)}. @@ -265,8 +248,9 @@ public void updateBannerInstructionsWith(Milestone milestone) { return; } BannerText primary = instructions.primary(); - updateManeuverView(primary.type(), primary.modifier(), extractRoundaboutDegreesFrom(primary)); + updateManeuverView(primary.type(), primary.modifier(), primary.degrees()); updateDataFromBannerText(primary, instructions.secondary()); + updateSubStep(instructions.sub()); } } @@ -412,12 +396,12 @@ private void bind() { upcomingDistanceText = findViewById(R.id.stepDistanceText); upcomingPrimaryText = findViewById(R.id.stepPrimaryText); upcomingSecondaryText = findViewById(R.id.stepSecondaryText); - thenManeuverView = findViewById(R.id.thenManeuverView); - thenStepText = findViewById(R.id.thenStepText); + subManeuverView = findViewById(R.id.subManeuverView); + subStepText = findViewById(R.id.subStepText); alertView = findViewById(R.id.alertView); rerouteLayout = findViewById(R.id.rerouteLayout); turnLaneLayout = findViewById(R.id.turnLaneLayout); - thenStepLayout = findViewById(R.id.thenStepLayout); + subStepLayout = findViewById(R.id.subStepLayout); rvTurnLanes = findViewById(R.id.rvTurnLanes); instructionLayout = findViewById(R.id.instructionLayout); instructionLayoutText = findViewById(R.id.instructionLayoutText); @@ -444,9 +428,9 @@ private void initializeBackground() { Drawable maneuverBackground = DrawableCompat.wrap(instructionLayoutManeuver.getBackground()).mutate(); DrawableCompat.setTint(maneuverBackground, navigationViewBannerBackgroundColor); - View thenStepLayout = findViewById(R.id.thenStepLayout); - Drawable thenStepBackground = DrawableCompat.wrap(thenStepLayout.getBackground()).mutate(); - DrawableCompat.setTint(thenStepBackground, navigationViewListBackgroundColor); + View subStepLayout = findViewById(R.id.subStepLayout); + Drawable subStepBackground = DrawableCompat.wrap(subStepLayout.getBackground()).mutate(); + DrawableCompat.setTint(subStepBackground, navigationViewListBackgroundColor); View turnLaneLayout = findViewById(R.id.turnLaneLayout); Drawable turnLaneBackground = DrawableCompat.wrap(turnLaneLayout.getBackground()).mutate(); @@ -597,9 +581,9 @@ public void onClick(View instructionLayoutText) { */ private boolean newDistanceText(InstructionModel model) { return !upcomingDistanceText.getText().toString().isEmpty() - && !TextUtils.isEmpty(model.getStepResources().getStepDistanceRemaining()) + && !TextUtils.isEmpty(model.retrieveStepDistanceRemaining()) && !upcomingDistanceText.getText().toString() - .contentEquals(model.getStepResources().getStepDistanceRemaining().toString()); + .contentEquals(model.retrieveStepDistanceRemaining().toString()); } /** @@ -608,7 +592,7 @@ private boolean newDistanceText(InstructionModel model) { * @param model provides distance text */ private void distanceText(InstructionModel model) { - upcomingDistanceText.setText(model.getStepResources().getStepDistanceRemaining()); + upcomingDistanceText.setText(model.retrieveStepDistanceRemaining()); } private InstructionLoader createInstructionLoader(TextView textView, BannerText bannerText) { @@ -642,9 +626,9 @@ private boolean newStep(RouteProgress routeProgress) { * @param model created with new {@link RouteProgress} holding turn lane data */ private void updateTurnLanes(InstructionModel model) { - List turnLanes = model.getStepResources().getTurnLanes(); - String maneuverViewModifier = model.getManeuverModifier(); - double durationRemaining = model.getProgress().currentLegProgress().currentStepProgress().durationRemaining(); + List turnLanes = model.retrieveTurnLanes(); + String maneuverViewModifier = model.retrieveUpcomingManeuverModifier(); + double durationRemaining = model.retrieveProgress().currentLegProgress().currentStepProgress().durationRemaining(); if (shouldShowTurnLanes(turnLanes, maneuverViewModifier, durationRemaining)) { if (turnLaneLayout.getVisibility() == GONE) { @@ -683,59 +667,43 @@ private void hideTurnLanes() { } /** - * Check if the the then step should be shown. + * Check if the the sub step should be shown. * If true, update the "then" maneuver and the "then" step text. * If false, hide the then layout. * - * @param model to determine if the then step layout should be shown - */ - private void updateThenStep(InstructionModel model) { - if (shouldShowThenStep(model)) { - String thenStepManeuverType = model.getStepResources().getThenStepManeuverType(); - String thenStepManeuverModifier = model.getStepResources().getThenStepManeuverModifier(); - thenManeuverView.setManeuverTypeAndModifier(thenStepManeuverType, thenStepManeuverModifier); - Float roundaboutAngle = model.getStepResources().getThenStepRoundaboutDegrees(); + * @param subText to determine if the then step layout should be shown + */ + private void updateSubStep(BannerText subText) { + if (shouldShowSubStep(subText)) { + String maneuverType = subText.type(); + String maneuverModifier = subText.modifier(); + subManeuverView.setManeuverTypeAndModifier(maneuverType, maneuverModifier); + Double roundaboutAngle = subText.degrees(); if (roundaboutAngle != null) { - thenManeuverView.setRoundaboutAngle(roundaboutAngle); + subManeuverView.setRoundaboutAngle(roundaboutAngle.floatValue()); } - thenStepText.setText(model.getThenBannerText().text()); + subStepText.setText(subText.text()); showThenStepLayout(); } else { hideThenStepLayout(); } } - /** - * First, checks if the turn lanes are visible (if they are, don't show then step). - * Second, checks if the upcoming step is less than 15 seconds long. - * This is our cue to show the thenStep. - * - * @param model to check the upcoming step - * @return true if should show, false if not - */ - private boolean shouldShowThenStep(InstructionModel model) { - return turnLaneLayout.getVisibility() != VISIBLE - && model.getThenBannerText() != null - && model.getStepResources().shouldShowThenStep(); + private boolean shouldShowSubStep(@Nullable BannerText subText) { + return turnLaneLayout.getVisibility() != VISIBLE && subText != null; } - /** - * Shows then step layout - */ private void showThenStepLayout() { - if (thenStepLayout.getVisibility() == GONE) { + if (subStepLayout.getVisibility() == GONE) { beginDelayedTransition(); - thenStepLayout.setVisibility(VISIBLE); + subStepLayout.setVisibility(VISIBLE); } } - /** - * Hides then step layout - */ private void hideThenStepLayout() { - if (thenStepLayout.getVisibility() == VISIBLE) { + if (subStepLayout.getVisibility() == VISIBLE) { beginDelayedTransition(); - thenStepLayout.setVisibility(GONE); + subStepLayout.setVisibility(GONE); } } @@ -783,9 +751,8 @@ private void updateDataFromInstruction(InstructionModel model) { updateDistanceText(model); updateInstructionList(model); updateTurnLanes(model); - updateThenStep(model); - if (newStep(model.getProgress())) { - LegStep upComingStep = model.getProgress().currentLegProgress().upComingStep(); + if (newStep(model.retrieveProgress())) { + LegStep upComingStep = model.retrieveProgress().currentLegProgress().upComingStep(); ImageCoordinator.getInstance().prefetchImageCache(upComingStep); } } @@ -823,20 +790,11 @@ private void updateDataFromBannerText(BannerText primaryBannerText, BannerText s * Updates new maneuver image if one is found. */ private void updateManeuverView(String maneuverViewType, String maneuverViewModifier, - @Nullable Float roundaboutAngle) { + @Nullable Double roundaboutAngle) { upcomingManeuverView.setManeuverTypeAndModifier(maneuverViewType, maneuverViewModifier); if (roundaboutAngle != null) { - upcomingManeuverView.setRoundaboutAngle(roundaboutAngle); - } - } - - @Nullable - private Float extractRoundaboutDegreesFrom(BannerText bannerText) { - Double degrees = bannerText.degrees(); - if (degrees != null) { - return degrees.floatValue(); + upcomingManeuverView.setRoundaboutAngle(roundaboutAngle.floatValue()); } - return null; } /** @@ -865,7 +823,7 @@ private void updateLandscapeConstraintsTo(int layoutRes) { * @param model to provide the current steps and unit type */ private void updateInstructionList(InstructionModel model) { - RouteProgress routeProgress = model.getProgress(); + RouteProgress routeProgress = model.retrieveProgress(); boolean isListShowing = instructionListLayout.getVisibility() == VISIBLE; instructionListAdapter.updateBannerListWith(routeProgress, isListShowing); } diff --git a/libandroid-navigation-ui/src/main/res/layout-land/instruction_layout.xml b/libandroid-navigation-ui/src/main/res/layout-land/instruction_layout.xml index 24c20cd0137..5725230f648 100644 --- a/libandroid-navigation-ui/src/main/res/layout-land/instruction_layout.xml +++ b/libandroid-navigation-ui/src/main/res/layout-land/instruction_layout.xml @@ -109,8 +109,8 @@ app:layout_constraintTop_toBottomOf="@+id/instructionLayoutText"/>