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

Commit

Permalink
[android] - derived source attribution (#8630)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobrun authored Apr 13, 2017
1 parent 21f54cc commit f045559
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 120 deletions.
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";
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
16 changes: 0 additions & 16 deletions platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml

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

0 comments on commit f045559

Please sign in to comment.