3
import android.annotation.SuppressLint;
4
import android.graphics.Color;
5
import android.location.Location;
6
import android.os.Bundle;
7
import android.view.Menu;
8
import android.view.MenuItem;
9
import android.view.animation.DecelerateInterpolator;
10
import android.view.animation.Interpolator;
11
import android.widget.ArrayAdapter;
12
import android.widget.Button;
13
import android.widget.Toast;
15
import java.util.ArrayList;
18
import ai.nextbillion.maps.camera.CameraUpdateFactory;
19
import ai.nextbillion.maps.core.MapView;
20
import ai.nextbillion.maps.core.NextbillionMap;
21
import ai.nextbillion.maps.core.OnMapReadyCallback;
22
import ai.nextbillion.maps.core.Style;
23
import ai.nextbillion.maps.location.LocationComponent;
24
import ai.nextbillion.maps.location.LocationComponentActivationOptions;
25
import ai.nextbillion.maps.location.LocationComponentOptions;
26
import ai.nextbillion.maps.location.engine.LocationEngineRequest;
27
import ai.nextbillion.maps.location.modes.CameraMode;
28
import ai.nextbillion.maps.location.permissions.PermissionsListener;
29
import ai.nextbillion.maps.location.permissions.PermissionsManager;
30
import androidx.annotation.NonNull;
31
import androidx.appcompat.app.AppCompatActivity;
32
import androidx.appcompat.widget.ListPopupWindow;
36
* This activity shows how to customize the LocationComponent's pulsing circle.
38
public class CustomizedLocationPulsingCircleActivity extends AppCompatActivity implements OnMapReadyCallback {
42
// Adjust these variables to customize the example's pulsing circle UI
43
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS = 2300;
44
private static final float SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS = 800;
45
private static final float THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS = 8000;
46
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_RADIUS = 35;
47
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_ALPHA = .55f;
48
private static final Interpolator DEFAULT_LOCATION_CIRCLE_INTERPOLATOR_PULSE_MODE
49
= new DecelerateInterpolator();
50
private static final boolean DEFAULT_LOCATION_CIRCLE_PULSE_FADE_MODE = true;
54
private static int LOCATION_CIRCLE_PULSE_COLOR;
55
private static float LOCATION_CIRCLE_PULSE_DURATION = DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS;
56
private static final String SAVED_STATE_LOCATION = "saved_state_location";
57
private static final String SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR = "saved_state_color";
58
private static final String SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION = "saved_state_duration";
59
private static final String LAYER_BELOW_ID = "waterway-label";
61
private Location lastLocation;
62
private MapView mapView;
63
private Button pulsingCircleDurationButton;
64
private Button pulsingCircleColorButton;
65
private PermissionsManager permissionsManager;
66
private LocationComponent locationComponent;
67
private NextbillionMap nextbillionMap;
68
private float currentPulseDuration;
72
protected void onCreate(Bundle savedInstanceState) {
73
super.onCreate(savedInstanceState);
74
setContentView(R.layout.activity_customized_location_pulsing_circle);
76
LOCATION_CIRCLE_PULSE_COLOR = Color.BLUE;
78
mapView = findViewById(R.id.mapView);
80
if (savedInstanceState != null) {
81
lastLocation = savedInstanceState.getParcelable(SAVED_STATE_LOCATION);
82
LOCATION_CIRCLE_PULSE_COLOR = savedInstanceState.getInt(SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR);
83
LOCATION_CIRCLE_PULSE_DURATION = savedInstanceState.getFloat(SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION);
86
pulsingCircleDurationButton = findViewById(R.id.button_location_circle_duration);
87
pulsingCircleDurationButton.setText(String.format("%sms",
88
String.valueOf(LOCATION_CIRCLE_PULSE_DURATION)));
89
pulsingCircleDurationButton.setOnClickListener(v -> {
90
if (locationComponent == null) {
93
showDurationListDialog();
96
pulsingCircleColorButton = findViewById(R.id.button_location_circle_color);
97
pulsingCircleColorButton.setOnClickListener(v -> {
98
if (locationComponent == null) {
101
showColorListDialog();
104
mapView.onCreate(savedInstanceState);
109
@SuppressLint("MissingPermission")
111
public void onMapReady(@NonNull NextbillionMap nextbillionMap) {
112
this.nextbillionMap = nextbillionMap;
113
nextbillionMap.animateCamera(CameraUpdateFactory.zoomBy(13));
115
nextbillionMap.setStyle(StyleConstants.LIGHT, style -> {
116
locationComponent = nextbillionMap.getLocationComponent();
118
LocationComponentOptions locationComponentOptions =
119
buildLocationComponentOptions(
120
LOCATION_CIRCLE_PULSE_COLOR,
121
LOCATION_CIRCLE_PULSE_DURATION)
125
LocationComponentActivationOptions locationComponentActivationOptions =
126
buildLocationComponentActivationOptions(style,locationComponentOptions);
128
locationComponent.activateLocationComponent(locationComponentActivationOptions);
129
locationComponent.setLocationComponentEnabled(true);
130
locationComponent.setCameraMode(CameraMode.TRACKING);
131
locationComponent.forceLocationUpdate(lastLocation);
135
private LocationComponentOptions.Builder buildLocationComponentOptions(int pulsingCircleColor,
136
float pulsingCircleDuration
138
currentPulseDuration = pulsingCircleDuration;
139
return LocationComponentOptions.builder(this)
140
.layerBelow(LAYER_BELOW_ID)
141
.pulseFadeEnabled(DEFAULT_LOCATION_CIRCLE_PULSE_FADE_MODE)
142
.pulseInterpolator(DEFAULT_LOCATION_CIRCLE_INTERPOLATOR_PULSE_MODE)
143
.pulseColor(pulsingCircleColor)
144
.pulseAlpha(DEFAULT_LOCATION_CIRCLE_PULSE_ALPHA)
145
.pulseSingleDuration(pulsingCircleDuration)
146
.pulseMaxRadius(DEFAULT_LOCATION_CIRCLE_PULSE_RADIUS);
149
@SuppressLint("MissingPermission")
150
private void setNewLocationComponentOptions(float newPulsingDuration,
151
int newPulsingColor) {
152
nextbillionMap.getStyle(style -> locationComponent.applyStyle(
153
buildLocationComponentOptions(
160
private LocationComponentActivationOptions buildLocationComponentActivationOptions(
161
@NonNull Style style,
162
@NonNull LocationComponentOptions locationComponentOptions) {
163
return LocationComponentActivationOptions
164
.builder(this, style)
165
.locationComponentOptions(locationComponentOptions)
166
.useDefaultLocationEngine(true)
167
.locationEngineRequest(new LocationEngineRequest.Builder(750)
168
.setFastestInterval(750)
169
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
175
public boolean onCreateOptionsMenu(Menu menu) {
176
getMenuInflater().inflate(R.menu.menu_pulsing_location_mode, menu);
180
@SuppressLint("MissingPermission")
182
public boolean onOptionsItemSelected(MenuItem item) {
183
if (locationComponent == null) {
184
return super.onOptionsItemSelected(item);
187
int id = item.getItemId();
188
if (id == R.id.action_component_disable) {
189
locationComponent.setLocationComponentEnabled(false);
191
} else if (id == R.id.action_component_enabled) {
192
locationComponent.setLocationComponentEnabled(true);
194
} else if (id == R.id.action_stop_pulsing) {
195
locationComponent.applyStyle(LocationComponentOptions.builder(
196
CustomizedLocationPulsingCircleActivity.this)
200
} else if (id == R.id.action_start_pulsing) {
201
locationComponent.applyStyle(buildLocationComponentOptions(
202
LOCATION_CIRCLE_PULSE_COLOR,
203
LOCATION_CIRCLE_PULSE_DURATION)
208
return super.onOptionsItemSelected(item);
211
private void checkPermissions() {
212
if (PermissionsManager.areLocationPermissionsGranted(this)) {
213
mapView.getMapAsync(this);
215
permissionsManager = new PermissionsManager(new PermissionsListener() {
217
public void onExplanationNeeded(List<String> permissionsToExplain) {
218
Toast.makeText(CustomizedLocationPulsingCircleActivity.this, "You need to accept location permissions.",
219
Toast.LENGTH_SHORT).show();
223
public void onPermissionResult(boolean granted) {
225
mapView.getMapAsync(CustomizedLocationPulsingCircleActivity.this);
231
permissionsManager.requestLocationPermissions(this);
235
private void showDurationListDialog() {
236
List<String> modes = new ArrayList<>();
237
modes.add(String.format("%sms", String.valueOf(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS)));
238
modes.add(String.format("%sms", String.valueOf(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS)));
239
modes.add(String.format("%sms", String.valueOf(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS)));
240
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
241
android.R.layout.simple_list_item_1, modes);
242
ListPopupWindow listPopup = new ListPopupWindow(this);
243
listPopup.setAdapter(profileAdapter);
244
listPopup.setAnchorView(pulsingCircleDurationButton);
245
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
246
String selectedMode = modes.get(position);
247
pulsingCircleDurationButton.setText(selectedMode);
248
if (selectedMode.contentEquals(String.format("%sms",
249
String.valueOf(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
250
LOCATION_CIRCLE_PULSE_DURATION = DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS;
251
setNewLocationComponentOptions(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
252
} else if (selectedMode.contentEquals(String.format("%sms",
253
String.valueOf(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
254
LOCATION_CIRCLE_PULSE_DURATION = SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS;
255
setNewLocationComponentOptions(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
256
} else if (selectedMode.contentEquals(String.format("%sms",
257
String.valueOf(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
258
LOCATION_CIRCLE_PULSE_DURATION = THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS;
259
setNewLocationComponentOptions(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
266
private void showColorListDialog() {
267
List<String> trackingTypes = new ArrayList<>();
268
trackingTypes.add("Blue");
269
trackingTypes.add("Red");
270
trackingTypes.add("Green");
271
trackingTypes.add("Gray");
272
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
273
android.R.layout.simple_list_item_1, trackingTypes);
274
ListPopupWindow listPopup = new ListPopupWindow(this);
275
listPopup.setAdapter(profileAdapter);
276
listPopup.setAnchorView(pulsingCircleColorButton);
277
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
278
String selectedTrackingType = trackingTypes.get(position);
279
pulsingCircleColorButton.setText(selectedTrackingType);
280
if (selectedTrackingType.contentEquals("Blue")) {
281
LOCATION_CIRCLE_PULSE_COLOR = Color.BLUE;
282
setNewLocationComponentOptions(currentPulseDuration, Color.BLUE);
283
} else if (selectedTrackingType.contentEquals("Red")) {
284
LOCATION_CIRCLE_PULSE_COLOR = Color.RED;
285
setNewLocationComponentOptions(currentPulseDuration, Color.RED);
286
} else if (selectedTrackingType.contentEquals("Green")) {
287
LOCATION_CIRCLE_PULSE_COLOR = Color.GREEN;
288
setNewLocationComponentOptions(currentPulseDuration, Color.GREEN);
289
} else if (selectedTrackingType.contentEquals("Gray")) {
290
LOCATION_CIRCLE_PULSE_COLOR = Color.parseColor("#4a4a4a");
291
setNewLocationComponentOptions(currentPulseDuration, Color.parseColor("#4a4a4a"));
300
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
301
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
302
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
306
protected void onStart() {
312
protected void onResume() {
318
protected void onPause() {
324
protected void onStop() {
329
@SuppressLint("MissingPermission")
331
protected void onSaveInstanceState(Bundle outState) {
332
super.onSaveInstanceState(outState);
333
mapView.onSaveInstanceState(outState);
334
if (locationComponent != null) {
335
outState.putParcelable(SAVED_STATE_LOCATION, locationComponent.getLastKnownLocation());
336
outState.putInt(SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR, LOCATION_CIRCLE_PULSE_COLOR);
337
outState.putFloat(SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION, LOCATION_CIRCLE_PULSE_DURATION);
342
protected void onDestroy() {
348
public void onLowMemory() {
350
mapView.onLowMemory();