package net.osmand.plus.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import net.osmand.data.LatLon;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopTextView;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarView;
import net.osmand.plus.views.mapwidgets.MapMarkersWidgetsFactory;
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry;
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.MapWidgetRegInfo;
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.WidgetState;
import net.osmand.plus.views.mapwidgets.NextTurnInfoWidget;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory.AlarmWidget;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory.BearingWidgetState;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory.LanesControl;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory.RulerWidget;
import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory.TimeControlWidgetState;
import net.osmand.plus.views.mapwidgets.TextInfoWidget;
import java.lang.reflect.Field;
public class MapInfoLayer extends OsmandMapLayer {
private final MapActivity map;
private final RouteLayer routeLayer;
private OsmandMapTileView view;
// groups
private LinearLayout rightStack;
private LinearLayout leftStack;
private ImageButton expand;
private static boolean expanded = false;
private LanesControl lanesControl;
private AlarmWidget alarmControl;
private RulerWidget rulerControl;
private MapWidgetRegistry mapInfoControls;
private OsmandSettings settings;
private DrawSettings drawSettings;
private TopTextView streetNameView;
private TopToolbarView topToolbarView;
private LatLon selectedPointLatLon;
public MapInfoLayer(MapActivity map, RouteLayer layer){
this.map = map;
settings = map.getMyApplication().getSettings();
this.routeLayer = layer;
}
public MapWidgetRegistry getMapInfoControls() {
return mapInfoControls;
}
public MapActivity getMapActivity() {
return map;
}
@Override
public void initLayer(final OsmandMapTileView view) {
this.view = view;
mapInfoControls = map.getMapLayers().getMapWidgetRegistry() ;
leftStack = (LinearLayout) map.findViewById(R.id.map_left_widgets_panel);
rightStack = (LinearLayout) map.findViewById(R.id.map_right_widgets_panel);
expand = (ImageButton) map.findViewById(R.id.map_collapse_button);
// update and create controls
registerAllControls();
map.getMyApplication().getAidlApi().registerWidgetControls(map);
recreateControls();
}
public MapWidgetRegInfo registerSideWidget(TextInfoWidget widget, int drawableMenu,
int messageId, String key, boolean left, int priorityOrder) {
MapWidgetRegInfo reg = mapInfoControls.registerSideWidgetInternal(widget, drawableMenu, messageId, key, left, priorityOrder);
updateReg(calculateTextState(), reg);
return reg;
}
public MapWidgetRegInfo registerSideWidget(TextInfoWidget widget, int drawableMenu,
String message, String key, boolean left, int priorityOrder) {
MapWidgetRegInfo reg = mapInfoControls.registerSideWidgetInternal(widget, drawableMenu, message, key, left, priorityOrder);
updateReg(calculateTextState(), reg);
return reg;
}
public void registerSideWidget(TextInfoWidget widget, WidgetState widgetState, String key, boolean left, int priorityOrder) {
MapWidgetRegInfo reg = mapInfoControls.registerSideWidgetInternal(widget, widgetState, key, left, priorityOrder);
updateReg(calculateTextState(), reg);
}
public <T extends TextInfoWidget> T getSideWidget(Class<T> cl) {
return mapInfoControls.getSideWidget(cl);
}
public void removeSideWidget(TextInfoWidget widget) {
mapInfoControls.removeSideWidgetInternal(widget);
}
public void addTopToolbarController(TopToolbarController controller) {
topToolbarView.addController(controller);
}
public void removeTopToolbarController(TopToolbarController controller) {
topToolbarView.removeController(controller);
}
public boolean hasTopToolbar() {
return topToolbarView.getTopController() != null;
}
public TopToolbarController getTopToolbarController(TopToolbarControllerType type) {
return topToolbarView.getController(type);
}
public void registerAllControls(){
RouteInfoWidgetsFactory ric = new RouteInfoWidgetsFactory();
MapInfoWidgetsFactory mic = new MapInfoWidgetsFactory();
MapMarkersWidgetsFactory mwf = map.getMapLayers().getMapMarkersLayer().getWidgetsFactory();
OsmandApplication app = view.getApplication();
lanesControl = ric.createLanesControl(map, view);
streetNameView = new TopTextView(map.getMyApplication(), map);
updateStreetName(false, calculateTextState());
topToolbarView = new TopToolbarView(map);
updateTopToolbar(false);
alarmControl = ric.createAlarmInfoControl(app, map);
alarmControl.setVisibility(false);
rulerControl = ric.createRulerControl(app, map);
rulerControl.setVisibility(false);
// register left stack
registerSideWidget(null, R.drawable.ic_action_compass, R.string.map_widget_compass, "compass", true, 4);
NextTurnInfoWidget bigInfoControl = ric.createNextInfoControl(map, app, false);
registerSideWidget(bigInfoControl, R.drawable.ic_action_next_turn, R.string.map_widget_next_turn, "next_turn", true, 5);
NextTurnInfoWidget smallInfoControl = ric.createNextInfoControl(map, app, true);
registerSideWidget(smallInfoControl, R.drawable.ic_action_next_turn, R.string.map_widget_next_turn_small, "next_turn_small", true, 6);
NextTurnInfoWidget nextNextInfoControl = ric.createNextNextInfoControl(map, app, true);
registerSideWidget(nextNextInfoControl, R.drawable.ic_action_next_turn, R.string.map_widget_next_next_turn, "next_next_turn",true, 7);
// register right stack
// priorityOrder: 10s navigation-related, 20s position-related, 30s recording- and other plugin-related, 40s general device information, 50s debugging-purpose
TextInfoWidget intermediateDist = ric.createIntermediateDistanceControl(map);
registerSideWidget(intermediateDist, R.drawable.ic_action_intermediate, R.string.map_widget_intermediate_distance, "intermediate_distance", false, 13);
TextInfoWidget dist = ric.createDistanceControl(map);
registerSideWidget(dist, R.drawable.ic_action_target, R.string.map_widget_distance, "distance", false, 14);
TextInfoWidget time = ric.createTimeControl(map);
registerSideWidget(time, new TimeControlWidgetState(app), "time", false, 15);
if (settings.USE_MAP_MARKERS.get()) {
TextInfoWidget marker = mwf.createMapMarkerControl(map, true);
registerSideWidget(marker, R.drawable.ic_action_flag_dark, R.string.map_marker_1st, "map_marker_1st", false, 16);
TextInfoWidget bearing = ric.createBearingControl(map);
registerSideWidget(bearing, new BearingWidgetState(app), "bearing", false, 17);
TextInfoWidget marker2nd = mwf.createMapMarkerControl(map, false);
registerSideWidget(marker2nd, R.drawable.ic_action_flag_dark, R.string.map_marker_2nd, "map_marker_2nd", false, 18);
} else {
TextInfoWidget bearing = ric.createBearingControl(map);
registerSideWidget(bearing, new BearingWidgetState(app), "bearing", false, 17);
}
TextInfoWidget speed = ric.createSpeedControl(map);
registerSideWidget(speed, R.drawable.ic_action_speed, R.string.map_widget_speed, "speed", false, 20);
TextInfoWidget maxspeed = ric.createMaxSpeedControl(map);
registerSideWidget(maxspeed, R.drawable.ic_action_speed_limit, R.string.map_widget_max_speed, "max_speed", false, 21);
TextInfoWidget alt = mic.createAltitudeControl(map);
registerSideWidget(alt, R.drawable.ic_action_altitude, R.string.map_widget_altitude, "altitude", false, 23);
TextInfoWidget gpsInfo = mic.createGPSInfoControl(map);
registerSideWidget(gpsInfo, R.drawable.ic_action_gps_info, R.string.map_widget_gps_info, "gps_info", false, 28);
TextInfoWidget plainTime = ric.createPlainTimeControl(map);
registerSideWidget(plainTime, R.drawable.ic_action_time, R.string.map_widget_plain_time, "plain_time", false, 41);
TextInfoWidget battery = ric.createBatteryControl(map);
registerSideWidget(battery, R.drawable.ic_action_battery, R.string.map_widget_battery, "battery", false, 42);
}
public void recreateControls() {
leftStack.removeAllViews();
mapInfoControls.populateStackControl(leftStack, settings.getApplicationMode(), true, expanded);
leftStack.requestLayout();
rightStack.removeAllViews();
mapInfoControls.populateStackControl(rightStack, settings.getApplicationMode(), false, expanded);
rightStack.requestLayout();
expand.setVisibility(mapInfoControls.hasCollapsibles(settings.getApplicationMode())?
View.VISIBLE : View.GONE);
this.expand.setImageResource(expanded ? R.drawable.map_up :
R.drawable.map_down);
expand.setContentDescription(map.getString(expanded ? R.string.shared_string_collapse : R.string.access_widget_expand));
expand.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
expanded = !expanded;
recreateControls();
}
});
}
public LatLon getSelectedPointLatLon() {
return selectedPointLatLon;
}
public void setSelectedPointLatLon(LatLon selectedPointLatLon) {
this.selectedPointLatLon = selectedPointLatLon;
routeLayer.setSelectedPointLatLon(selectedPointLatLon);
}
private static class TextState {
boolean textBold ;
boolean night;
int textColor ;
int textShadowColor ;
int boxTop;
int rightRes;
int leftRes;
int expand;
int boxFree;
int textShadowRadius;
}
private int themeId = -1;
public void updateColorShadowsOfText() {
boolean transparent = view.getSettings().TRANSPARENT_MAP_THEME.get();
boolean nightMode = drawSettings != null && drawSettings.isNightMode();
boolean following = routeLayer.getHelper().isFollowingMode();
int calcThemeId = (transparent ? 4 : 0) | (nightMode ? 2 : 0) | (following ? 1 : 0);
if (themeId != calcThemeId) {
themeId = calcThemeId;
TextState ts = calculateTextState();
map.findViewById(R.id.map_center_info).setBackgroundResource(ts.boxFree);
for (MapWidgetRegInfo reg : mapInfoControls.getLeftWidgetSet()) {
updateReg(ts, reg);
}
for (MapWidgetRegInfo reg : mapInfoControls.getRightWidgetSet()) {
updateReg(ts, reg);
}
updateStreetName(nightMode, ts);
updateTopToolbar(nightMode);
lanesControl.updateTextSize(nightMode, ts.textColor, ts.textShadowColor, ts.textBold, ts.textShadowRadius / 2);
rulerControl.updateTextSize(nightMode, ts.textColor, ts.textShadowColor, (int) (2 * view.getDensity()));
this.expand.setBackgroundResource(ts.expand);
rightStack.invalidate();
leftStack.invalidate();
}
}
private void updateStreetName(boolean nightMode, TextState ts) {
streetNameView.setBackgroundResource(AndroidUiHelper.isOrientationPortrait(map) ? ts.boxTop
: ts.boxFree);
streetNameView.updateTextColor(nightMode, ts.textColor, ts.textShadowColor, ts.textBold, ts.textShadowRadius);
}
private void updateTopToolbar(boolean nightMode) {
topToolbarView.updateColors(nightMode);
}
private void updateReg(TextState ts, MapWidgetRegInfo reg) {
View v = reg.widget != null ? reg.widget.getView().findViewById(R.id.widget_bg) : null;
if(v != null) {
v.setBackgroundResource(reg.left ? ts.leftRes : ts.rightRes);
reg.widget.updateTextColor(ts.textColor, ts.textShadowColor, ts.textBold, ts.textShadowRadius);
reg.widget.updateIconMode(ts.night);
}
}
private TextState calculateTextState() {
boolean transparent = view.getSettings().TRANSPARENT_MAP_THEME.get();
boolean nightMode = drawSettings != null && drawSettings.isNightMode();
boolean following = routeLayer.getHelper().isFollowingMode();
TextState ts = new TextState();
ts.textBold = following;
ts.night = nightMode;
ts.textColor = nightMode ? ContextCompat.getColor(view.getContext(), R.color.widgettext_night) : Color.BLACK;
// Night shadowColor always use widgettext_shadow_night, same as widget background color for non-transparent
ts.textShadowColor = nightMode ? ContextCompat.getColor(view.getContext(), R.color.widgettext_shadow_night) : Color.WHITE;
if (!transparent && !nightMode) {
ts.textShadowRadius = 0;
} else {
ts.textShadowRadius = (int) (4 * view.getDensity());
}
if (transparent) {
ts.boxTop = R.drawable.btn_flat_transparent;
ts.rightRes = R.drawable.btn_left_round_transparent;
ts.leftRes = R.drawable.btn_right_round_transparent;
ts.expand = R.drawable.btn_inset_circle_transparent;
ts.boxFree = R.drawable.btn_round_transparent;
} else if (nightMode) {
ts.boxTop = R.drawable.btn_flat_night;
ts.rightRes = R.drawable.btn_left_round_night;
ts.leftRes = R.drawable.btn_right_round_night;
ts.expand = R.drawable.btn_inset_circle_night;
ts.boxFree = R.drawable.btn_round_night;
} else {
ts.boxTop = R.drawable.btn_flat;
ts.rightRes = R.drawable.btn_left_round;
ts.leftRes = R.drawable.btn_right_round;
ts.expand = R.drawable.btn_inset_circle;
ts.boxFree = R.drawable.btn_round;
}
return ts;
}
@Override
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings drawSettings) {
this.drawSettings = drawSettings;
// update data on draw
updateColorShadowsOfText();
mapInfoControls.updateInfo(settings.getApplicationMode(), drawSettings, expanded);
streetNameView.updateInfo(drawSettings);
topToolbarView.updateInfo();
alarmControl.updateInfo(drawSettings);
rulerControl.updateInfo(tileBox, drawSettings);
lanesControl.updateInfo(drawSettings);
}
@Override
public void destroyLayer() {
}
@Override
public boolean drawInScreenPixels() {
return true;
}
public static String getStringPropertyName(Context ctx, String propertyName, String defValue) {
try {
Field f = R.string.class.getField("rendering_attr_" + propertyName + "_name");
if (f != null) {
Integer in = (Integer) f.get(null);
return ctx.getString(in);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
return defValue;
}
public static String getStringPropertyDescription(Context ctx, String propertyName, String defValue) {
try {
Field f = R.string.class.getField("rendering_attr_" + propertyName + "_description");
if (f != null) {
Integer in = (Integer) f.get(null);
return ctx.getString(in);
}
} catch (Exception e) {
//e.printStackTrace();
System.err.println(e.getMessage());
}
return defValue;
}
}