Skip to main content

A simple offline map

A newer version of the Maps SDK is available
This page uses v9.7.1 of the Mapbox Maps SDK. A newer version of the SDK is available. Learn about the latest version, v11.3.0, in the Maps SDK documentation.
Note

This example is a part of the Mapbox Android Demo app. You can find the values for all referenced resources in the res directory. For example, see res/values/activity_strings.xml for R.string.* references used in this example.

activity_offline_simple
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".examples.offline.SimpleOfflineMapActivity">

<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="37.73359"
mapbox:mapbox_cameraTargetLng="-119.58410"
mapbox:mapbox_cameraZoom="10"
mapbox:mapbox_cameraZoomMin="10"/>

<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="25dp"
android:paddingLeft="25dp"
android:layout_gravity="center"
android:paddingRight="25dp"
android:visibility="gone"/>

</FrameLayout>
SimpleOfflineMapActivity.java
package com.mapbox.mapboxandroiddemo.examples.offline;

import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.offline.OfflineRegion;
import com.mapbox.mapboxsdk.offline.OfflineRegionError;
import com.mapbox.mapboxsdk.offline.OfflineRegionStatus;
import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition;

import org.json.JSONObject;

import timber.log.Timber;

/**
* Download and view an offline map using the Mapbox Android SDK.
*/
public class SimpleOfflineMapActivity extends AppCompatActivity {

private boolean isEndNotified;
private ProgressBar progressBar;
private MapView mapView;
private OfflineManager offlineManager;

// JSON encoding/decoding
public static final String JSON_CHARSET = "UTF-8";
public static final String JSON_FIELD_REGION_NAME = "FIELD_REGION_NAME";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Mapbox access token is configured here. This needs to be called either in your application
// object or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token));

// This contains the MapView in XML and needs to be called after the access token is configured.
setContentView(R.layout.activity_offline_simple);

mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {

mapboxMap.setStyle(Style.OUTDOORS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {

// Set up the OfflineManager
offlineManager = OfflineManager.getInstance(SimpleOfflineMapActivity.this);

// Create a bounding box for the offline region
LatLngBounds latLngBounds = new LatLngBounds.Builder()
.include(new LatLng(37.7897, -119.5073)) // Northeast
.include(new LatLng(37.6744, -119.6815)) // Southwest
.build();

// Define the offline region
OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
style.getUri(),
latLngBounds,
10,
20,
SimpleOfflineMapActivity.this.getResources().getDisplayMetrics().density);

// Set the metadata
byte[] metadata;
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put(JSON_FIELD_REGION_NAME, "Yosemite National Park");
String json = jsonObject.toString();
metadata = json.getBytes(JSON_CHARSET);
} catch (Exception exception) {
Timber.e("Failed to encode metadata: %s", exception.getMessage());
metadata = null;
}

// Create the region asynchronously
if (metadata != null) {
offlineManager.createOfflineRegion(
definition,
metadata,
new OfflineManager.CreateOfflineRegionCallback() {
@Override
public void onCreate(OfflineRegion offlineRegion) {
offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);

// Display the download progress bar
progressBar = findViewById(R.id.progress_bar);
startProgress();

// Monitor the download progress using setObserver
offlineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
@Override
public void onStatusChanged(OfflineRegionStatus status) {

// Calculate the download percentage and update the progress bar
double percentage = status.getRequiredResourceCount() >= 0
? (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :
0.0;

if (status.isComplete()) {
// Download complete
endProgress(getString(R.string.simple_offline_end_progress_success));
} else if (status.isRequiredResourceCountPrecise()) {
// Switch to determinate state
setPercentage((int) Math.round(percentage));
}
}

@Override
public void onError(OfflineRegionError error) {
// If an error occurs, print to logcat
Timber.e("onError reason: %s", error.getReason());
Timber.e("onError message: %s", error.getMessage());
}

@Override
public void mapboxTileCountLimitExceeded(long limit) {
// Notify if offline region exceeds maximum tile count
Timber.e("Mapbox tile count limit exceeded: %s", limit);
}
});
}

@Override
public void onError(String error) {
Timber.e("Error: %s", error);
}
});
}
}
});
}
});
}

@Override
public void onResume() {
super.onResume();
mapView.onResume();
}

@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}

@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}

@Override
public void onPause() {
super.onPause();
mapView.onPause();
if (offlineManager != null) {
offlineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
@Override
public void onList(OfflineRegion[] offlineRegions) {
if (offlineRegions.length > 0) {
// delete the last item in the offlineRegions list which will be yosemite offline map
offlineRegions[(offlineRegions.length - 1)].delete(new OfflineRegion.OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
Toast.makeText(
SimpleOfflineMapActivity.this,
getString(R.string.basic_offline_deleted_toast),
Toast.LENGTH_LONG
).show();
}

@Override
public void onError(String error) {
Timber.e("On delete error: %s", error);
}
});
}
}

@Override
public void onError(String error) {
Timber.e("onListError: %s", error);
}
});
}
}

@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}

@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}

// Progress bar methods
private void startProgress() {

// Start and show the progress bar
isEndNotified = false;
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.VISIBLE);
}

private void setPercentage(final int percentage) {
progressBar.setIndeterminate(false);
progressBar.setProgress(percentage);
}

private void endProgress(final String message) {
// Don't notify more than once
if (isEndNotified) {
return;
}

// Stop and hide the progress bar
isEndNotified = true;
progressBar.setIndeterminate(false);
progressBar.setVisibility(View.GONE);

// Show a toast
Toast.makeText(SimpleOfflineMapActivity.this, message, Toast.LENGTH_LONG).show();
}
}