Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[android] - derived source attribution #8630

Merged
merged 1 commit into from
Apr 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.mapbox.mapboxsdk.maps;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
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 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.
* <p>
* 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.
* </p>
*/
class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener {

private static final String MAP_FEEDBACK_URL = "https://www.mapbox.com/map-feedback";
Copy link
Contributor

@1ec5 1ec5 Apr 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn’t hard-code this URL, because sources other than Mapbox Streets are free to provide their own map feedback URLs. Look for <a> tags with class set to mapbox-improve-map.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ticketed this out as tail work in #8730, since custom feedback links are admittedly quite an edge case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for ticketing that out, will fix it separately from this PR.

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<String, String> 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, R.style.mapbox_AlertDialogStyle);
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 {
showAttributionWebPage(which);
}
}

private boolean isLatestEntry(int attributionKeyIndex) {
return attributionKeyIndex == attributionKeys.length - 1;
}

private void showTelemetryDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
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) {
String url = context.getResources().getString(R.string.mapbox_telemetryLink);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
context.startActivity(intent);
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 showAttributionWebPage(int which) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String url = attributionMap.get(attributionKeys[which]);
if (url.contains(MAP_FEEDBACK_URL)) {
url = buildMapFeedbackMapUrl(mapboxMap.getCameraPosition());
}
intent.setData(Uri.parse(url));
context.startActivity(intent);
}

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 static class AttributionBuilder {

private final HashMap<String, String> 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<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
package com.mapbox.mapboxsdk.maps;

import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.SurfaceTexture;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.app.AlertDialog;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
Expand All @@ -28,15 +23,13 @@
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ZoomButtonsController;

import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
Expand Down Expand Up @@ -155,7 +148,7 @@ private void initialise(@NonNull final Context context, @NonNull final MapboxMap
// inject widgets with MapboxMap
compassView.setMapboxMap(mapboxMap);
myLocationView.setMapboxMap(mapboxMap);
attrView.setOnClickListener(new AttributionOnClickListener(context, transform));
attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap));

// Ensure this view is interactable
setClickable(true);
Expand Down Expand Up @@ -593,78 +586,6 @@ void setMapboxMap(MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
}

private static class AttributionOnClickListener implements View.OnClickListener, DialogInterface.OnClickListener {

private static final int ATTRIBUTION_INDEX_IMPROVE_THIS_MAP = 2;
private static final int ATTRIBUTION_INDEX_TELEMETRY_SETTINGS = 3;
private Context context;
private Transform transform;

public AttributionOnClickListener(Context context, Transform transform) {
this.context = context;
this.transform = transform;
}

// Called when someone presses the attribution icon
@Override
public void onClick(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
String[] items = context.getResources().getStringArray(R.array.mapbox_attribution_names);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, items), this);
builder.show();
}

// Called when someone selects an attribution, 'Improve this map' adds location data to the url
@Override
public void onClick(DialogInterface dialog, int which) {
final Context context = ((Dialog) dialog).getContext();
if (which == ATTRIBUTION_INDEX_TELEMETRY_SETTINGS) {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
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) {
String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[3];
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
context.startActivity(intent);
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();
return;
}
String url = context.getResources().getStringArray(R.array.mapbox_attribution_links)[which];
if (which == ATTRIBUTION_INDEX_IMPROVE_THIS_MAP) {
CameraPosition cameraPosition = transform.getCameraPosition();
if (cameraPosition != null) {
url = String.format(url, cameraPosition.target.getLongitude(),
cameraPosition.target.getLatitude(), (int) cameraPosition.zoom);
}
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
context.startActivity(intent);
}
}

/**
* Definition of a map change event.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ public String getId() {
return nativeGetId();
}

/**
* Retrieve the source attribution.
* <p>
* Will return an empty String if no attribution is available.
* </p>
*
* @return the string representation of the attribution in html format
*/
public String getAttribution() {
return nativeGetAttribution();
}

/**
* Internal use
*
Expand All @@ -38,4 +50,6 @@ public long getNativePtr() {

protected native String nativeGetId();

protected native String nativeGetAttribution();

}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<public name="mapbox_style_satellite" type="string" />
<public name="mapbox_style_satellite_streets" type="string" />

<!-- Exposed error messages -->
<!-- Exposed strings -->
<public name="mapbox_compassContentDescription" type="string" />
<public name="mapbox_attributionsIconContentDescription" type="string" />
<public name="mapbox_myLocationViewContentDescription" type="string" />
Expand All @@ -100,6 +100,7 @@
<public name="mapbox_attributionTelemetryPositive" type="string" />
<public name="mapbox_attributionTelemetryNegative" type="string" />
<public name="mapbox_attributionTelemetryNeutral" type="string" />
<public name="mapbox_telemetrySettings" type="string"/>
<public name="mapbox_offline_error_region_definition_invalid" type="string" />

<!-- Exposed drawables -->
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<string name="mapbox_attributionTelemetryNegative">Disagree</string>
<string name="mapbox_attributionTelemetryNeutral">More info</string>
<string name="mapbox_offline_error_region_definition_invalid">Provided OfflineRegionDefinition doesn\'t fit the world bounds: %s</string>
<string name="mapbox_telemetrySettings">Telemetry Settings</string>
<string name="mapbox_telemetryLink" translatable="false">https://www.mapbox.com/telemetry/</string>

<!-- these are public -->
<!-- Using one of these constants means your map style will always use the latest version and
Expand Down
Loading