Improve display of transport routes badges in context menu

This commit is contained in:
Chumva 2018-09-25 15:37:51 +03:00
parent cadc1fc57b
commit 4a6c5c28c2
5 changed files with 206 additions and 87 deletions

View file

@ -72,6 +72,14 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/transport_badges_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default">
<GridView <GridView
android:id="@+id/transport_stop_routes_grid" android:id="@+id/transport_stop_routes_grid"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -80,12 +88,23 @@
android:horizontalSpacing="@dimen/context_menu_transport_grid_spacing" android:horizontalSpacing="@dimen/context_menu_transport_grid_spacing"
android:numColumns="auto_fit" android:numColumns="auto_fit"
android:paddingBottom="@dimen/context_menu_transport_grid_spacing" android:paddingBottom="@dimen/context_menu_transport_grid_spacing"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:paddingTop="@dimen/context_menu_transport_padding_top" android:paddingTop="@dimen/context_menu_transport_padding_top"
android:verticalSpacing="@dimen/context_menu_transport_grid_spacing" android:verticalSpacing="@dimen/context_menu_transport_grid_spacing"
android:visibility="gone" /> android:visibility="gone" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/local_routes_more_text_view"
style="@style/TextAppearance.ContextMenuSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/context_menu_padding_margin_small"
android:layout_marginRight="@dimen/context_menu_padding_margin_small"
android:text="@string/more_transport_on_stop_hint"
android:textSize="@dimen/default_sub_text_size"
android:visibility="gone"
osmand:typeface="@string/font_roboto_medium" />
<LinearLayout <LinearLayout
android:id="@+id/nearby_routes" android:id="@+id/nearby_routes"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -94,8 +113,6 @@
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny" android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
android:gravity="top" android:gravity="top"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default"
android:paddingTop="3dp"> android:paddingTop="3dp">
<net.osmand.plus.widgets.TextViewEx <net.osmand.plus.widgets.TextViewEx
@ -119,6 +136,9 @@
android:numColumns="auto_fit" android:numColumns="auto_fit"
android:verticalSpacing="@dimen/context_menu_transport_grid_spacing" android:verticalSpacing="@dimen/context_menu_transport_grid_spacing"
android:visibility="gone" /> android:visibility="gone" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View file

@ -11,6 +11,7 @@
Thx - Hardy Thx - Hardy
--> -->
<string name="more_transport_on_stop_hint">There is more transport on this stop.</string>
<string name="ask_for_location_permission">Please give OsmAnd permission for location to continue.</string> <string name="ask_for_location_permission">Please give OsmAnd permission for location to continue.</string>
<string name="thank_you_for_feedback">Thank you for feedback</string> <string name="thank_you_for_feedback">Thank you for feedback</string>
<string name="poi_cannot_be_found">Node or way cannot be found.</string> <string name="poi_cannot_be_found">Node or way cannot be found.</string>

View file

@ -344,6 +344,13 @@ public class AndroidUtils {
: ctx.getResources().getColor(R.color.secondary_text_light)); : ctx.getResources().getColor(R.color.secondary_text_light));
} }
public static int getTextWidth(float textSize, String text) {
Paint paint = new Paint();
paint.setTextSize(textSize);
return (int) paint.measureText(text);
}
public static int dpToPx(Context ctx, float dp) { public static int dpToPx(Context ctx, float dp) {
Resources r = ctx.getResources(); Resources r = ctx.getResources();
return (int) TypedValue.applyDimension( return (int) TypedValue.applyDimension(

View file

@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@ -80,7 +81,7 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
public static final int CURRENT_Y_UNDEFINED = Integer.MAX_VALUE; public static final int CURRENT_Y_UNDEFINED = Integer.MAX_VALUE;
private static final int MAX_TRANSPORT_ROUTES_BADGES = 6; private static final String TRANSPORT_BADGE_MORE_ITEM = "...";
private View view; private View view;
private InterceptorLinearLayout mainView; private InterceptorLinearLayout mainView;
@ -92,6 +93,13 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
private View topButtonContainer; private View topButtonContainer;
private LockableScrollView menuScrollView; private LockableScrollView menuScrollView;
private LinearLayout nearbyRoutesLayout;
private LinearLayout routesBadgesContainer;
private GridView localTransportStopRoutesGrid;
private GridView nearbyTransportStopRoutesGrid;
private TextView nearbyRoutesWithinTv;
private TextView localRoutesMoreTv;
private View zoomButtonsView; private View zoomButtonsView;
private ImageButton zoomInButtonView; private ImageButton zoomInButtonView;
private ImageButton zoomOutButtonView; private ImageButton zoomOutButtonView;
@ -137,6 +145,8 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
private int screenOrientation; private int screenOrientation;
private boolean created; private boolean created;
private boolean transportBadgesCreated;
private UpdateLocationViewCache updateLocationViewCache; private UpdateLocationViewCache updateLocationViewCache;
@Override @Override
@ -495,32 +505,19 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
zoomButtonsView.setVisibility(View.GONE); zoomButtonsView.setVisibility(View.GONE);
} }
GridView localTransportStopRoutesGrid = (GridView) view.findViewById(R.id.transport_stop_routes_grid); localTransportStopRoutesGrid = (GridView) view.findViewById(R.id.transport_stop_routes_grid);
GridView nearbyTransportStopRoutesGrid = (GridView) view.findViewById(R.id.transport_stop_nearby_routes_grid); nearbyTransportStopRoutesGrid = (GridView) view.findViewById(R.id.transport_stop_nearby_routes_grid);
TextView nearbRoutesWithinTv = (TextView) view.findViewById(R.id.nearby_routes_within_text_view); nearbyRoutesWithinTv = (TextView) view.findViewById(R.id.nearby_routes_within_text_view);
LinearLayout nearbyRoutesLayout = (LinearLayout) view.findViewById(R.id.nearby_routes); localRoutesMoreTv = (TextView) view.findViewById(R.id.local_routes_more_text_view);
nearbyRoutesLayout = (LinearLayout) view.findViewById(R.id.nearby_routes);
routesBadgesContainer = (LinearLayout) view.findViewById(R.id.transport_badges_container);
List<TransportStopRoute> localTransportStopRoutes = menu.getLocalTransportStopRoutes();
List<TransportStopRoute> nearbyTransportStopRoutes = menu.getNearbyTransportStopRoutes();
if (nightMode) { if (nightMode) {
nearbRoutesWithinTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_bottom_view_secondary_text_color_dark)); nearbyRoutesWithinTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_bottom_view_secondary_text_color_dark));
localRoutesMoreTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_bottom_view_secondary_text_color_dark));
} else { } else {
nearbRoutesWithinTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_nearby_routes_text_color_dark)); nearbyRoutesWithinTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_nearby_routes_text_color_dark));
} localRoutesMoreTv.setTextColor(ContextCompat.getColor(getContext(), R.color.ctx_menu_nearby_routes_text_color_dark));
if (localTransportStopRoutes != null && localTransportStopRoutes.size() > 0) {
localTransportStopRoutesGrid.setAdapter(createTransportStopRouteAdapter(localTransportStopRoutes));
localTransportStopRoutesGrid.setVisibility(View.VISIBLE);
} else {
localTransportStopRoutesGrid.setVisibility(View.GONE);
}
if (nearbyTransportStopRoutes != null && nearbyTransportStopRoutes.size() > 0) {
String nearInDistance = getMyApplication().getString(R.string.transport_nearby_routes) + " "
+ OsmAndFormatter.getFormattedDistance(TransportStopController.SHOW_STOPS_RADIUS_METERS, getMyApplication()) +":";
nearbRoutesWithinTv.setText(nearInDistance);
nearbyTransportStopRoutesGrid.setAdapter(createTransportStopRouteAdapter(nearbyTransportStopRoutes));
nearbyTransportStopRoutesGrid.setVisibility(View.VISIBLE);
} else {
nearbyRoutesLayout.setVisibility(View.GONE);
} }
View buttonsBottomBorder = view.findViewById(R.id.buttons_bottom_border); View buttonsBottomBorder = view.findViewById(R.id.buttons_bottom_border);
@ -631,6 +628,9 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
@Override @Override
public void onLayoutChange(View view, int left, int top, int right, int bottom, public void onLayoutChange(View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) { int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (!transportBadgesCreated) {
createTransportBadges();
}
if (forceUpdateLayout || bottom != oldBottom) { if (forceUpdateLayout || bottom != oldBottom) {
forceUpdateLayout = false; forceUpdateLayout = false;
processScreenHeight(view.getParent()); processScreenHeight(view.getParent());
@ -643,13 +643,19 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
return view; return view;
} }
private TransportStopRouteAdapter createTransportStopRouteAdapter(List<TransportStopRoute> routes) { private TransportStopRouteAdapter createTransportStopRouteAdapter(List<TransportStopRoute> routes, boolean needMoreItem) {
final TransportStopRouteAdapter adapter = new TransportStopRouteAdapter(getMyApplication(), filterTransportRoutes(routes), nightMode); List<Object> items = new ArrayList<Object>(routes);
if (needMoreItem) {
items.add(TRANSPORT_BADGE_MORE_ITEM);
}
final TransportStopRouteAdapter adapter = new TransportStopRouteAdapter(getMyApplication(), items, nightMode);
adapter.setListener(new TransportStopRouteAdapter.OnClickListener() { adapter.setListener(new TransportStopRouteAdapter.OnClickListener() {
@Override @Override
public void onClick(int position) { public void onClick(int position) {
TransportStopRoute route = adapter.getItem(position); Object object = adapter.getItem(position);
if (route != null) { if (object != null) {
if (object instanceof TransportStopRoute) {
TransportStopRoute route = (TransportStopRoute) object;
PointDescription pd = new PointDescription(PointDescription.POINT_TYPE_TRANSPORT_ROUTE, PointDescription pd = new PointDescription(PointDescription.POINT_TYPE_TRANSPORT_ROUTE,
route.getDescription(getMapActivity().getMyApplication(), false)); route.getDescription(getMapActivity().getMyApplication(), false));
menu.show(menu.getLatLon(), pd, route); menu.show(menu.getLatLon(), pd, route);
@ -657,6 +663,15 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
stopsLayer.setRoute(route); stopsLayer.setRoute(route);
int cz = route.calculateZoom(0, getMapActivity().getMapView().getCurrentRotatedTileBox()); int cz = route.calculateZoom(0, getMapActivity().getMapView().getCurrentRotatedTileBox());
getMapActivity().changeZoom(cz - getMapActivity().getMapView().getZoom()); getMapActivity().changeZoom(cz - getMapActivity().getMapView().getZoom());
} else if (object instanceof String) {
if (object.equals(TRANSPORT_BADGE_MORE_ITEM)) {
if (menu.isLandscapeLayout()) {
changeMenuState(getFullScreenTopPosY(), false, false);
} else {
openMenuFullScreen();
}
}
}
} }
} }
}); });
@ -669,9 +684,6 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
if (!containsRef(filteredRoutes, route.route)) { if (!containsRef(filteredRoutes, route.route)) {
filteredRoutes.add(route); filteredRoutes.add(route);
} }
if (filteredRoutes.size() >= MAX_TRANSPORT_ROUTES_BADGES) {
break;
}
} }
return filteredRoutes; return filteredRoutes;
} }
@ -1273,6 +1285,76 @@ public class MapContextMenuFragment extends BaseOsmAndFragment implements Downlo
} }
} }
private void createTransportBadges() {
if (!transportBadgesCreated) {
List<TransportStopRoute> localTransportStopRoutes = filterTransportRoutes(menu.getLocalTransportStopRoutes());
List<TransportStopRoute> nearbyTransportStopRoutes = filterTransportRoutes(menu.getNearbyTransportStopRoutes());
int localColumnsPerRow = getRoutesBadgesColumnsPerRow(null);
int maxLocalRows = (int) Math.round(Math.ceil((double) localTransportStopRoutes.size() / localColumnsPerRow));
updateLocalRoutesBadges(localTransportStopRoutes, localColumnsPerRow);
updateNearbyRoutesBadges(maxLocalRows, nearbyTransportStopRoutes);
transportBadgesCreated = true;
}
}
private void updateLocalRoutesBadges(List<TransportStopRoute> localTransportStopRoutes, int localColumnsPerRow) {
int localRoutesSize = localTransportStopRoutes.size();
if (localRoutesSize > 0) {
int maxLocalBadges = localColumnsPerRow * 5;
TransportStopRouteAdapter adapter;
if (localRoutesSize > maxLocalBadges) {
adapter = createTransportStopRouteAdapter(localTransportStopRoutes.subList(0, maxLocalBadges), false);
localRoutesMoreTv.setVisibility(View.VISIBLE);
} else {
adapter = createTransportStopRouteAdapter(localTransportStopRoutes, false);
localRoutesMoreTv.setVisibility(View.GONE);
}
localTransportStopRoutesGrid.setAdapter(adapter);
localTransportStopRoutesGrid.setVisibility(View.VISIBLE);
} else {
localTransportStopRoutesGrid.setVisibility(View.GONE);
localRoutesMoreTv.setVisibility(View.GONE);
}
}
private void updateNearbyRoutesBadges(int maxLocalRows, List<TransportStopRoute> nearbyTransportStopRoutes) {
int nearbyRoutesSize = nearbyTransportStopRoutes.size();
boolean moreLocalItems = localRoutesMoreTv.getVisibility() == View.VISIBLE;
if (maxLocalRows <= 5 && !moreLocalItems && nearbyRoutesSize > 0) {
String nearInDistance = getString(R.string.transport_nearby_routes) + " "
+ OsmAndFormatter.getFormattedDistance(TransportStopController.SHOW_STOPS_RADIUS_METERS, getMyApplication()) + ":";
nearbyRoutesWithinTv.setText(nearInDistance);
int nearbyColumnsPerRow = getRoutesBadgesColumnsPerRow(nearInDistance);
int maxNearbyRows = Math.min(3, 6 - maxLocalRows);
int nearbyMaxItems = maxNearbyRows * nearbyColumnsPerRow - 1;
TransportStopRouteAdapter adapter;
if (nearbyRoutesSize > nearbyMaxItems) {
adapter = createTransportStopRouteAdapter(nearbyTransportStopRoutes.subList(0, nearbyMaxItems), true);
} else {
adapter = createTransportStopRouteAdapter(nearbyTransportStopRoutes, false);
}
nearbyTransportStopRoutesGrid.setAdapter(adapter);
nearbyTransportStopRoutesGrid.setVisibility(View.VISIBLE);
} else {
nearbyRoutesLayout.setVisibility(View.GONE);
}
}
private int getRoutesBadgesColumnsPerRow(@Nullable String nearInDistance) {
double badgeWidth = getResources().getDimension(R.dimen.context_menu_transport_grid_item_width);
double gridSpacing = getResources().getDimension(R.dimen.context_menu_transport_grid_spacing);
double gridPadding = getResources().getDimension(R.dimen.context_menu_padding_margin_default);
int availableSpace;
if (nearInDistance == null) {
availableSpace = (int) (routesBadgesContainer.getWidth() - gridPadding * 2);
} else {
int textWidth = AndroidUtils.getTextWidth(getResources().getDimensionPixelSize(R.dimen.default_sub_text_size), nearInDistance);
double paddingTv = getResources().getDimension(R.dimen.context_menu_padding_margin_small);
availableSpace = (int) (routesBadgesContainer.getWidth() - gridPadding * 2 - paddingTv - textWidth);
}
return (int) ((availableSpace + gridSpacing) / (badgeWidth + gridSpacing));
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void runLayoutListener() { private void runLayoutListener() {
if (view != null) { if (view != null) {

View file

@ -3,6 +3,7 @@ package net.osmand.plus.mapcontextmenu;
import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -16,13 +17,13 @@ import net.osmand.plus.transport.TransportStopRoute;
import java.util.List; import java.util.List;
public class TransportStopRouteAdapter extends ArrayAdapter<TransportStopRoute> { public class TransportStopRouteAdapter extends ArrayAdapter<Object> {
private boolean nightMode; private boolean nightMode;
private OnClickListener listener; private OnClickListener listener;
private OsmandApplication app; private OsmandApplication app;
public TransportStopRouteAdapter(@NonNull OsmandApplication application, @NonNull List<TransportStopRoute> objects, boolean nightMode) { public TransportStopRouteAdapter(@NonNull OsmandApplication application, @NonNull List<Object> objects, boolean nightMode) {
super(application, 0, objects); super(application, 0, objects);
this.nightMode = nightMode; this.nightMode = nightMode;
this.app = application; this.app = application;
@ -38,16 +39,23 @@ public class TransportStopRouteAdapter extends ArrayAdapter<TransportStopRoute>
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.transport_stop_route_item, parent, false); convertView = LayoutInflater.from(getContext()).inflate(R.layout.transport_stop_route_item, parent, false);
} }
Object object = getItem(position);
TransportStopRoute transportStopRoute = getItem(position); if (object != null) {
if (transportStopRoute != null) { String routeRef = "";
int bgColor = 0;
if (object instanceof TransportStopRoute) {
TransportStopRoute transportStopRoute = (TransportStopRoute) object;
routeRef = getAdjustedRouteRef(transportStopRoute.route.getRef());
bgColor = transportStopRoute.getColor(app, nightMode);
} else if (object instanceof String) {
routeRef = (String) object;
bgColor = ContextCompat.getColor(app, R.color.route_info_unchecked_mode_icon_color);
}
TextView transportStopRouteTextView = (TextView) convertView.findViewById(R.id.transport_stop_route_text); TextView transportStopRouteTextView = (TextView) convertView.findViewById(R.id.transport_stop_route_text);
transportStopRouteTextView.setText(getAdjustedRouteRef(transportStopRoute.route.getRef())); transportStopRouteTextView.setText(routeRef);
GradientDrawable gradientDrawableBg = (GradientDrawable) transportStopRouteTextView.getBackground(); GradientDrawable gradientDrawableBg = (GradientDrawable) transportStopRouteTextView.getBackground();
int bgColor = transportStopRoute.getColor(app, nightMode);
gradientDrawableBg.setColor(bgColor); gradientDrawableBg.setColor(bgColor);
transportStopRouteTextView.setTextColor(UiUtilities.getContrastColor(app, bgColor, true)); transportStopRouteTextView.setTextColor(UiUtilities.getContrastColor(app, bgColor, true));
}
convertView.setOnClickListener(new View.OnClickListener() { convertView.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -57,6 +65,7 @@ public class TransportStopRouteAdapter extends ArrayAdapter<TransportStopRoute>
} }
} }
}); });
}
return convertView; return convertView;
} }