package com.mapbox.mapboxsdk.maps; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.support.annotation.NonNull; import android.text.Html; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.URLSpan; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Toast; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Locale; /** * Responsible for managing attribution interactions on the map. *

* When the user clicks the attribution icon, {@link AttributionDialogManager#onClick(View)} will be invoked. * An attribution dialog will be shown to the user with contents based on the attributions found in the map style. * Additionally an telemetry option item is shown to configure telemetry settings. *

*/ class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener { private static final String MAP_FEEDBACK_URL = "https://www.mapbox.com/map-feedback"; private static final String MAP_FEEDBACK_LOCATION_FORMAT = MAP_FEEDBACK_URL + "/#/%f/%f/%d"; private final Context context; private final MapboxMap mapboxMap; private String[] attributionKeys; private HashMap attributionMap; AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) { this.context = context; this.mapboxMap = mapboxMap; } // Called when someone presses the attribution icon on the map @Override public void onClick(View view) { attributionMap = new AttributionBuilder(context, mapboxMap).build(); showAttributionDialog(); } private void showAttributionDialog() { attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.mapbox_attributionsDialogTitle); builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this); builder.show(); } // Called when someone selects an attribution or telemetry settings from the dialog @Override public void onClick(DialogInterface dialog, int which) { if (isLatestEntry(which)) { showTelemetryDialog(); } else { showMapFeedbackWebPage(which); } } private boolean isLatestEntry(int attributionKeyIndex) { return attributionKeyIndex == attributionKeys.length - 1; } private void showTelemetryDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.mapbox_attributionTelemetryTitle); builder.setMessage(R.string.mapbox_attributionTelemetryMessage); builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MapboxTelemetry.getInstance().setTelemetryEnabled(true); dialog.cancel(); } }); builder.setNeutralButton(R.string.mapbox_attributionTelemetryNeutral, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showWebPage(context.getResources().getString(R.string.mapbox_telemetryLink)); dialog.cancel(); } }); builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MapboxTelemetry.getInstance().setTelemetryEnabled(false); dialog.cancel(); } }); builder.show(); } private void showMapFeedbackWebPage(int which) { String url = attributionMap.get(attributionKeys[which]); if (url.contains(MAP_FEEDBACK_URL)) { url = buildMapFeedbackMapUrl(mapboxMap.getCameraPosition()); } showWebPage(url); } private String buildMapFeedbackMapUrl(CameraPosition cameraPosition) { // appends current location to the map feedback url if available return cameraPosition != null ? String.format(Locale.getDefault(), MAP_FEEDBACK_LOCATION_FORMAT, cameraPosition.target.getLongitude(), cameraPosition.target.getLatitude(), (int) cameraPosition.zoom) : MAP_FEEDBACK_URL; } private void showWebPage(@NonNull String url) { try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); context.startActivity(intent); } catch (ActivityNotFoundException exception) { // explicitly handling if the device hasn't have a web browser installed. #8899 Toast.makeText(context, R.string.mapbox_attributionErrorNoBrowser, Toast.LENGTH_LONG).show(); } } private static class AttributionBuilder { private final HashMap map = new LinkedHashMap<>(); private final Context context; private final MapboxMap mapboxMap; AttributionBuilder(Context context, MapboxMap mapboxMap) { this.context = context.getApplicationContext(); this.mapboxMap = mapboxMap; } private HashMap build() { for (Source source : mapboxMap.getSources()) { parseAttribution(source.getAttribution()); } addTelemetryEntryToAttributionMap(); return map; } private void parseAttribution(String attributionSource) { if (!TextUtils.isEmpty(attributionSource)) { SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource); URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class); for (URLSpan urlSpan : urlSpans) { map.put(resolveAnchorValue(htmlBuilder, urlSpan), urlSpan.getURL()); } } } private String resolveAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) { int start = htmlBuilder.getSpanStart(urlSpan); int end = htmlBuilder.getSpanEnd(urlSpan); int length = end - start; char[] charKey = new char[length]; htmlBuilder.getChars(start, end, charKey, 0); return String.valueOf(charKey); } private void addTelemetryEntryToAttributionMap() { String telemetryKey = context.getString(R.string.mapbox_telemetrySettings); String telemetryLink = context.getString(R.string.mapbox_telemetryLink); map.put(telemetryKey, telemetryLink); } } }