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);

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
1package ai.nextbillion;
2
3import android.annotation.SuppressLint;
4import android.graphics.RectF;
5import android.location.Location;
6import android.os.Bundle;
7import android.view.Menu;
8import android.view.MenuItem;
9import android.view.View;
10import android.widget.ArrayAdapter;
11import android.widget.Button;
12import android.widget.Toast;
13
14import java.util.ArrayList;
15import java.util.List;
16
17import ai.nextbillion.maps.camera.CameraUpdateFactory;
18import ai.nextbillion.maps.core.MapView;
19import ai.nextbillion.maps.core.NextbillionMap;
20import ai.nextbillion.maps.core.OnMapReadyCallback;
21import ai.nextbillion.maps.location.LocationComponent;
22import ai.nextbillion.maps.location.LocationComponentActivationOptions;
23import ai.nextbillion.maps.location.LocationComponentOptions;
24import ai.nextbillion.maps.location.OnCameraTrackingChangedListener;
25import ai.nextbillion.maps.location.OnLocationCameraTransitionListener;
26import ai.nextbillion.maps.location.OnLocationClickListener;
27import ai.nextbillion.maps.location.engine.LocationEngineRequest;
28import ai.nextbillion.maps.location.modes.CameraMode;
29import ai.nextbillion.maps.location.modes.RenderMode;
30import ai.nextbillion.maps.location.permissions.PermissionsListener;
31import ai.nextbillion.maps.location.permissions.PermissionsManager;
32import androidx.annotation.NonNull;
33import androidx.appcompat.app.AppCompatActivity;
34import androidx.appcompat.widget.ListPopupWindow;
35
36public 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.