element instead of an image, only available in leaflet map
23 | private final @Nullable LeafletDivIcon divIcon;
24 |
25 | private AirMapMarker(T object, long id, MarkerOptions markerOptions) {
26 | this(object, id, markerOptions, null);
27 | }
28 |
29 | private AirMapMarker(T object, long id, @Nullable LeafletDivIcon divIcon) {
30 | this(object, id, new MarkerOptions(), divIcon);
31 | }
32 |
33 | private AirMapMarker(T object, long id, MarkerOptions markerOptions,
34 | @Nullable LeafletDivIcon divIcon) {
35 | this.object = object;
36 | this.id = id;
37 | this.markerOptions = markerOptions;
38 | this.divIcon = divIcon;
39 | }
40 |
41 | public T object() {
42 | return object;
43 | }
44 |
45 | public long getId() {
46 | return id;
47 | }
48 |
49 | public LatLng getLatLng() {
50 | return markerOptions.getPosition();
51 | }
52 |
53 | void setLatLng(LatLng latLng) {
54 | markerOptions.position(latLng);
55 | }
56 |
57 | public String getTitle() {
58 | return markerOptions.getTitle();
59 | }
60 |
61 | public String getSnippet() {
62 | return markerOptions.getSnippet();
63 | }
64 |
65 | public @Nullable LeafletDivIcon getDivIcon() {
66 | return divIcon;
67 | }
68 |
69 | public MarkerOptions getMarkerOptions() {
70 | return markerOptions;
71 | }
72 |
73 | /** Sets a marker associated to this object */
74 | void setGoogleMarker(Marker marker) {
75 | this.marker = marker;
76 | }
77 |
78 | public Builder
toBuilder() {
79 | Builder builder = new Builder()
80 | .id(id)
81 | .object(object)
82 | .position(markerOptions.getPosition())
83 | .alpha(markerOptions.getAlpha())
84 | .anchor(markerOptions.getAnchorU(), markerOptions.getAnchorV())
85 | .bitmapDescriptor(markerOptions.getIcon())
86 | .infoWindowAnchor(markerOptions.getInfoWindowAnchorU(),
87 | markerOptions.getInfoWindowAnchorV())
88 | .snippet(markerOptions.getSnippet())
89 | .title(markerOptions.getTitle())
90 | .draggable(markerOptions.isDraggable())
91 | .visible(markerOptions.isVisible())
92 | .alpha(markerOptions.getAlpha())
93 | .rotation(markerOptions.getRotation())
94 | .flat(markerOptions.isFlat());
95 | if (divIcon != null) {
96 | builder.divIconHtml(divIcon.getHtml())
97 | .divIconWidth(divIcon.getWidth())
98 | .divIconHeight(divIcon.getHeight());
99 | }
100 | return builder;
101 | }
102 |
103 | public Marker getMarker() {
104 | return marker;
105 | }
106 |
107 | public static class Builder {
108 | private T object;
109 | private long id;
110 | private final MarkerOptions markerOptions = new MarkerOptions();
111 | private String divIconHtml;
112 | private int divIconHeight;
113 | private int divIconWidth;
114 |
115 | public Builder() {
116 | }
117 |
118 | public Builder object(T object) {
119 | this.object = object;
120 | return this;
121 | }
122 |
123 | public Builder id(long id) {
124 | this.id = id;
125 | return this;
126 | }
127 |
128 | public Builder position(LatLng position) {
129 | markerOptions.position(position);
130 | return this;
131 | }
132 |
133 | public Builder anchor(float u, float v) {
134 | markerOptions.anchor(u, v);
135 | return this;
136 | }
137 |
138 | public Builder infoWindowAnchor(float u, float v) {
139 | markerOptions.infoWindowAnchor(u, v);
140 | return this;
141 | }
142 |
143 | public Builder title(String title) {
144 | markerOptions.title(title);
145 | return this;
146 | }
147 |
148 | public Builder divIconHtml(String divIconHtml) {
149 | this.divIconHtml = divIconHtml;
150 | return this;
151 | }
152 |
153 | public Builder divIconWidth(int width) {
154 | this.divIconWidth = width;
155 | return this;
156 | }
157 |
158 | public Builder divIconHeight(int height) {
159 | this.divIconHeight = height;
160 | return this;
161 | }
162 |
163 | public Builder snippet(String snippet) {
164 | markerOptions.snippet(snippet);
165 | return this;
166 | }
167 |
168 | public Builder iconId(int iconId) {
169 | try {
170 | markerOptions.icon(BitmapDescriptorFactory.fromResource(iconId));
171 | } catch (NullPointerException ignored) {
172 | // google play services is not available
173 | }
174 | return this;
175 | }
176 |
177 | public Builder bitmap(Bitmap bitmap) {
178 | try {
179 | bitmapDescriptor(BitmapDescriptorFactory.fromBitmap(bitmap));
180 | } catch (NullPointerException ignored) {
181 | // google play services is not available
182 | }
183 | return this;
184 | }
185 |
186 | public Builder bitmapDescriptor(BitmapDescriptor bitmap) {
187 | markerOptions.icon(bitmap);
188 | return this;
189 | }
190 |
191 | public Builder draggable(boolean draggable) {
192 | markerOptions.draggable(draggable);
193 | return this;
194 | }
195 |
196 | public Builder visible(boolean visible) {
197 | markerOptions.visible(visible);
198 | return this;
199 | }
200 |
201 | public Builder flat(boolean flat) {
202 | markerOptions.flat(flat);
203 | return this;
204 | }
205 |
206 | public Builder rotation(float rotation) {
207 | markerOptions.rotation(rotation);
208 | return this;
209 | }
210 |
211 | public Builder alpha(float alpha) {
212 | markerOptions.alpha(alpha);
213 | return this;
214 | }
215 |
216 | public Builder zIndex(float zIndex) {
217 | markerOptions.zIndex(zIndex);
218 | return this;
219 | }
220 |
221 | public AirMapMarker build() {
222 | return new AirMapMarker<>(object, id, markerOptions,
223 | divIconHtml == null ? null :
224 | new LeafletDivIcon(divIconHtml, divIconWidth, divIconHeight));
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapPolygon.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.graphics.Color;
4 |
5 | import androidx.annotation.NonNull;
6 | import com.google.android.gms.maps.model.LatLng;
7 | import com.google.android.gms.maps.model.Polygon;
8 | import com.google.android.gms.maps.model.PolygonOptions;
9 |
10 | public class AirMapPolygon {
11 |
12 | private static final int STROKE_WIDTH = 1;
13 | private static final int STROKE_COLOR = Color.BLUE;
14 |
15 | private final T object;
16 | private final long id;
17 | private final PolygonOptions polygonOptions;
18 | private Polygon googlePolygon;
19 |
20 | public AirMapPolygon(T object, long id, PolygonOptions polygonOptions) {
21 | this.object = object;
22 | this.id = id;
23 | this.polygonOptions = polygonOptions;
24 | }
25 |
26 | public T getObject() {
27 | return object;
28 | }
29 |
30 | public long getId() {
31 | return id;
32 | }
33 |
34 | public PolygonOptions getPolygonOptions() {
35 | return polygonOptions;
36 | }
37 |
38 | public Polygon getGooglePolygon() {
39 | return googlePolygon;
40 | }
41 |
42 | public void setGooglePolygon(Polygon googlePolygon) {
43 | this.googlePolygon = googlePolygon;
44 | }
45 |
46 | public static class Builder {
47 | private final PolygonOptions polygonOptions = new PolygonOptions();
48 | private T object;
49 | private long id;
50 |
51 | public Builder() {
52 | polygonOptions.strokeWidth(STROKE_WIDTH);
53 | polygonOptions.strokeColor(STROKE_COLOR);
54 | }
55 |
56 | public Builder object(T object) {
57 | this.object = object;
58 | return this;
59 | }
60 |
61 | public Builder id(long id) {
62 | this.id = id;
63 | return this;
64 | }
65 |
66 | public Builder strokeColor(int color) {
67 | polygonOptions.strokeColor(color);
68 | return this;
69 | }
70 |
71 | public Builder strokeWidth(float width) {
72 | this.polygonOptions.strokeWidth(width);
73 | return this;
74 | }
75 |
76 | public Builder fillColor(int color) {
77 | this.polygonOptions.fillColor(color);
78 | return this;
79 | }
80 |
81 | public Builder geodesic(boolean geodesic) {
82 | this.polygonOptions.geodesic(geodesic);
83 | return this;
84 | }
85 |
86 | public Builder zIndex(float zIndex) {
87 | this.polygonOptions.zIndex(zIndex);
88 | return this;
89 | }
90 |
91 | public Builder visible(boolean visible) {
92 | this.polygonOptions.visible(visible);
93 | return this;
94 | }
95 |
96 | public Builder add(LatLng point) {
97 | this.polygonOptions.add(point);
98 | return this;
99 | }
100 |
101 | public Builder add(LatLng... points) {
102 | this.polygonOptions.add(points);
103 | return this;
104 | }
105 |
106 | public Builder addAll(@NonNull Iterable points) {
107 | this.polygonOptions.addAll(points);
108 | return this;
109 | }
110 |
111 | public Builder addHole(@NonNull Iterable points) {
112 | this.polygonOptions.addHole(points);
113 | return this;
114 | }
115 |
116 | public AirMapPolygon build() {
117 | return new AirMapPolygon<>(object, id, polygonOptions);
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapPolyline.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.google.android.gms.maps.GoogleMap;
6 | import com.google.android.gms.maps.model.LatLng;
7 | import com.google.android.gms.maps.model.Polyline;
8 | import com.google.android.gms.maps.model.PolylineOptions;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * Helper class for keeping record of data needed to display a polyline, as well as an optional
14 | * object T associated with the polyline.
15 | */
16 | public class AirMapPolyline {
17 |
18 | private static final int STROKE_WIDTH = 1;
19 | private static final int STROKE_COLOR = Color.BLUE;
20 |
21 | private T object;
22 | private int strokeWidth;
23 | private long id;
24 | private List points;
25 | private String title;
26 | private int strokeColor;
27 | private Polyline googlePolyline;
28 |
29 | public AirMapPolyline(List points, long id) {
30 | this(null, points, id);
31 | }
32 |
33 | public AirMapPolyline(T object, List points, long id) {
34 | this(object, points, id, STROKE_WIDTH, STROKE_COLOR);
35 | }
36 |
37 | public AirMapPolyline(T object, List points, long id, int strokeWidth, int strokeColor) {
38 | this.object = object;
39 | this.points = points;
40 | this.id = id;
41 | this.strokeWidth = strokeWidth;
42 | this.strokeColor = strokeColor;
43 | }
44 |
45 | public long getId() {
46 | return id;
47 | }
48 |
49 | public void setId(long id) {
50 | this.id = id;
51 | }
52 |
53 | public List getPoints() {
54 | return points;
55 | }
56 |
57 | public void setPoints(List points) {
58 | this.points = points;
59 | }
60 |
61 | public String getTitle() {
62 | return title;
63 | }
64 |
65 | public void setTitle(String title) {
66 | this.title = title;
67 | }
68 |
69 | public T getObject() {
70 | return object;
71 | }
72 |
73 | public void setObject(T object) {
74 | this.object = object;
75 | }
76 |
77 | public int getStrokeWidth() {
78 | return strokeWidth;
79 | }
80 |
81 | public int getStrokeColor() {
82 | return strokeColor;
83 | }
84 |
85 | /**
86 | * Add this polyline to the given {@link GoogleMap} instance
87 | *
88 | * @param googleMap the {@link GoogleMap} instance to which the polyline will be added
89 | */
90 | public void addToGoogleMap(GoogleMap googleMap) {
91 | // add the polyline and keep a reference so it can be removed
92 | googlePolyline = googleMap.addPolyline(new PolylineOptions()
93 | .addAll(points)
94 | .width(strokeWidth)
95 | .color(strokeColor));
96 | }
97 |
98 | /**
99 | * Remove this polyline from a GoogleMap (if it was added).
100 | *
101 | * @return true if the {@link Polyline} was removed
102 | */
103 | public boolean removeFromGoogleMap() {
104 | if (googlePolyline != null) {
105 | googlePolyline.remove();
106 | return true;
107 | }
108 | return false;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | import java.util.Locale;
7 |
8 | /** Defines maps to be used with {@link com.airbnb.android.airmapview.WebViewMapFragment} */
9 | public class AirMapType {
10 |
11 | private static final String ARG_MAP_DOMAIN = "map_domain";
12 | private static final String ARG_FILE_NAME = "map_file_name";
13 | private static final String ARG_MAP_URL = "map_url";
14 | private final String fileName;
15 | private final String mapUrl;
16 | private final String domain;
17 |
18 | public AirMapType(String fileName, String mapUrl, String domain) {
19 | this.fileName = fileName;
20 | this.mapUrl = mapUrl;
21 | this.domain = domain;
22 | }
23 |
24 | /** @return the name of the HTML file in /assets */
25 | String getFileName() {
26 | return fileName;
27 | }
28 |
29 | /** @return the base URL for a maps API */
30 | String getMapUrl() {
31 | return mapUrl;
32 | }
33 |
34 | /** @return domain of the maps API to use */
35 | String getDomain() {
36 | return domain;
37 | }
38 |
39 | public Bundle toBundle() {
40 | return toBundle(new Bundle());
41 | }
42 |
43 | public Bundle toBundle(Bundle bundle) {
44 | bundle.putString(ARG_MAP_DOMAIN, getDomain());
45 | bundle.putString(ARG_MAP_URL, getMapUrl());
46 | bundle.putString(ARG_FILE_NAME, getFileName());
47 | return bundle;
48 | }
49 |
50 | public static AirMapType fromBundle(Bundle bundle) {
51 | return new AirMapType(
52 | bundle.getString(ARG_FILE_NAME, ""),
53 | bundle.getString(ARG_MAP_URL, ""),
54 | bundle.getString(ARG_MAP_DOMAIN, ""));
55 | }
56 |
57 | public String getMapData(Resources resources) {
58 | return AirMapUtils.getStringFromFile(resources, fileName)
59 | .replace("MAPURL", mapUrl)
60 | .replace("LANGTOKEN", Locale.getDefault().getLanguage())
61 | .replace("REGIONTOKEN", Locale.getDefault().getCountry());
62 | }
63 |
64 | @SuppressWarnings("RedundantIfStatement")
65 | @Override
66 | public boolean equals(Object o) {
67 | if (this == o) {
68 | return true;
69 | }
70 |
71 | if (o == null || !(o instanceof AirMapType)) {
72 | return false;
73 | }
74 |
75 | AirMapType that = (AirMapType) o;
76 |
77 | if (domain != null ? !domain.equals(that.domain) : that.domain != null) {
78 | return false;
79 | }
80 |
81 | if (fileName != null ? !fileName.equals(that.fileName) : that.fileName != null) {
82 | return false;
83 | }
84 |
85 | if (mapUrl != null ? !mapUrl.equals(that.mapUrl) : that.mapUrl != null) {
86 | return false;
87 | }
88 |
89 | return true;
90 | }
91 |
92 | @Override public int hashCode() {
93 | int result = fileName != null ? fileName.hashCode() : 0;
94 | result = 31 * result + (mapUrl != null ? mapUrl.hashCode() : 0);
95 | result = 31 * result + (domain != null ? domain.hashCode() : 0);
96 | return result;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapUtils.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.res.Resources;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.InputStreamReader;
9 |
10 | public class AirMapUtils {
11 |
12 | public static String getStringFromFile(Resources resources, String filePath) {
13 | try {
14 | InputStream is = resources.getAssets().open(filePath);
15 | String ret = convertStreamToString(is);
16 | is.close();
17 | return ret;
18 | } catch (IOException e) {
19 | throw new RuntimeException("unable to load asset " + filePath);
20 | }
21 | }
22 |
23 | public static String convertStreamToString(InputStream is) throws IOException {
24 | BufferedReader reader = new BufferedReader(new InputStreamReader(is));
25 | StringBuilder sb = new StringBuilder();
26 | String line;
27 | while ((line = reader.readLine()) != null) {
28 | sb.append(line).append("\n");
29 | }
30 | reader.close();
31 | return sb.toString();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.view.MotionEvent;
7 | import android.widget.FrameLayout;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.FragmentManager;
12 | import com.airbnb.android.airmapview.listeners.InfoWindowCreator;
13 | import com.airbnb.android.airmapview.listeners.OnCameraChangeListener;
14 | import com.airbnb.android.airmapview.listeners.OnCameraMoveListener;
15 | import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener;
16 | import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback;
17 | import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback;
18 | import com.airbnb.android.airmapview.listeners.OnMapClickListener;
19 | import com.airbnb.android.airmapview.listeners.OnMapInitializedListener;
20 | import com.airbnb.android.airmapview.listeners.OnMapLoadedListener;
21 | import com.airbnb.android.airmapview.listeners.OnMapMarkerClickListener;
22 | import com.airbnb.android.airmapview.listeners.OnMapMarkerDragListener;
23 | import com.google.android.gms.maps.GoogleMap;
24 | import com.google.android.gms.maps.model.LatLng;
25 | import com.google.android.gms.maps.model.LatLngBounds;
26 | import com.google.android.gms.maps.model.Marker;
27 |
28 | import org.json.JSONException;
29 |
30 | public class AirMapView extends FrameLayout
31 | implements OnCameraChangeListener, OnMapClickListener, OnMapMarkerDragListener,
32 | OnMapMarkerClickListener, OnMapLoadedListener, OnInfoWindowClickListener {
33 |
34 | private static final int INVALID_ZOOM = -1;
35 |
36 | protected AirMapInterface mapInterface;
37 | private OnCameraMoveListener onCameraMoveListener;
38 | private OnCameraChangeListener onCameraChangeListener;
39 | private boolean mOnCameraMoveTriggered;
40 | private OnMapInitializedListener onMapInitializedListener;
41 | private OnMapMarkerClickListener onMapMarkerClickListener;
42 | private OnMapMarkerDragListener onMapMarkerDragListener;
43 | private OnMapClickListener onMapClickListener;
44 | private OnInfoWindowClickListener onInfoWindowClickListener;
45 |
46 | public AirMapView(Context context) {
47 | super(context);
48 | inflateView();
49 | }
50 |
51 | public AirMapView(Context context, AttributeSet attrs) {
52 | super(context, attrs);
53 | inflateView();
54 | }
55 |
56 | public AirMapView(Context context, AttributeSet attrs, int defStyle) {
57 | super(context, attrs, defStyle);
58 | inflateView();
59 | }
60 |
61 | private void inflateView() {
62 | LayoutInflater.from(getContext()).inflate(R.layout.map_view, this);
63 | }
64 |
65 | public void initialize(FragmentManager fragmentManager, AirMapInterface mapInterface) {
66 | if (mapInterface == null || fragmentManager == null) {
67 | throw new IllegalArgumentException("Either mapInterface or fragmentManager is null");
68 | }
69 |
70 | this.mapInterface = mapInterface;
71 | this.mapInterface.setOnMapLoadedListener(this);
72 |
73 | fragmentManager.beginTransaction()
74 | .replace(getId(), (Fragment) this.mapInterface)
75 | .commit();
76 |
77 | fragmentManager.executePendingTransactions();
78 | }
79 |
80 | /**
81 | * Used for initialization of the underlying map provider.
82 | *
83 | * @param fragmentManager required for initialization
84 | */
85 | public void initialize(FragmentManager fragmentManager) {
86 | AirMapInterface mapInterface = (AirMapInterface)
87 | fragmentManager.findFragmentById(R.id.map_frame);
88 |
89 | if (mapInterface != null) {
90 | initialize(fragmentManager, mapInterface);
91 | } else {
92 | initialize(fragmentManager, new DefaultAirMapViewBuilder(getContext()).builder().build());
93 | }
94 | }
95 |
96 | public void setOnMapInitializedListener(OnMapInitializedListener mapInitializedListener) {
97 | onMapInitializedListener = mapInitializedListener;
98 | }
99 |
100 | @Override public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
101 | if (ev.getAction() == MotionEvent.ACTION_MOVE) {
102 | if (onCameraMoveListener != null && !mOnCameraMoveTriggered) {
103 | onCameraMoveListener.onCameraMove();
104 | mOnCameraMoveTriggered = true;
105 | }
106 | } else if (ev.getAction() == MotionEvent.ACTION_UP) {
107 | mOnCameraMoveTriggered = false;
108 | }
109 |
110 | return super.dispatchTouchEvent(ev);
111 | }
112 |
113 | public void setOnCameraChangeListener(OnCameraChangeListener onCameraChangeListener) {
114 | this.onCameraChangeListener = onCameraChangeListener;
115 | }
116 |
117 | /**
118 | * Sets the map {@link com.airbnb.android.airmapview.listeners.OnCameraMoveListener}
119 | *
120 | * @param onCameraMoveListener The OnCameraMoveListener to be set
121 | */
122 | public void setOnCameraMoveListener(OnCameraMoveListener onCameraMoveListener) {
123 | this.onCameraMoveListener = onCameraMoveListener;
124 | }
125 |
126 | public final AirMapInterface getMapInterface() {
127 | return mapInterface;
128 | }
129 |
130 | public void onDestroyView() {
131 | if (isInitialized()) {
132 | mapInterface.setMyLocationEnabled(false);
133 | }
134 | }
135 |
136 | public int getZoom() {
137 | if (isInitialized()) {
138 | return mapInterface.getZoom();
139 | }
140 |
141 | return INVALID_ZOOM;
142 | }
143 |
144 | public LatLng getCenter() {
145 | if (isInitialized()) {
146 | return mapInterface.getCenter();
147 | }
148 | return null;
149 | }
150 |
151 | public boolean setCenter(LatLng latLng) {
152 | if (isInitialized()) {
153 | mapInterface.setCenter(latLng);
154 | return true;
155 | }
156 | return false;
157 | }
158 |
159 | public boolean animateCenter(LatLng latLng) {
160 | if (isInitialized()) {
161 | mapInterface.animateCenter(latLng);
162 | return true;
163 | }
164 | return false;
165 | }
166 |
167 | public boolean setZoom(int zoom) {
168 | if (isInitialized()) {
169 | mapInterface.setZoom(zoom);
170 | return true;
171 | }
172 | return false;
173 | }
174 |
175 | public boolean setCenterZoom(LatLng latLng, int zoom) {
176 | if (isInitialized()) {
177 | mapInterface.setCenterZoom(latLng, zoom);
178 | return true;
179 | }
180 | return false;
181 | }
182 |
183 | public boolean animateCenterZoom(LatLng latLng, int zoom) {
184 | if (isInitialized()) {
185 | mapInterface.animateCenterZoom(latLng, zoom);
186 | return true;
187 | }
188 | return false;
189 | }
190 |
191 | public boolean setBounds(LatLngBounds latLngBounds, int boundsPadding) {
192 | if (isInitialized()) {
193 | mapInterface.setCenter(latLngBounds, boundsPadding);
194 | return true;
195 | }
196 | return false;
197 | }
198 |
199 | public void getScreenBounds(OnMapBoundsCallback callback) {
200 | if (isInitialized()) {
201 | mapInterface.getMapScreenBounds(callback);
202 | }
203 | }
204 |
205 | public void getMapMarkerScreenLocation(LatLng latLng, OnLatLngScreenLocationCallback callback) {
206 | if (isInitialized()) {
207 | mapInterface.getScreenLocation(latLng, callback);
208 | }
209 | }
210 |
211 | public void drawCircle(LatLng latLng, int radius) {
212 | if (isInitialized()) {
213 | mapInterface.drawCircle(latLng, radius);
214 | }
215 | }
216 |
217 | public void drawCircle(LatLng latLng, int radius, int strokeColor) {
218 | if (isInitialized()) {
219 | mapInterface.drawCircle(latLng, radius, strokeColor);
220 | }
221 | }
222 |
223 | public void drawCircle(LatLng latLng, int radius, int strokeColor, int strokeWidth) {
224 | if (isInitialized()) {
225 | mapInterface.drawCircle(latLng, radius, strokeColor, strokeWidth);
226 | }
227 | }
228 |
229 | public void drawCircle(LatLng latLng, int radius, int strokeColor, int strokeWidth,
230 | int fillColor) {
231 | if (isInitialized()) {
232 | mapInterface.drawCircle(latLng, radius, strokeColor, strokeWidth, fillColor);
233 | }
234 | }
235 |
236 | public void setPadding(int left, int top, int right, int bottom) {
237 | if (isInitialized()) {
238 | mapInterface.setPadding(left, top, right, bottom);
239 | }
240 | }
241 |
242 | public void setOnMarkerClickListener(OnMapMarkerClickListener listener) {
243 | onMapMarkerClickListener = listener;
244 | }
245 |
246 | public void setOnMarkerDragListener(OnMapMarkerDragListener listener) {
247 | onMapMarkerDragListener = listener;
248 | }
249 |
250 | public void setOnMapClickListener(OnMapClickListener listener) {
251 | onMapClickListener = listener;
252 | }
253 |
254 | public void setInfoWindowAdapter(GoogleMap.InfoWindowAdapter adapter, InfoWindowCreator creator) {
255 | if (isInitialized()) {
256 | mapInterface.setInfoWindowCreator(adapter, creator);
257 | }
258 | }
259 |
260 | public void setOnInfoWindowClickListener(OnInfoWindowClickListener listener) {
261 | onInfoWindowClickListener = listener;
262 | }
263 |
264 | public void clearMarkers() {
265 | if (isInitialized()) {
266 | mapInterface.clearMarkers();
267 | }
268 | }
269 |
270 | public boolean addPolyline(AirMapPolyline polyline) {
271 | if (isInitialized()) {
272 | mapInterface.addPolyline(polyline);
273 | return true;
274 | }
275 | return false;
276 | }
277 |
278 | public void setMapType(MapType mapType) {
279 | mapInterface.setMapType(mapType);
280 | }
281 |
282 | public boolean removePolyline(AirMapPolyline polyline) {
283 | if (isInitialized()) {
284 | mapInterface.removePolyline(polyline);
285 | return true;
286 | }
287 | return false;
288 | }
289 |
290 | public boolean addPolygon(AirMapPolygon polygon) {
291 | if (isInitialized()) {
292 | mapInterface.addPolygon(polygon);
293 | return true;
294 | }
295 | return false;
296 | }
297 |
298 | public boolean removePolygon(AirMapPolygon polygon) {
299 | if (isInitialized()) {
300 | mapInterface.removePolygon(polygon);
301 | return true;
302 | }
303 | return false;
304 | }
305 |
306 | public void setGeoJsonLayer(AirMapGeoJsonLayer layer) throws JSONException {
307 | if (!isInitialized()) {
308 | return;
309 | }
310 | mapInterface.setGeoJsonLayer(layer);
311 | }
312 |
313 | public void clearGeoJsonLayer() {
314 | if (!isInitialized()) {
315 | return;
316 | }
317 | mapInterface.clearGeoJsonLayer();
318 | }
319 |
320 | public boolean isInitialized() {
321 | return mapInterface != null && mapInterface.isInitialized();
322 | }
323 |
324 | public boolean addMarker(AirMapMarker> marker) {
325 | if (isInitialized()) {
326 | mapInterface.addMarker(marker);
327 | return true;
328 | }
329 | return false;
330 | }
331 |
332 | public boolean removeMarker(AirMapMarker> marker) {
333 | if (isInitialized()) {
334 | mapInterface.removeMarker(marker);
335 | return true;
336 | }
337 | return false;
338 | }
339 |
340 | public boolean moveMarker(AirMapMarker> marker, LatLng to) {
341 | if (isInitialized()) {
342 | mapInterface.moveMarker(marker, to);
343 | return true;
344 | }
345 | return false;
346 | }
347 |
348 | public void setMyLocationEnabled(boolean trackUserLocation) {
349 | mapInterface.setMyLocationEnabled(trackUserLocation);
350 | }
351 |
352 | public void setMyLocationButtonEnabled(boolean enabled) {
353 | mapInterface.setMyLocationButtonEnabled(enabled);
354 | }
355 |
356 | @Override public void onCameraChanged(LatLng latLng, int zoom) {
357 | if (onCameraChangeListener != null) {
358 | onCameraChangeListener.onCameraChanged(latLng, zoom);
359 | }
360 | }
361 |
362 | @Override public void onMapClick(LatLng latLng) {
363 | if (onMapClickListener != null) {
364 | onMapClickListener.onMapClick(latLng);
365 | }
366 | }
367 |
368 | @Override public boolean onMapMarkerClick(AirMapMarker> airMarker) {
369 | if (onMapMarkerClickListener != null) {
370 | return onMapMarkerClickListener.onMapMarkerClick(airMarker);
371 | } else {
372 | return false;
373 | }
374 | }
375 |
376 | @Override public void onMapMarkerDragStart(Marker marker) {
377 | if (onMapMarkerDragListener != null) {
378 | onMapMarkerDragListener.onMapMarkerDragStart(marker);
379 | }
380 | }
381 |
382 | @Override public void onMapMarkerDrag(Marker marker) {
383 | if (onMapMarkerDragListener != null) {
384 | onMapMarkerDragListener.onMapMarkerDrag(marker);
385 | }
386 | }
387 |
388 | @Override public void onMapMarkerDragEnd(Marker marker) {
389 | if (onMapMarkerDragListener != null) {
390 | onMapMarkerDragListener.onMapMarkerDragEnd(marker);
391 | }
392 | }
393 |
394 | @Override public void onMapMarkerDragStart(long id, LatLng latLng) {
395 | if (onMapMarkerDragListener != null) {
396 | onMapMarkerDragListener.onMapMarkerDragStart(id, latLng);
397 | }
398 | }
399 |
400 | @Override public void onMapMarkerDrag(long id, LatLng latLng) {
401 | if (onMapMarkerDragListener != null) {
402 | onMapMarkerDragListener.onMapMarkerDrag(id, latLng);
403 | }
404 | }
405 |
406 | @Override public void onMapMarkerDragEnd(long id, LatLng latLng) {
407 | if (onMapMarkerDragListener != null) {
408 | onMapMarkerDragListener.onMapMarkerDragEnd(id, latLng);
409 | }
410 | }
411 |
412 | @Override public void onMapLoaded() {
413 | if (isInitialized()) {
414 | mapInterface.setOnCameraChangeListener(this);
415 | mapInterface.setOnMapClickListener(this);
416 | mapInterface.setOnMarkerClickListener(this);
417 | mapInterface.setOnMarkerDragListener(this);
418 | mapInterface.setOnInfoWindowClickListener(this);
419 |
420 | if (onMapInitializedListener != null) {
421 | // only send map Initialized callback if map initialized successfully and is laid out
422 | // initialization can fail if the map leaves the screen before it loads
423 | MapLaidOutCheckKt.doWhenMapIsLaidOut(this, () -> onMapInitializedListener.onMapInitialized());
424 | }
425 | }
426 | }
427 |
428 | @Override public void onInfoWindowClick(AirMapMarker> airMarker) {
429 | if (onInfoWindowClickListener != null) {
430 | onInfoWindowClickListener.onInfoWindowClick(airMarker);
431 | }
432 | }
433 | }
434 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapViewBuilder.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public interface AirMapViewBuilder {
4 |
5 | AirMapViewBuilder withOptions(Q arg);
6 |
7 | T build();
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/AirMapViewTypes.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | /**
4 | * Lists all available AirMapView implementations. Types are listed in order of preference.
5 | */
6 | public enum AirMapViewTypes {
7 | NATIVE, WEB
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/DefaultAirMapViewBuilder.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.Context;
4 | import android.content.pm.ApplicationInfo;
5 | import android.content.pm.PackageManager;
6 | import android.os.Bundle;
7 | import android.text.TextUtils;
8 | import android.util.Log;
9 |
10 | import com.google.android.gms.common.ConnectionResult;
11 | import com.google.android.gms.common.GooglePlayServicesUtil;
12 |
13 | /**
14 | * Use this class to request an AirMapView builder.
15 | */
16 | public class DefaultAirMapViewBuilder {
17 |
18 | private static final String TAG = DefaultAirMapViewBuilder.class.getSimpleName();
19 | private final boolean isNativeMapSupported;
20 | private final Context context;
21 |
22 | /**
23 | * Default {@link DefaultAirMapViewBuilder} constructor.
24 | *
25 | * @param context The application context.
26 | */
27 | public DefaultAirMapViewBuilder(Context context) {
28 | this(context, checkNativeMapSupported(context));
29 | }
30 |
31 | /**
32 | * @param isNativeMapSupported Whether or not Google Play services is available on the
33 | * device. If you set this to true and it is not available,
34 | * bad things can happen.
35 | */
36 | public DefaultAirMapViewBuilder(Context context, boolean isNativeMapSupported) {
37 | this.isNativeMapSupported = isNativeMapSupported;
38 | this.context = context;
39 | }
40 |
41 | /**
42 | * Returns the first/default supported AirMapView implementation in order of preference, as
43 | * defined by {@link AirMapViewTypes}.
44 | */
45 | public AirMapViewBuilder builder() {
46 | if (isNativeMapSupported) {
47 | return new NativeAirMapViewBuilder();
48 | }
49 | return getWebMapViewBuilder();
50 | }
51 |
52 | /**
53 | * Returns the AirMapView implementation as requested by the mapType argument. Use this method if
54 | * you need to request a specific AirMapView implementation that is not necessarily the preferred
55 | * type. For example, you can use it to explicit request a web-based map implementation.
56 | *
57 | * @param mapType Map type for the requested AirMapView implementation.
58 | * @return An {@link AirMapViewBuilder} for the requested {@link AirMapViewTypes} mapType.
59 | */
60 | public AirMapViewBuilder builder(AirMapViewTypes mapType) {
61 | switch (mapType) {
62 | case NATIVE:
63 | if (isNativeMapSupported) {
64 | return new NativeAirMapViewBuilder();
65 | }
66 | break;
67 | case WEB:
68 | return getWebMapViewBuilder();
69 | }
70 | throw new UnsupportedOperationException("Requested map type is not supported");
71 | }
72 |
73 | /**
74 | * Decides what the Map Web provider should be used and generates a builder for it.
75 | *
76 | * @return The AirMapViewBuilder for the selected Map Web provider.
77 | */
78 | private AirMapViewBuilder getWebMapViewBuilder() {
79 | if (context != null) {
80 | try {
81 | ApplicationInfo ai = context.getPackageManager()
82 | .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
83 | Bundle bundle = ai.metaData;
84 | String accessToken = bundle.getString("com.mapbox.ACCESS_TOKEN");
85 | String mapId = bundle.getString("com.mapbox.MAP_ID");
86 |
87 | if (!TextUtils.isEmpty(accessToken) && !TextUtils.isEmpty(mapId)) {
88 | return new MapboxWebMapViewBuilder(accessToken, mapId);
89 | }
90 | } catch (PackageManager.NameNotFoundException e) {
91 | Log.e(TAG, "Failed to load Mapbox access token and map id", e);
92 | }
93 | }
94 | return new WebAirMapViewBuilder();
95 | }
96 |
97 | private static boolean checkNativeMapSupported(Context context) {
98 | return isGooglePlayServicesAvailable(context);
99 | }
100 |
101 | private static boolean isGooglePlayServicesAvailable(Context context) {
102 | return GooglePlayServicesUtil.
103 | isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/FixedWebView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.Context;
4 | import android.content.res.Configuration;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.webkit.WebView;
8 |
9 | /**
10 | * FIXME: Remove this class when AppCompat bug is fixed.
11 | * In short, AppCompat 1.1.0 makes WebView crashes on Android 5.0~5.1.1 (Lollipop).
12 | * See https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview.
13 | */
14 | public class FixedWebView extends WebView {
15 | public FixedWebView(Context context) {
16 | super(fixedContext(context));
17 | }
18 |
19 | public FixedWebView(Context context, AttributeSet attrs) {
20 | super(fixedContext(context), attrs);
21 | }
22 |
23 | public FixedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
24 | super(fixedContext(context), attrs, defStyleAttr);
25 | }
26 |
27 | private static Context fixedContext(Context context) {
28 | if (Build.VERSION_CODES.LOLLIPOP == Build.VERSION.SDK_INT ||
29 | Build.VERSION_CODES.LOLLIPOP_MR1 == Build.VERSION.SDK_INT) {
30 | return context.createConfigurationContext(new Configuration());
31 | }
32 | return context;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/GoogleChinaMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class GoogleChinaMapType extends AirMapType {
4 |
5 | public GoogleChinaMapType() {
6 | super("google_map.html", "http://ditu.google.cn/maps/api/js", "www.google.cn");
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/GoogleChinaWebViewMapFragment.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class GoogleChinaWebViewMapFragment extends GoogleWebViewMapFragment {
4 | public static GoogleChinaWebViewMapFragment newInstance(AirMapType mapType) {
5 | return (GoogleChinaWebViewMapFragment) new GoogleChinaWebViewMapFragment()
6 | .setArguments(mapType);
7 | }
8 |
9 | @Override
10 | protected boolean isChinaMode() {
11 | return true;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/GoogleWebMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class GoogleWebMapType extends AirMapType {
4 |
5 | public GoogleWebMapType() {
6 | super("google_map.html", "https://maps.googleapis.com/maps/api/js", "www.googleapis.com");
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/GoogleWebViewMapFragment.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import java.util.Locale;
4 |
5 | public class GoogleWebViewMapFragment extends WebViewMapFragment {
6 | public static GoogleWebViewMapFragment newInstance(AirMapType mapType) {
7 | return (GoogleWebViewMapFragment) new GoogleWebViewMapFragment().setArguments(mapType);
8 | }
9 |
10 | @Override public void setMapType(MapType type) {
11 | String webType = null;
12 | switch (type) {
13 | case MAP_TYPE_NORMAL:
14 | webType = "google.maps.MapTypeId.ROADMAP";
15 | break;
16 | case MAP_TYPE_SATELLITE:
17 | webType = "google.maps.MapTypeId.SATELLITE";
18 | break;
19 | case MAP_TYPE_TERRAIN:
20 | webType = "google.maps.MapTypeId.TERRAIN";
21 | break;
22 | }
23 | webView.loadUrl(String.format(Locale.US, "javascript:setMapTypeId(%1$s);", webType));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletBaiduMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class LeafletBaiduMapType extends LeafletMapType {
4 |
5 | public LeafletBaiduMapType() {
6 | super("Baidu");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletDivIcon.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class LeafletDivIcon {
4 | private final String html;
5 | private final int width;
6 | private final int height;
7 |
8 | public LeafletDivIcon(String html, int width, int height) {
9 | this.html = html;
10 | this.width = width;
11 | this.height = height;
12 | }
13 |
14 | public String getHtml() {
15 | return html;
16 | }
17 |
18 | public int getWidth() {
19 | return width;
20 | }
21 |
22 | public int getHeight() {
23 | return height;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletGaodeMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class LeafletGaodeMapType extends LeafletMapType {
4 |
5 | public LeafletGaodeMapType() {
6 | super("Gaode");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletGoogleChinaMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class LeafletGoogleChinaMapType extends LeafletMapType {
4 |
5 | public LeafletGoogleChinaMapType() {
6 | super("GoogleChina");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletGoogleMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public class LeafletGoogleMapType extends LeafletMapType {
4 |
5 | public LeafletGoogleMapType() {
6 | super("Google");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public abstract class LeafletMapType extends AirMapType {
4 |
5 | // For leaflet, we define some map provider in leaflet_map.html file.
6 | // So need to supply a provider name here. (like Google, GoogleChina, Baidu, Gaode)
7 | public LeafletMapType(String mapProvider) {
8 | super("leaflet_map.html", mapProvider, "");
9 | }
10 |
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/LeafletWebViewMapFragment.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | import java.util.Locale;
6 |
7 | public class LeafletWebViewMapFragment extends WebViewMapFragment {
8 | public static LeafletWebViewMapFragment newInstance(AirMapType mapType) {
9 | return (LeafletWebViewMapFragment) new LeafletWebViewMapFragment().setArguments(mapType);
10 | }
11 |
12 | @Override
13 | public void setMapType(MapType type) {
14 | String webType = null;
15 | switch (type) {
16 | case MAP_TYPE_NORMAL:
17 | webType = "Normal";
18 | break;
19 | case MAP_TYPE_SATELLITE:
20 | webType = "Satellite";
21 | break;
22 | case MAP_TYPE_TERRAIN:
23 | webType = "Terrain";
24 | break;
25 | }
26 | webView.loadUrl(String.format(Locale.US, "javascript:setMapTypeId('%1$s');", webType));
27 | }
28 |
29 | @Override public void addMarker(AirMapMarker> marker) {
30 | if (marker == null || marker.getDivIcon() == null || marker.getDivIcon().getHtml() == null) {
31 | super.addMarker(marker);
32 | } else {
33 | LatLng latLng = marker.getLatLng();
34 | markers.put(marker.getId(), marker);
35 | webView.loadUrl(
36 | String.format(Locale.US,
37 | "javascript:addMarkerWithId(%1$f, %2$f, %3$d, '%4$s', '%5$s', %6$b, '%7$s', %8$d, " +
38 | "%9$d);",
39 | latLng.latitude, latLng.longitude, marker.getId(), marker.getTitle(),
40 | marker.getSnippet(), marker.getMarkerOptions().isDraggable(),
41 | marker.getDivIcon().getHtml(), marker.getDivIcon().getWidth(),
42 | marker.getDivIcon().getHeight()));
43 | }
44 | }
45 |
46 | @Override
47 | public void setCenterZoom(LatLng latLng, int zoom) {
48 | webView.loadUrl(String.format(Locale.US, "javascript:centerZoomMap(%1$f, %2$f, %3$d);", latLng.latitude,
49 | latLng.longitude, zoom));
50 | }
51 |
52 | @Override
53 | public void animateCenter(LatLng latLng) {
54 | webView.loadUrl(String.format(Locale.US, "javascript:animateCenterMap(%1$f, %2$f);", latLng.latitude,
55 | latLng.longitude));
56 | }
57 |
58 | @Override
59 | public void animateCenterZoom(LatLng latLng, int zoom) {
60 | webView.loadUrl(String.format(Locale.US, "javascript:animateCenterZoomMap(%1$f, %2$f, %3$d);", latLng.latitude,
61 | latLng.longitude, zoom));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/MapLaidOutCheck.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview
2 |
3 | import androidx.core.view.doOnLayout
4 |
5 | fun AirMapView.doWhenMapIsLaidOut(runnable: Runnable) = doOnLayout { runnable.run() }
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/MapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | public enum MapType {
4 | MAP_TYPE_NORMAL, MAP_TYPE_SATELLITE, MAP_TYPE_TERRAIN
5 | }
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/MapboxWebMapType.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | public class MapboxWebMapType extends AirMapType {
7 | private final String mapId;
8 | private final String accessToken;
9 |
10 | protected static final String ARG_MAPBOX_ACCESS_TOKEN = "MAPBOX_ACCESS_TOKEN";
11 | protected static final String ARG_MAPBOX_MAPID = "MAPBOX_MAPID";
12 |
13 | /**
14 | * Primary Constructor
15 | *
16 | * @param accessToken Mapbox Access Token
17 | * @param mapId Mapbox Map Id
18 | */
19 | public MapboxWebMapType(String accessToken, String mapId) {
20 | super("mapbox.html", "https://api.tiles.mapbox.com/mapbox.js/v2.2.1", "www.mapbox.com");
21 | this.accessToken = accessToken;
22 | this.mapId = mapId;
23 | }
24 |
25 | /**
26 | * Private Constructor used for Bundle Serialization
27 | *
28 | * @param fileName File Name
29 | * @param mapUrl Map URL
30 | * @param domain Map Domain
31 | * @param accessToken Mapbox Access Token
32 | * @param mapId Mapbox Map Id
33 | */
34 | private MapboxWebMapType(String fileName, String mapUrl, String domain,
35 | String accessToken, String mapId) {
36 | super(fileName, mapUrl, domain);
37 | this.accessToken = accessToken;
38 | this.mapId = mapId;
39 | }
40 |
41 | public Bundle toBundle(Bundle bundle) {
42 | super.toBundle(bundle);
43 | bundle.putString(ARG_MAPBOX_ACCESS_TOKEN, accessToken);
44 | bundle.putString(ARG_MAPBOX_MAPID, mapId);
45 | return bundle;
46 | }
47 |
48 | public static MapboxWebMapType fromBundle(Bundle bundle) {
49 | AirMapType airMapType = AirMapType.fromBundle(bundle);
50 | String mapboxAccessToken = bundle.getString(ARG_MAPBOX_ACCESS_TOKEN, "");
51 | String mapboxMapId = bundle.getString(ARG_MAPBOX_MAPID, "");
52 | return new MapboxWebMapType(airMapType.getFileName(), airMapType.getMapUrl(),
53 | airMapType.getDomain(), mapboxAccessToken, mapboxMapId);
54 | }
55 |
56 | @Override
57 | public String getMapData(Resources resources) {
58 | String mapData = super.getMapData(resources);
59 |
60 | mapData = mapData.replace(ARG_MAPBOX_ACCESS_TOKEN, accessToken);
61 | mapData = mapData.replace(ARG_MAPBOX_MAPID, mapId);
62 |
63 | return mapData;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/MapboxWebMapViewBuilder.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | /**
4 | * AirMapView map that uses the web based Mapbox implementation.
5 | */
6 | public class MapboxWebMapViewBuilder implements AirMapViewBuilder {
7 |
8 | private AirMapType options;
9 | private final String accessToken;
10 | private final String mapId;
11 |
12 | /**
13 | * Constructor
14 | *
15 | * @param accessToken Mapbox Access Token
16 | * @param mapId Mapbox Map Id
17 | */
18 | public MapboxWebMapViewBuilder(String accessToken, String mapId) {
19 | super();
20 | this.accessToken = accessToken;
21 | this.mapId = mapId;
22 | }
23 |
24 | @Override
25 | public AirMapViewBuilder withOptions(AirMapType options) {
26 | this.options = options;
27 | return this;
28 | }
29 |
30 | /**
31 | * Build the map fragment with the requested options
32 | *
33 | * @return The {@link WebViewMapFragment} map fragment.
34 | */
35 | @Override
36 | public WebViewMapFragment build() {
37 | if (options == null) {
38 | options = new MapboxWebMapType(accessToken, mapId);
39 | }
40 | if (options instanceof MapboxWebMapType) {
41 | return MapboxWebViewMapFragment.newInstance(options);
42 | }
43 | throw new IllegalStateException("Unable to build MapboxWebMapViewFragment." +
44 | " options == '" + options + "'");
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/MapboxWebViewMapFragment.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import java.util.Locale;
9 |
10 | public class MapboxWebViewMapFragment extends WebViewMapFragment {
11 |
12 | public static MapboxWebViewMapFragment newInstance(AirMapType mapType) {
13 | return (MapboxWebViewMapFragment) new MapboxWebViewMapFragment().setArguments(mapType);
14 | }
15 |
16 | @Override public View onCreateView(
17 | LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
18 |
19 | View view = super.onCreateView(inflater, container, savedInstanceState);
20 |
21 | MapboxWebMapType mapType = MapboxWebMapType.fromBundle(getArguments());
22 | webView.loadDataWithBaseURL(mapType.getDomain(), mapType.getMapData(getResources()),
23 | "text/html", "base64", null);
24 |
25 | return view;
26 | }
27 |
28 | @Override public void setMapType(MapType type) {
29 | String mapBoxType = null;
30 | switch (type) {
31 | case MAP_TYPE_NORMAL:
32 | mapBoxType = "mapbox.streets";
33 | break;
34 | case MAP_TYPE_SATELLITE:
35 | mapBoxType = "mapbox.satellite";
36 | break;
37 | case MAP_TYPE_TERRAIN:
38 | mapBoxType = "mapbox.outdoors";
39 | break;
40 | }
41 | webView.loadUrl(String.format(Locale.US, "javascript:setMapTypeId(\"%1$s\");", mapBoxType));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/NativeAirMapViewBuilder.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import com.google.android.gms.maps.GoogleMapOptions;
4 |
5 | /**
6 | * AirMapView map that uses the native Google Maps implementation. IMPORTANT: In order to use this,
7 | * Google Play Services needs to be installed on the device.
8 | */
9 | public class NativeAirMapViewBuilder
10 | implements AirMapViewBuilder {
11 |
12 | private AirGoogleMapOptions options;
13 |
14 | @Override public AirMapViewBuilder withOptions(
15 | AirGoogleMapOptions options) {
16 | this.options = options;
17 | return this;
18 | }
19 |
20 | /**
21 | * Build the map fragment with the requested options
22 | *
23 | * @return The {@link NativeGoogleMapFragment} map fragment.
24 | */
25 | @Override public NativeGoogleMapFragment build() {
26 | if (options == null) {
27 | options = new AirGoogleMapOptions(new GoogleMapOptions());
28 | }
29 | return NativeGoogleMapFragment.newInstance(options);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/NativeGoogleMapFragment.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Point;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import androidx.annotation.NonNull;
11 | import com.airbnb.android.airmapview.listeners.InfoWindowCreator;
12 | import com.airbnb.android.airmapview.listeners.OnCameraChangeListener;
13 | import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener;
14 | import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback;
15 | import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback;
16 | import com.airbnb.android.airmapview.listeners.OnMapClickListener;
17 | import com.airbnb.android.airmapview.listeners.OnMapLoadedListener;
18 | import com.airbnb.android.airmapview.listeners.OnMapMarkerClickListener;
19 | import com.airbnb.android.airmapview.listeners.OnMapMarkerDragListener;
20 | import com.airbnb.android.airmapview.listeners.OnSnapshotReadyListener;
21 | import com.google.android.gms.maps.CameraUpdateFactory;
22 | import com.google.android.gms.maps.GoogleMap;
23 | import com.google.android.gms.maps.OnMapReadyCallback;
24 | import com.google.android.gms.maps.Projection;
25 | import com.google.android.gms.maps.SupportMapFragment;
26 | import com.google.android.gms.maps.UiSettings;
27 | import com.google.android.gms.maps.model.CameraPosition;
28 | import com.google.android.gms.maps.model.CircleOptions;
29 | import com.google.android.gms.maps.model.LatLng;
30 | import com.google.android.gms.maps.model.LatLngBounds;
31 | import com.google.android.gms.maps.model.Marker;
32 | import com.google.android.gms.maps.model.Polygon;
33 | import com.google.maps.android.geojson.GeoJsonLayer;
34 | import com.google.maps.android.geojson.GeoJsonPolygonStyle;
35 |
36 | import org.json.JSONException;
37 | import org.json.JSONObject;
38 |
39 | import java.util.HashMap;
40 | import java.util.Map;
41 |
42 | public class NativeGoogleMapFragment extends SupportMapFragment implements AirMapInterface {
43 | private GoogleMap googleMap;
44 | private OnMapLoadedListener onMapLoadedListener;
45 | private boolean myLocationEnabled;
46 | private GeoJsonLayer layerOnMap;
47 | private final Map> markers = new HashMap<>();
48 |
49 | public static NativeGoogleMapFragment newInstance(AirGoogleMapOptions options) {
50 | return new NativeGoogleMapFragment().setArguments(options);
51 | }
52 |
53 | public NativeGoogleMapFragment setArguments(AirGoogleMapOptions options) {
54 | setArguments(options.toBundle());
55 | return this;
56 | }
57 |
58 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
59 | Bundle savedInstanceState) {
60 | View v = super.onCreateView(inflater, container, savedInstanceState);
61 |
62 | init();
63 |
64 | return v;
65 | }
66 |
67 | public void init() {
68 | getMapAsync(new OnMapReadyCallback() {
69 | @Override public void onMapReady(GoogleMap googleMap) {
70 | if (googleMap != null && getActivity() != null) {
71 | NativeGoogleMapFragment.this.googleMap = googleMap;
72 | UiSettings settings = NativeGoogleMapFragment.this.googleMap.getUiSettings();
73 | settings.setZoomControlsEnabled(false);
74 | settings.setMyLocationButtonEnabled(false);
75 | setMyLocationEnabled(myLocationEnabled);
76 |
77 | if (onMapLoadedListener != null) {
78 | onMapLoadedListener.onMapLoaded();
79 | }
80 | }
81 | }
82 | });
83 | }
84 |
85 | @Override public boolean isInitialized() {
86 | return googleMap != null && getActivity() != null;
87 | }
88 |
89 | @Override public void clearMarkers() {
90 | markers.clear();
91 | googleMap.clear();
92 | }
93 |
94 | @Override public void addMarker(AirMapMarker> airMarker) {
95 | Marker marker = googleMap.addMarker(airMarker.getMarkerOptions());
96 | airMarker.setGoogleMarker(marker);
97 | markers.put(marker, airMarker);
98 | }
99 |
100 | @Override public void moveMarker(AirMapMarker> marker, LatLng to) {
101 | marker.setLatLng(to);
102 | marker.getMarker().setPosition(to);
103 | }
104 |
105 | @Override public void removeMarker(AirMapMarker> marker) {
106 | Marker nativeMarker = marker.getMarker();
107 | if (nativeMarker != null) {
108 | nativeMarker.remove();
109 | markers.remove(nativeMarker);
110 | }
111 | }
112 |
113 | @Override public void setOnInfoWindowClickListener(final OnInfoWindowClickListener listener) {
114 | googleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
115 | @Override public void onInfoWindowClick(Marker marker) {
116 | AirMapMarker> airMarker = markers.get(marker);
117 | if (airMarker != null) {
118 | listener.onInfoWindowClick(airMarker);
119 | }
120 | }
121 | });
122 | }
123 |
124 | @Override public void setInfoWindowCreator(GoogleMap.InfoWindowAdapter adapter,
125 | InfoWindowCreator creator) {
126 | googleMap.setInfoWindowAdapter(adapter);
127 | }
128 |
129 | @Override public void drawCircle(LatLng latLng, int radius) {
130 | drawCircle(latLng, radius, CIRCLE_BORDER_COLOR);
131 | }
132 |
133 | @Override public void drawCircle(LatLng latLng, int radius, int borderColor) {
134 | drawCircle(latLng, radius, borderColor, CIRCLE_BORDER_WIDTH);
135 | }
136 |
137 | @Override public void drawCircle(LatLng latLng, int radius, int borderColor, int borderWidth) {
138 | drawCircle(latLng, radius, borderColor, borderWidth, CIRCLE_FILL_COLOR);
139 | }
140 |
141 | @Override public void drawCircle(LatLng latLng, int radius, int borderColor, int borderWidth,
142 | int fillColor) {
143 | googleMap.addCircle(new CircleOptions()
144 | .center(latLng)
145 | .strokeColor(borderColor)
146 | .strokeWidth(borderWidth)
147 | .fillColor(fillColor)
148 | .radius(radius));
149 | }
150 |
151 | @Override public void getMapScreenBounds(OnMapBoundsCallback callback) {
152 | final Projection projection = googleMap.getProjection();
153 | int hOffset = getResources().getDimensionPixelOffset(R.dimen.map_horizontal_padding);
154 | int vOffset = getResources().getDimensionPixelOffset(R.dimen.map_vertical_padding);
155 |
156 | LatLngBounds.Builder builder = LatLngBounds.builder();
157 | builder.include(projection.fromScreenLocation(new Point(hOffset, vOffset))); // top-left
158 | builder.include(projection.fromScreenLocation(
159 | new Point(getView().getWidth() - hOffset, vOffset))); // top-right
160 | builder.include(projection.fromScreenLocation(
161 | new Point(hOffset, getView().getHeight() - vOffset))); // bottom-left
162 | builder.include(projection.fromScreenLocation(new Point(getView().getWidth() - hOffset,
163 | getView().getHeight() - vOffset))); // bottom-right
164 |
165 | callback.onMapBoundsReady(builder.build());
166 | }
167 |
168 | @Override public void getScreenLocation(LatLng latLng, OnLatLngScreenLocationCallback callback) {
169 | callback.onLatLngScreenLocationReady(googleMap.getProjection().toScreenLocation(latLng));
170 | }
171 |
172 | @Override public void setCenter(LatLngBounds latLngBounds, int boundsPadding) {
173 | googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, boundsPadding));
174 | }
175 |
176 | @Override public void setZoom(int zoom) {
177 | googleMap.animateCamera(
178 | CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, zoom));
179 | }
180 |
181 | @Override public void animateCenter(LatLng latLng) {
182 | googleMap.animateCamera(CameraUpdateFactory.newLatLng(latLng));
183 | }
184 |
185 | @Override public void setCenter(LatLng latLng) {
186 | googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
187 | }
188 |
189 | @Override public LatLng getCenter() {
190 | return googleMap.getCameraPosition().target;
191 | }
192 |
193 | @Override public int getZoom() {
194 | return (int) googleMap.getCameraPosition().zoom;
195 | }
196 |
197 | @Override
198 | public void setOnCameraChangeListener(final OnCameraChangeListener onCameraChangeListener) {
199 | googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
200 | @Override public void onCameraChange(CameraPosition cameraPosition) {
201 | // camera change can occur programmatically.
202 | if (isResumed()) {
203 | onCameraChangeListener.onCameraChanged(cameraPosition.target, (int) cameraPosition.zoom);
204 | }
205 | }
206 | });
207 | }
208 |
209 | @Override public void setOnMapLoadedListener(OnMapLoadedListener onMapLoadedListener) {
210 | this.onMapLoadedListener = onMapLoadedListener;
211 | }
212 |
213 | @Override public void setCenterZoom(LatLng latLng, int zoom) {
214 | googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom));
215 | }
216 |
217 | @Override public void animateCenterZoom(LatLng latLng, int zoom) {
218 | googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom));
219 | }
220 |
221 | @Override public void setOnMarkerClickListener(final OnMapMarkerClickListener listener) {
222 | googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
223 | @Override public boolean onMarkerClick(Marker marker) {
224 | AirMapMarker> airMarker = markers.get(marker);
225 | if (airMarker != null) {
226 | return listener.onMapMarkerClick(airMarker);
227 | }
228 | return false;
229 | }
230 | });
231 | }
232 |
233 | @Override public void setOnMarkerDragListener(final OnMapMarkerDragListener listener) {
234 | if (listener == null) {
235 | googleMap.setOnMarkerDragListener(null);
236 | return;
237 | }
238 | googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
239 | @Override
240 | public void onMarkerDragStart(Marker marker) {
241 | listener.onMapMarkerDragStart(marker);
242 | }
243 |
244 | @Override
245 | public void onMarkerDrag(Marker marker) {
246 | listener.onMapMarkerDrag(marker);
247 | }
248 |
249 | @Override
250 | public void onMarkerDragEnd(Marker marker) {
251 | listener.onMapMarkerDragEnd(marker);
252 | }
253 | });
254 | }
255 |
256 | @Override public void setOnMapClickListener(final OnMapClickListener listener) {
257 | googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
258 | @Override
259 | public void onMapClick(LatLng latLng) {
260 | listener.onMapClick(latLng);
261 | }
262 | });
263 | }
264 |
265 | @Override public void setPadding(int left, int top, int right, int bottom) {
266 | googleMap.setPadding(left, top, right, bottom);
267 | }
268 |
269 | @Override public void setMyLocationEnabled(boolean enabled) {
270 | if (myLocationEnabled != enabled) {
271 | myLocationEnabled = enabled;
272 | if (!RuntimePermissionUtils.checkLocationPermissions(getActivity(), this)) {
273 | myLocationEnabled = false;
274 | }
275 | }
276 | }
277 |
278 | @Override public void onLocationPermissionsGranted() {
279 | //noinspection MissingPermission
280 | googleMap.setMyLocationEnabled(myLocationEnabled);
281 | }
282 |
283 | @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
284 | @NonNull int[] grantResults) {
285 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
286 | RuntimePermissionUtils.onRequestPermissionsResult(this, requestCode, grantResults);
287 | }
288 |
289 | @Override public boolean isMyLocationEnabled() {
290 | return googleMap.isMyLocationEnabled();
291 | }
292 |
293 | @Override public void setMyLocationButtonEnabled(boolean enabled) {
294 | googleMap.getUiSettings().setMyLocationButtonEnabled(enabled);
295 | }
296 |
297 | @Override public void setMapToolbarEnabled(boolean enabled) {
298 | googleMap.getUiSettings().setMapToolbarEnabled(enabled);
299 | }
300 |
301 | @Override public void addPolyline(AirMapPolyline polyline) {
302 | polyline.addToGoogleMap(googleMap);
303 | }
304 |
305 | @Override public void removePolyline(AirMapPolyline polyline) {
306 | polyline.removeFromGoogleMap();
307 | }
308 |
309 | @Override public void addPolygon(AirMapPolygon polygon) {
310 | Polygon googlePolygon = googleMap.addPolygon(polygon.getPolygonOptions());
311 | polygon.setGooglePolygon(googlePolygon);
312 | }
313 |
314 | @Override public void removePolygon(AirMapPolygon polygon) {
315 | Polygon nativePolygon = polygon.getGooglePolygon();
316 | if (nativePolygon != null) {
317 | nativePolygon.remove();
318 | }
319 | }
320 |
321 | @Override public void setMapType(MapType type) {
322 | int nativeType = 0;
323 | switch (type) {
324 | case MAP_TYPE_NORMAL:
325 | nativeType = GoogleMap.MAP_TYPE_NORMAL;
326 | break;
327 | case MAP_TYPE_SATELLITE:
328 | nativeType = GoogleMap.MAP_TYPE_SATELLITE;
329 | break;
330 | case MAP_TYPE_TERRAIN:
331 | nativeType = GoogleMap.MAP_TYPE_TERRAIN;
332 | break;
333 | }
334 | googleMap.setMapType(nativeType);
335 | }
336 |
337 | /**
338 | * This method will return the google map if initialized. Will return null otherwise
339 | *
340 | * @return returns google map if initialized
341 | */
342 | public GoogleMap getGoogleMap() {
343 | return googleMap;
344 | }
345 |
346 | @Override
347 | public void setGeoJsonLayer(final AirMapGeoJsonLayer airMapGeoJsonLayer) throws JSONException {
348 | // clear any existing layers
349 | clearGeoJsonLayer();
350 |
351 | layerOnMap = new GeoJsonLayer(googleMap, new JSONObject(airMapGeoJsonLayer.geoJson));
352 | GeoJsonPolygonStyle style = layerOnMap.getDefaultPolygonStyle();
353 | style.setStrokeColor(airMapGeoJsonLayer.strokeColor);
354 | style.setStrokeWidth(airMapGeoJsonLayer.strokeWidth);
355 | style.setFillColor(airMapGeoJsonLayer.fillColor);
356 | layerOnMap.addLayerToMap();
357 | }
358 |
359 | @Override public void clearGeoJsonLayer() {
360 | if (layerOnMap == null) {
361 | return;
362 | }
363 | layerOnMap.removeLayerFromMap();
364 | layerOnMap = null;
365 | }
366 |
367 | @Override public void getSnapshot(final OnSnapshotReadyListener listener) {
368 | getGoogleMap().snapshot(new GoogleMap.SnapshotReadyCallback() {
369 | @Override public void onSnapshotReady(Bitmap bitmap) {
370 | listener.onSnapshotReady(bitmap);
371 | }
372 | });
373 | }
374 |
375 | @Override public void onDestroyView() {
376 | clearGeoJsonLayer();
377 | super.onDestroyView();
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/RuntimePermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import androidx.core.app.ActivityCompat;
8 |
9 | import static androidx.core.content.PermissionChecker.checkSelfPermission;
10 |
11 | /**
12 | * Utility class that handles runtime permissions
13 | */
14 | final class RuntimePermissionUtils {
15 |
16 | private RuntimePermissionUtils() {
17 | }
18 |
19 | private static final byte LOCATION_PERMISSION_REQUEST_CODE = 1;
20 | private static final String[] LOCATION_PERMISSIONS =
21 | new String[] { "android.permission.ACCESS_FINE_LOCATION",
22 | "android.permission.ACCESS_COARSE_LOCATION" };
23 |
24 | /**
25 | * Verifies that any of the given permissions have been granted.
26 | *
27 | * @return returns true if all permissions have been granted.
28 | */
29 | private static boolean verifyPermissions(int... grantResults) {
30 | for (int result : grantResults) {
31 | if (result != PackageManager.PERMISSION_GRANTED) {
32 | return false;
33 | }
34 | }
35 | return true;
36 | }
37 |
38 | /**
39 | * Returns true if the context has access to any given permissions.
40 | */
41 | private static boolean hasSelfPermissions(Context context, String... permissions) {
42 | for (String permission : permissions) {
43 | if (checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
44 | return true;
45 | }
46 | }
47 | return false;
48 | }
49 |
50 | /**
51 | * Checks given permissions are needed to show rationale.
52 | *
53 | * @return returns true if one of the permission is needed to show rationale.
54 | */
55 | static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
56 | for (String permission : permissions) {
57 | if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
58 | return true;
59 | }
60 | }
61 | return false;
62 | }
63 |
64 | /**
65 | * Check if any location permissions are granted, and invoke onLocationPermissionsGranted() in the
66 | * callback if granted. It will ask users for location permissions and invoke the same callback if
67 | * no permission was granted and got granted at runtime.
68 | *
69 | * @param airMapInterface the callback interface if permission is granted.
70 | */
71 | static boolean checkLocationPermissions(Activity targetActivity,
72 | AirMapInterface airMapInterface) {
73 | if (hasSelfPermissions(targetActivity, LOCATION_PERMISSIONS)) {
74 | airMapInterface.onLocationPermissionsGranted();
75 | return true;
76 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
77 | targetActivity.requestPermissions(LOCATION_PERMISSIONS, LOCATION_PERMISSION_REQUEST_CODE);
78 | }
79 | //else don't have location permissions in pre M, don't do anything.
80 | return false;
81 | }
82 |
83 | /**
84 | * Dispatch actions based off requested permission results.
85 | * Further actions like
86 | * 1> Rationale: showing a snack bar to explain why the permissions are needed and
87 | * 2> Denied: adding airMapInterface.onLocationPermissionsDenied()
88 | * should be added here if needed.
89 | */
90 | static void onRequestPermissionsResult(AirMapInterface airMapInterface, int requestCode,
91 | int[] grantResults) {
92 | switch (requestCode) {
93 | case LOCATION_PERMISSION_REQUEST_CODE:
94 | if (verifyPermissions(grantResults)) {
95 | airMapInterface.onLocationPermissionsGranted();
96 | }
97 | break;
98 | default:
99 | break;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/WebAirMapViewBuilder.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | /**
4 | * AirMapView map that uses the web based Google Maps implementation.
5 | */
6 | public class WebAirMapViewBuilder implements AirMapViewBuilder {
7 |
8 | private AirMapType options;
9 |
10 | @Override
11 | public AirMapViewBuilder withOptions(AirMapType options) {
12 | this.options = options;
13 | return this;
14 | }
15 |
16 | /**
17 | * Build the map fragment with the requested options
18 | *
19 | * @return The {@link WebViewMapFragment} map fragment.
20 | */
21 | @Override public WebViewMapFragment build() {
22 | if (options == null) {
23 | options = new GoogleWebMapType();
24 | }
25 | if (options instanceof GoogleWebMapType) {
26 | return GoogleWebViewMapFragment.newInstance(options);
27 | }
28 | if (options instanceof GoogleChinaMapType) {
29 | return GoogleChinaWebViewMapFragment.newInstance(options);
30 | }
31 | if (options instanceof LeafletMapType) {
32 | return LeafletWebViewMapFragment.newInstance(options);
33 | }
34 | return null;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/InfoWindowCreator.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import android.view.View;
4 |
5 | import com.airbnb.android.airmapview.AirMapMarker;
6 |
7 | public interface InfoWindowCreator {
8 | View createInfoWindow(AirMapMarker> airMarker);
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnCameraChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | public interface OnCameraChangeListener {
6 | void onCameraChanged(LatLng latLng, int zoom);
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnCameraMoveListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | /**
4 | * This event is triggered once as soon as the map camera starts moving and then is not triggered
5 | * again until the next time the user moves the map camera again. This is handled by AirMapView
6 | * instead of the actual GoogleMap implementation since this is not supported by them.
7 | */
8 | public interface OnCameraMoveListener {
9 | void onCameraMove();
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnInfoWindowClickListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.airbnb.android.airmapview.AirMapMarker;
4 |
5 | public interface OnInfoWindowClickListener {
6 | void onInfoWindowClick(AirMapMarker> airMarker);
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnLatLngScreenLocationCallback.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import android.graphics.Point;
4 |
5 | public interface OnLatLngScreenLocationCallback {
6 | void onLatLngScreenLocationReady(Point point);
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapBoundsCallback.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.google.android.gms.maps.model.LatLngBounds;
4 |
5 | public interface OnMapBoundsCallback {
6 | void onMapBoundsReady(LatLngBounds bounds);
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapClickListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 |
5 | public interface OnMapClickListener {
6 | void onMapClick(LatLng latLng);
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapInitializedListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | public interface OnMapInitializedListener {
4 | void onMapInitialized();
5 | }
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapLoadedListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | public interface OnMapLoadedListener {
4 | void onMapLoaded();
5 | }
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapMarkerClickListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.airbnb.android.airmapview.AirMapMarker;
4 |
5 | public interface OnMapMarkerClickListener {
6 |
7 | /*
8 | * Called when an airMarker has been clicked or tapped.
9 | * Return true if the listener has consumed the event (i.e., the default behavior should not occur);
10 | * false otherwise (i.e., the default behavior should occur).
11 | * The default behavior is for the camera to move to the marker and an info window to appear.
12 | * See: https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.OnMarkerClickListener
13 | * */
14 | boolean onMapMarkerClick(AirMapMarker> airMarker);
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnMapMarkerDragListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 | import com.google.android.gms.maps.model.Marker;
5 |
6 | public interface OnMapMarkerDragListener {
7 | void onMapMarkerDragStart(Marker marker);
8 |
9 | void onMapMarkerDrag(Marker marker);
10 |
11 | void onMapMarkerDragEnd(Marker marker);
12 |
13 | void onMapMarkerDragStart(long id, LatLng latLng);
14 |
15 | void onMapMarkerDrag(long id, LatLng latLng);
16 |
17 | void onMapMarkerDragEnd(long id, LatLng latLng);
18 | }
19 |
--------------------------------------------------------------------------------
/library/src/main/java/com/airbnb/android/airmapview/listeners/OnSnapshotReadyListener.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview.listeners;
2 |
3 | import android.graphics.Bitmap;
4 | import androidx.annotation.Nullable;
5 |
6 | public interface OnSnapshotReadyListener {
7 | void onSnapshotReady(@Nullable Bitmap bitmap);
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/fragment_webview.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/map_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 20dp
4 | 40dp
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AirMapView
3 |
4 |
--------------------------------------------------------------------------------
/library/src/test/java/com/airbnb/android/airmapview/AirMapTypeTest.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.hamcrest.core.IsEqual;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.assertThat;
9 | import static org.mockito.Mockito.mock;
10 | import static org.mockito.Mockito.verify;
11 | import static org.mockito.Mockito.when;
12 |
13 | public class AirMapTypeTest {
14 |
15 | @Test public void shouldConvertToBundle() {
16 | Bundle bundle = mock(Bundle.class);
17 | AirMapType mapType = new GoogleWebMapType();
18 | mapType.toBundle(bundle);
19 |
20 | verify(bundle).putString("map_domain", mapType.getDomain());
21 | verify(bundle).putString("map_url", mapType.getMapUrl());
22 | verify(bundle).putString("map_file_name", mapType.getFileName());
23 | }
24 |
25 | @Test public void shouldConstructFromBundle() {
26 | GoogleWebMapType mapType = new GoogleWebMapType();
27 | Bundle bundle = mock(Bundle.class);
28 |
29 | when(bundle.getString("map_domain", "")).thenReturn(mapType.getDomain());
30 | when(bundle.getString("map_url", "")).thenReturn(mapType.getMapUrl());
31 | when(bundle.getString("map_file_name", "")).thenReturn(mapType.getFileName());
32 |
33 | assertThat(AirMapType.fromBundle(bundle), IsEqual.equalTo(mapType));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/library/src/test/java/com/airbnb/android/airmapview/DefaultAirMapViewBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.instanceOf;
7 | import static org.junit.Assert.assertThat;
8 |
9 | public class DefaultAirMapViewBuilderTest {
10 |
11 | @Before public void setUp() {
12 | }
13 |
14 | @Test public void shouldReturnNativeAirMapViewByDefault() {
15 | DefaultAirMapViewBuilder factory = new DefaultAirMapViewBuilder(null, true);
16 | assertThat(factory.builder(), instanceOf(NativeAirMapViewBuilder.class));
17 | }
18 |
19 | @Test public void shouldReturnWebAirMapViewIfDefaultNotSupported() {
20 | DefaultAirMapViewBuilder factory = new DefaultAirMapViewBuilder(null, false);
21 | assertThat(factory.builder(), instanceOf(WebAirMapViewBuilder.class));
22 | }
23 |
24 | @Test public void shouldReturnNativeAirMapViewWhenRequestedExplicitly() {
25 | DefaultAirMapViewBuilder factory = new DefaultAirMapViewBuilder(null, true);
26 | AirMapViewBuilder builder = factory.builder(AirMapViewTypes.NATIVE);
27 | assertThat(builder, instanceOf(NativeAirMapViewBuilder.class));
28 | }
29 |
30 | @Test(expected = UnsupportedOperationException.class)
31 | public void shouldThrowWhenRequestedNativeWebViewAndNotSupported() {
32 | DefaultAirMapViewBuilder factory = new DefaultAirMapViewBuilder(null, false);
33 | factory.builder(AirMapViewTypes.NATIVE);
34 | }
35 |
36 | @Test public void shouldReturnWebAirMapViewWhenRequestedExplicitly() {
37 | DefaultAirMapViewBuilder factory = new DefaultAirMapViewBuilder(null, false);
38 | AirMapViewBuilder builder = factory.builder(AirMapViewTypes.WEB);
39 | assertThat(builder, instanceOf(WebAirMapViewBuilder.class));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/library/src/test/java/com/airbnb/android/airmapview/NativeAirMapViewBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.CoreMatchers.instanceOf;
8 | import static org.junit.Assert.assertThat;
9 | import static org.mockito.Mockito.mock;
10 | import static org.mockito.Mockito.when;
11 |
12 | public class NativeAirMapViewBuilderTest {
13 |
14 | @Test public void shouldBuildNativeAirMapView() {
15 | NativeAirMapViewBuilder builder = new NativeAirMapViewBuilder();
16 | AirGoogleMapOptions options = mock(AirGoogleMapOptions.class);
17 | when(options.toBundle()).thenReturn(new Bundle());
18 | assertThat(builder.withOptions(options).build(), instanceOf(NativeGoogleMapFragment.class));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/test/java/com/airbnb/android/airmapview/WebAirMapViewBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.android.airmapview;
2 |
3 | import android.os.Bundle;
4 |
5 | import org.junit.Ignore;
6 | import org.junit.Test;
7 |
8 | import static org.hamcrest.CoreMatchers.instanceOf;
9 | import static org.junit.Assert.assertThat;
10 | import static org.mockito.Mockito.mock;
11 | import static org.mockito.Mockito.when;
12 |
13 | public class WebAirMapViewBuilderTest {
14 |
15 | @Test @Ignore("Can't really test this right now since we can' mock the Bundle")
16 | public void shouldReturnGoogleWebAirMapViewByDefault() {
17 | WebAirMapViewBuilder factory = new WebAirMapViewBuilder();
18 | assertThat(factory.build(), instanceOf(GoogleWebViewMapFragment.class));
19 | }
20 |
21 | @Test public void shouldBuildGoogleWebAirMapViewWithOptions() {
22 | WebAirMapViewBuilder factory = new WebAirMapViewBuilder();
23 | GoogleWebMapType mapType = mock(GoogleWebMapType.class);
24 | when(mapType.toBundle()).thenReturn(new Bundle());
25 | assertThat(factory.withOptions(mapType).build(), instanceOf(GoogleWebViewMapFragment.class));
26 | }
27 |
28 | @Test public void shouldBuildGoogleChinaWebAirMapViewWithOptions() {
29 | WebAirMapViewBuilder factory = new WebAirMapViewBuilder();
30 | GoogleChinaMapType mapType = mock(GoogleChinaMapType.class);
31 | when(mapType.toBundle()).thenReturn(new Bundle());
32 | assertThat(factory.withOptions(mapType).build(),
33 | instanceOf(GoogleChinaWebViewMapFragment.class));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/eric/asdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/aars/amazon-maps-api-2.0.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/AirMapView/533d6818ad4e6119a1aa7360e6868b5d990b9934/sample/aars/amazon-maps-api-2.0.aar
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | applicationId "com.airbnb.airmapview.sample"
8 | minSdkVersion 21
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | multiDexEnabled true
13 | }
14 | lintOptions {
15 | disable 'GradleCompatible'
16 | }
17 |
18 | compileOptions {
19 | sourceCompatibility JavaVersion.VERSION_1_8
20 | targetCompatibility JavaVersion.VERSION_1_8
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation project(':library')
26 | implementation 'androidx.multidex:multidex:2.0.1'
27 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
28 | implementation 'androidx.appcompat:appcompat:1.0.2'
29 | implementation 'com.google.android.gms:play-services-maps:17.0.0'
30 | }
31 |
--------------------------------------------------------------------------------
/sample/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/AirMapView/533d6818ad4e6119a1aa7360e6868b5d990b9934/sample/debug.keystore
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/felipe_lima/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/debug/res/values/google_maps_api.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | AIzaSyDxxG08-MZlZV772EMqzliBRP9b1o_rGvg
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
21 |
22 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
46 |
49 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/airbnb/airmapview/sample/LogsAdapter.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.airmapview.sample;
2 |
3 | import android.graphics.Bitmap;
4 | import androidx.annotation.IntDef;
5 | import androidx.recyclerview.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import java.lang.annotation.Retention;
12 | import java.lang.annotation.RetentionPolicy;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class LogsAdapter extends RecyclerView.Adapter {
17 | @IntDef({VIEW_TYPE_STRING, VIEW_TYPE_BITMAP})
18 | @Retention(RetentionPolicy.SOURCE)
19 | private @interface ViewType { }
20 | private static final int VIEW_TYPE_STRING = 1;
21 | private static final int VIEW_TYPE_BITMAP = 2;
22 |
23 | private final List