Location Tracking Modes

This example shows how to Switch Location Tracking Modes

  • Location Permissions Handling

  • Switch Location component RenderMode

  • Switch Location Tracking Camera Mode

  • Enable/Disable Location Component

  • Tracking Current Location Automatically when MapReady

    • locationComponent.setLocationComponentEnabled(true);
    • locationComponent.setRenderMode(RenderMode.COMPASS);
    • locationComponent.setCameraMode(CameraMode.TRACKING);
Location Tracking Modes

For all code examples, refer to Android Maps SDK Code Examples

activity_location_modes.xml view source

1
<?xml version="1.0" encoding="utf-8"?>
2
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
xmlns:app="http://schemas.android.com/apk/res-auto"
4
xmlns:tools="http://schemas.android.com/tools"
5
android:layout_width="match_parent"
6
android:layout_height="match_parent">
7
8
<ai.nextbillion.maps.core.MapView
9
android:id="@+id/mapView"
10
android:layout_width="match_parent"
11
android:layout_height="match_parent"
12
android:layout_marginBottom="0dp"
13
app:layout_constraintBottom_toTopOf="parent"
14
app:layout_constraintLeft_toLeftOf="parent"
15
app:layout_constraintRight_toRightOf="parent"
16
app:layout_constraintTop_toTopOf="parent"
17
app:nbmap_uiAttribution="false" />
18
19
<View
20
android:id="@+id/view_protected_gesture_area"
21
android:layout_width="0dp"
22
android:layout_height="0dp"
23
android:alpha="0.5"
24
android:background="@android:color/holo_red_light"
25
app:layout_constraintStart_toStartOf="@id/mapView"
26
app:layout_constraintTop_toTopOf="@id/mapView" />
27
28
<LinearLayout
29
android:id="@+id/linearLayout"
30
style="?android:attr/buttonBarStyle"
31
android:layout_width="0dp"
32
android:layout_height="wrap_content"
33
android:background="@color/palette_mint_100"
34
android:orientation="horizontal"
35
android:weightSum="4"
36
app:layout_constraintBottom_toBottomOf="parent"
37
app:layout_constraintLeft_toLeftOf="parent"
38
app:layout_constraintRight_toRightOf="parent"
39
tools:layout_constraintBottom_creator="1"
40
tools:layout_constraintLeft_creator="1"
41
tools:layout_constraintRight_creator="1">
42
43
<TextView
44
android:id="@+id/tv_mode"
45
android:layout_width="0dp"
46
android:layout_height="match_parent"
47
android:layout_weight=".75"
48
android:gravity="center"
49
android:text="Mode:"
50
android:textColor="@color/white"
51
android:textSize="18sp"
52
android:textStyle="bold" />
53
54
<Button
55
android:id="@+id/button_location_mode"
56
style="?android:attr/buttonBarButtonStyle"
57
android:layout_width="0dp"
58
android:layout_height="wrap_content"
59
android:layout_weight="1.25"
60
android:gravity="center"
61
android:text="Normal"
62
android:textColor="@android:color/white" />
63
64
<TextView
65
android:id="@+id/tv_tracking"
66
android:layout_width="0dp"
67
android:layout_height="match_parent"
68
android:layout_weight=".85"
69
android:gravity="center"
70
android:text="Tracking:"
71
android:textColor="@color/white"
72
android:textSize="18sp"
73
android:textStyle="bold" />
74
75
<Button
76
android:id="@+id/button_location_tracking"
77
style="?android:attr/buttonBarButtonStyle"
78
android:layout_width="0dp"
79
android:layout_height="wrap_content"
80
android:layout_weight="1.15"
81
android:gravity="center"
82
android:text="None"
83
android:textColor="@android:color/white" />
84
85
</LinearLayout>
86
87
</androidx.constraintlayout.widget.ConstraintLayout>

LocationModesActivity view source

1
package ai.nextbillion;
2
3
import android.annotation.SuppressLint;
4
import android.graphics.RectF;
5
import android.location.Location;
6
import android.os.Bundle;
7
import android.view.Menu;
8
import android.view.MenuItem;
9
import android.view.View;
10
import android.widget.ArrayAdapter;
11
import android.widget.Button;
12
import android.widget.Toast;
13
14
import java.util.ArrayList;
15
import java.util.List;
16
17
import ai.nextbillion.maps.camera.CameraUpdateFactory;
18
import ai.nextbillion.maps.core.MapView;
19
import ai.nextbillion.maps.core.NextbillionMap;
20
import ai.nextbillion.maps.core.OnMapReadyCallback;
21
import ai.nextbillion.maps.location.LocationComponent;
22
import ai.nextbillion.maps.location.LocationComponentActivationOptions;
23
import ai.nextbillion.maps.location.LocationComponentOptions;
24
import ai.nextbillion.maps.location.OnCameraTrackingChangedListener;
25
import ai.nextbillion.maps.location.OnLocationCameraTransitionListener;
26
import ai.nextbillion.maps.location.OnLocationClickListener;
27
import ai.nextbillion.maps.location.engine.LocationEngineRequest;
28
import ai.nextbillion.maps.location.modes.CameraMode;
29
import ai.nextbillion.maps.location.modes.RenderMode;
30
import ai.nextbillion.maps.location.permissions.PermissionsListener;
31
import ai.nextbillion.maps.location.permissions.PermissionsManager;
32
import androidx.annotation.NonNull;
33
import androidx.appcompat.app.AppCompatActivity;
34
import androidx.appcompat.widget.ListPopupWindow;
35
36
public class LocationModesActivity extends AppCompatActivity implements OnMapReadyCallback,
37
OnLocationClickListener, OnCameraTrackingChangedListener {
38
39
private MapView mapView;
40
private Button locationModeBtn;
41
private Button locationTrackingBtn;
42
private View protectedGestureArea;
43
44
private PermissionsManager permissionsManager;
45
46
private LocationComponent locationComponent;
47
private NextbillionMap nextbillionMap;
48
private boolean defaultStyle = false;
49
50
private static final String SAVED_STATE_CAMERA = "saved_state_camera";
51
private static final String SAVED_STATE_RENDER = "saved_state_render";
52
private static final String SAVED_STATE_LOCATION = "saved_state_location";
53
54
@CameraMode.Mode
55
private int cameraMode = CameraMode.TRACKING;
56
57
@RenderMode.Mode
58
private int renderMode = RenderMode.NORMAL;
59
60
private Location lastLocation;
61
62
@Override
63
protected void onCreate(Bundle savedInstanceState) {
64
super.onCreate(savedInstanceState);
65
setContentView(R.layout.activity_location_modes);
66
67
mapView = findViewById(R.id.mapView);
68
protectedGestureArea = findViewById(R.id.view_protected_gesture_area);
69
70
locationModeBtn = findViewById(R.id.button_location_mode);
71
locationModeBtn.setOnClickListener(v -> {
72
if (locationComponent == null) {
73
return;
74
}
75
showModeListDialog();
76
});
77
78
locationTrackingBtn = findViewById(R.id.button_location_tracking);
79
locationTrackingBtn.setOnClickListener(v -> {
80
if (locationComponent == null) {
81
return;
82
}
83
showTrackingListDialog();
84
});
85
86
87
if (savedInstanceState != null) {
88
cameraMode = savedInstanceState.getInt(SAVED_STATE_CAMERA);
89
renderMode = savedInstanceState.getInt(SAVED_STATE_RENDER);
90
lastLocation = savedInstanceState.getParcelable(SAVED_STATE_LOCATION);
91
}
92
93
mapView.onCreate(savedInstanceState);
94
95
if (PermissionsManager.areLocationPermissionsGranted(this)) {
96
mapView.getMapAsync(this);
97
} else {
98
permissionsManager = new PermissionsManager(new PermissionsListener() {
99
@Override
100
public void onExplanationNeeded(List<String> permissionsToExplain) {
101
Toast.makeText(LocationModesActivity.this, "You need to accept location permissions.",
102
Toast.LENGTH_SHORT).show();
103
}
104
105
@Override
106
public void onPermissionResult(boolean granted) {
107
if (granted) {
108
mapView.getMapAsync(LocationModesActivity.this);
109
} else {
110
finish();
111
}
112
}
113
});
114
permissionsManager.requestLocationPermissions(this);
115
}
116
}
117
118
@Override
119
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
120
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
121
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
122
}
123
124
@SuppressLint("MissingPermission")
125
@Override
126
public void onMapReady(@NonNull NextbillionMap nextbillionMap) {
127
this.nextbillionMap = nextbillionMap;
128
nextbillionMap.animateCamera(CameraUpdateFactory.zoomBy(13));
129
nextbillionMap.setStyle(StyleConstants.NBMAP_STREETS, style -> {
130
locationComponent = nextbillionMap.getLocationComponent();
131
locationComponent.activateLocationComponent(
132
LocationComponentActivationOptions
133
.builder(this, style)
134
.useSpecializedLocationLayer(true)
135
.useDefaultLocationEngine(true)
136
.locationEngineRequest(new LocationEngineRequest.Builder(750)
137
.setFastestInterval(750)
138
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
139
.build())
140
.build());
141
142
locationComponent.setLocationComponentEnabled(true);
143
locationComponent.addOnLocationClickListener(this);
144
locationComponent.addOnCameraTrackingChangedListener(this);
145
locationComponent.setCameraMode(cameraMode);
146
setRendererMode(renderMode);
147
locationComponent.forceLocationUpdate(lastLocation);
148
});
149
}
150
151
@Override
152
public boolean onCreateOptionsMenu(Menu menu) {
153
getMenuInflater().inflate(R.menu.menu_location_mode, menu);
154
return true;
155
}
156
157
@SuppressLint("MissingPermission")
158
@Override
159
public boolean onOptionsItemSelected(MenuItem item) {
160
if (locationComponent == null) {
161
return super.onOptionsItemSelected(item);
162
}
163
164
int id = item.getItemId();
165
if (id == R.id.action_component_disable) {
166
locationComponent.setLocationComponentEnabled(false);
167
return true;
168
} else if (id == R.id.action_component_enabled) {
169
locationComponent.setLocationComponentEnabled(true);
170
return true;
171
} else if (id == R.id.action_gestures_management_disabled) {
172
disableGesturesManagement();
173
return true;
174
} else if (id == R.id.action_gestures_management_enabled) {
175
enableGesturesManagement();
176
return true;
177
} else if (id == R.id.action_component_throttling_enabled) {
178
locationComponent.setMaxAnimationFps(5);
179
} else if (id == R.id.action_component_throttling_disabled) {
180
locationComponent.setMaxAnimationFps(Integer.MAX_VALUE);
181
} else if (id == R.id.action_component_animate_while_tracking) {
182
locationComponent.zoomWhileTracking(17, 750, new NextbillionMap.CancelableCallback() {
183
@Override
184
public void onCancel() {
185
// No impl
186
}
187
188
@Override
189
public void onFinish() {
190
locationComponent.tiltWhileTracking(60);
191
}
192
});
193
if (locationComponent.getCameraMode() == CameraMode.NONE) {
194
195
Toast.makeText(this, "Not possible to animate - not tracking", Toast.LENGTH_SHORT).show();
196
}
197
}
198
199
return super.onOptionsItemSelected(item);
200
}
201
202
private void disableGesturesManagement() {
203
if (locationComponent == null) {
204
return;
205
}
206
207
protectedGestureArea.getLayoutParams().height = 0;
208
protectedGestureArea.getLayoutParams().width = 0;
209
210
LocationComponentOptions options = locationComponent
211
.getLocationComponentOptions()
212
.toBuilder()
213
.trackingGesturesManagement(false)
214
.build();
215
locationComponent.applyStyle(options);
216
}
217
218
private void enableGesturesManagement() {
219
if (locationComponent == null) {
220
return;
221
}
222
223
RectF rectF = new RectF(0f, 0f, mapView.getWidth() / 2f, mapView.getHeight() / 2f);
224
protectedGestureArea.getLayoutParams().height = (int) rectF.bottom;
225
protectedGestureArea.getLayoutParams().width = (int) rectF.right;
226
227
LocationComponentOptions options = locationComponent
228
.getLocationComponentOptions()
229
.toBuilder()
230
.trackingGesturesManagement(true)
231
.trackingMultiFingerProtectedMoveArea(rectF)
232
.trackingMultiFingerMoveThreshold(500)
233
.build();
234
locationComponent.applyStyle(options);
235
}
236
237
@Override
238
protected void onStart() {
239
super.onStart();
240
mapView.onStart();
241
}
242
243
@Override
244
protected void onResume() {
245
super.onResume();
246
mapView.onResume();
247
}
248
249
@Override
250
protected void onPause() {
251
super.onPause();
252
mapView.onPause();
253
}
254
255
@Override
256
protected void onStop() {
257
super.onStop();
258
mapView.onStop();
259
}
260
261
@SuppressLint("MissingPermission")
262
@Override
263
protected void onSaveInstanceState(Bundle outState) {
264
super.onSaveInstanceState(outState);
265
mapView.onSaveInstanceState(outState);
266
outState.putInt(SAVED_STATE_CAMERA, cameraMode);
267
outState.putInt(SAVED_STATE_RENDER, renderMode);
268
if (locationComponent != null) {
269
outState.putParcelable(SAVED_STATE_LOCATION, locationComponent.getLastKnownLocation());
270
}
271
}
272
273
@Override
274
protected void onDestroy() {
275
super.onDestroy();
276
mapView.onDestroy();
277
}
278
279
@Override
280
public void onLowMemory() {
281
super.onLowMemory();
282
mapView.onLowMemory();
283
}
284
285
@Override
286
public void onLocationComponentClick() {
287
Toast.makeText(this, "OnLocationComponentClick", Toast.LENGTH_LONG).show();
288
}
289
290
private void showModeListDialog() {
291
List<String> modes = new ArrayList<>();
292
modes.add("Normal");
293
modes.add("Compass");
294
modes.add("GPS");
295
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
296
android.R.layout.simple_list_item_1, modes);
297
ListPopupWindow listPopup = new ListPopupWindow(this);
298
listPopup.setAdapter(profileAdapter);
299
listPopup.setAnchorView(locationModeBtn);
300
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
301
String selectedMode = modes.get(position);
302
locationModeBtn.setText(selectedMode);
303
if (selectedMode.contentEquals("Normal")) {
304
setRendererMode(RenderMode.NORMAL);
305
} else if (selectedMode.contentEquals("Compass")) {
306
setRendererMode(RenderMode.COMPASS);
307
} else if (selectedMode.contentEquals("GPS")) {
308
setRendererMode(RenderMode.GPS);
309
}
310
listPopup.dismiss();
311
});
312
listPopup.show();
313
}
314
315
private void setRendererMode(@RenderMode.Mode int mode) {
316
renderMode = mode;
317
locationComponent.setRenderMode(mode);
318
if (mode == RenderMode.NORMAL) {
319
locationModeBtn.setText("Normal");
320
} else if (mode == RenderMode.COMPASS) {
321
locationModeBtn.setText("Compass");
322
} else if (mode == RenderMode.GPS) {
323
locationModeBtn.setText("Gps");
324
}
325
}
326
327
private void showTrackingListDialog() {
328
List<String> trackingTypes = new ArrayList<>();
329
trackingTypes.add("None");
330
trackingTypes.add("None Compass");
331
trackingTypes.add("None GPS");
332
trackingTypes.add("Tracking");
333
trackingTypes.add("Tracking Compass");
334
trackingTypes.add("Tracking GPS");
335
trackingTypes.add("Tracking GPS North");
336
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
337
android.R.layout.simple_list_item_1, trackingTypes);
338
ListPopupWindow listPopup = new ListPopupWindow(this);
339
listPopup.setAdapter(profileAdapter);
340
listPopup.setAnchorView(locationTrackingBtn);
341
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
342
String selectedTrackingType = trackingTypes.get(position);
343
locationTrackingBtn.setText(selectedTrackingType);
344
if (selectedTrackingType.contentEquals("None")) {
345
setCameraTrackingMode(CameraMode.NONE);
346
} else if (selectedTrackingType.contentEquals("None Compass")) {
347
setCameraTrackingMode(CameraMode.NONE_COMPASS);
348
} else if (selectedTrackingType.contentEquals("None GPS")) {
349
setCameraTrackingMode(CameraMode.NONE_GPS);
350
} else if (selectedTrackingType.contentEquals("Tracking")) {
351
setCameraTrackingMode(CameraMode.TRACKING);
352
} else if (selectedTrackingType.contentEquals("Tracking Compass")) {
353
setCameraTrackingMode(CameraMode.TRACKING_COMPASS);
354
} else if (selectedTrackingType.contentEquals("Tracking GPS")) {
355
setCameraTrackingMode(CameraMode.TRACKING_GPS);
356
} else if (selectedTrackingType.contentEquals("Tracking GPS North")) {
357
setCameraTrackingMode(CameraMode.TRACKING_GPS_NORTH);
358
}
359
listPopup.dismiss();
360
});
361
listPopup.show();
362
}
363
364
private void setCameraTrackingMode(@CameraMode.Mode int mode) {
365
locationComponent.setCameraMode(mode, 1200, 16.0, null, 45.0,
366
new OnLocationCameraTransitionListener() {
367
@Override
368
public void onLocationCameraTransitionFinished(@CameraMode.Mode int cameraMode) {
369
Toast.makeText(LocationModesActivity.this, "Transition finished", Toast.LENGTH_SHORT).show();
370
}
371
372
@Override
373
public void onLocationCameraTransitionCanceled(@CameraMode.Mode int cameraMode) {
374
Toast.makeText(LocationModesActivity.this, "Transition canceled", Toast.LENGTH_SHORT).show();
375
}
376
});
377
}
378
379
@Override
380
public void onCameraTrackingDismissed() {
381
locationTrackingBtn.setText("None");
382
}
383
384
@Override
385
public void onCameraTrackingChanged(int currentMode) {
386
this.cameraMode = currentMode;
387
if (currentMode == CameraMode.NONE) {
388
locationTrackingBtn.setText("None");
389
} else if (currentMode == CameraMode.NONE_COMPASS) {
390
locationTrackingBtn.setText("None Compass");
391
} else if (currentMode == CameraMode.NONE_GPS) {
392
locationTrackingBtn.setText("None GPS");
393
} else if (currentMode == CameraMode.TRACKING) {
394
locationTrackingBtn.setText("Tracking");
395
} else if (currentMode == CameraMode.TRACKING_COMPASS) {
396
locationTrackingBtn.setText("Tracking Compass");
397
} else if (currentMode == CameraMode.TRACKING_GPS) {
398
locationTrackingBtn.setText("Tracking GPS");
399
} else if (currentMode == CameraMode.TRACKING_GPS_NORTH) {
400
locationTrackingBtn.setText("Tracking GPS North");
401
}
402
}
403
}

Summary: The given code is an Android activity that demonstrates various location modes and features using the NextbillionMap library. It initializes a map view, handles location permissions, tracks the current location, switches between location component render modes and tracking camera modes and enables or disables the location component.

initMapView:

  • The map view is initialized in the onCreate method by finding the view with the ID R.id.mapView. This method sets up the map view and initializes other UI elements and variables.

Location Permissions Handling:

  • The code checks if location permissions are granted using PermissionsManager.areLocationPermissionsGranted(this). If permissions are granted, the map is asynchronously loaded using mapView.getMapAsync(this). If permissions are not granted, a PermissionsManager is created and used to request location permissions. The result of the permission request is handled in the onPermissionResult method.

Tracking Current Location Automatically when MapReady:

  • When the map is ready (onMapReady), the NextbillionMap instance is obtained. The map's style is set, and the location component is activated with various options, including a specialized location layer, default location engine, and location update interval. The location component is enabled and configured with listeners for location clicks and camera tracking changes. The last known location is also updated.

Switch Location component RenderMode:

  • The render mode can be switched using the showModeListDialog method, which displays a dialog with options for Normal, Compass, and GPS render modes. The selected mode is applied to the location component using setRendererMode().

Switch Location Tracking CameraMode:

  • The camera tracking mode can be switched using the showTrackingListDialog method, which displays a dialog with options for different camera tracking modes. The selected mode is applied to the location component using setCameraTrackingMode().

Enable/Disable Location Component:

  • The location component can be enabled or disabled by clicking on the corresponding menu items in the options menu. The status of the location component is updated accordingly using setLocationComponentEnabled.

The code also includes various lifecycle methods for managing the map view, saving and restoring the state of the activity, and handling user interactions with the location component, such as clicking and dismissing camera tracking.

Overall, the code demonstrates how to use the NextbillionMap library to implement location-based features in an Android application, including handling permissions, tracking the user's location, and customizing the render and camera tracking modes of the location component.

© 2024 NextBillion.ai all rights reserved.