> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/android/java/llms.txt)

# Isochrone time slider

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/android/android/assets/medias/java-example-isochrone-seekbar-activity-219b3f60d32ea1a9bbe0d0f24c49ddd0.mp4).

> **Note**
> 
> This example is a part of the [Mapbox Android Demo app](https://github.com/mapbox/mapbox-android-demo). You can find the values for all referenced resources in the [`res`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res) directory. For example, see [`res/values/activity_strings.xml`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/values/activity_strings.xml) for `R.string.*` references used in this example. The dependencies can be found [here.](https://github.com/mapbox/mapbox-navigation-android-examples/blob/main/app/build.gradle) The examples use View binding. [See setup documention if necessary.](https://developer.android.com/topic/libraries/view-binding)

> **Note: Isochrone API pricing**
> 
> Products used within the Mapbox Java SDK, including the Isochrone API, are individually billed. For pricing information, see the documentation for the [Isochrone API](https://docs.mapbox.com/api/navigation/isochrone/#isochrone-api-pricing).

Title: `activity_javaservices_isochrone_with_seekbar`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/layout/activity_javaservices_isochrone_with_seekbar.xml)

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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.javaservices.IsochroneSeekbarActivity">

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/guideline9"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        mapbox:mapbox_cameraTargetLat="48.20355511"
        mapbox:mapbox_cameraTargetLng="16.374893"
        mapbox:mapbox_cameraZoom="8" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.7" />

    <TextView
        android:id="@+id/want_to_live_within_textview"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:gravity="center"

        android:text="@string/live_within_textview"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mapView" />

    <TextView
        android:id="@+id/minutes_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/minutes_textview"
        android:textSize="20sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="@+id/want_to_live_within_textview"
        app:layout_constraintStart_toStartOf="@+id/want_to_live_within_textview"
        app:layout_constraintTop_toBottomOf="@+id/want_to_live_within_textview"
        tools:text="10min" />

    <SeekBar
        android:id="@+id/isochrone_minute_seekbar_slider"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="@+id/want_to_live_within_textview"
        app:layout_constraintStart_toStartOf="@+id/want_to_live_within_textview"
        app:layout_constraintTop_toBottomOf="@+id/minutes_textview" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

**Java**

Title: `IsochroneSeekbarActivity.java`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/IsochroneSeekbarActivity.java)

```java
package com.mapbox.mapboxandroiddemo.examples.javaservices;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

import com.mapbox.api.isochrone.IsochroneCriteria;
import com.mapbox.api.isochrone.MapboxIsochrone;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
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.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import com.mapbox.turf.TurfMeta;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;

import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.geometryType;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;

/**
 * Use an Android system seekbar to set the minute range and use the Mapbox Isochrone API
 * (https://www.mapbox.com/api-documentation/#isochrone) to see how far one can
 * travel in a car.
 */
public class IsochroneSeekbarActivity extends AppCompatActivity {

  private static final String RESPONSE_FILL_LAYER_GEOJSON_SOURCE_ID = "RESPONSE_FILL_LAYER_GEOJSON_SOURCE_ID";
  private static final String ISOCHRONE_FILL_LAYER = "ISOCHRONE_FILL_LAYER";
  private static final String MAP_CENTER_DOWNTOWN_VIENNA_SOURCE_ID = "MAP_CENTER_DOWNTOWN_VIENNA_SOURCE_ID";
  private static final String MAP_CENTER_DOWNTOWN_VIENNA_ICON_ID = "MAP_CENTER_DOWNTOWN_VIENNA_ICON_ID";
  private static final String MAP_CENTER_DOWNTOWN_VIENNA_LAYER_ID = "MAP_CENTER_DOWNTOWN_VIENNA_LAYER_ID";
  private static final Point DOWNTOWN_VIENNA = Point.fromLngLat(16.374893, 48.20355511);
  private MapView mapView;
  private MapboxMap mapboxMap;

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

    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);
    mapView.getMapAsync(new OnMapReadyCallback() {
      @Override
      public void onMapReady(@NonNull MapboxMap mapboxMap) {
        mapboxMap.setStyle(new Style.Builder().fromUri(Style.LIGHT)
            .withImage(MAP_CENTER_DOWNTOWN_VIENNA_ICON_ID, BitmapUtils.getBitmapFromDrawable(
                getResources().getDrawable(R.drawable.red_marker)))
            .withSource(new GeoJsonSource(MAP_CENTER_DOWNTOWN_VIENNA_SOURCE_ID,
                Feature.fromGeometry(DOWNTOWN_VIENNA)))
            .withSource(new GeoJsonSource(RESPONSE_FILL_LAYER_GEOJSON_SOURCE_ID))
            .withLayer(new SymbolLayer(MAP_CENTER_DOWNTOWN_VIENNA_LAYER_ID,
                MAP_CENTER_DOWNTOWN_VIENNA_SOURCE_ID).withProperties(
                iconImage(MAP_CENTER_DOWNTOWN_VIENNA_ICON_ID),
                iconIgnorePlacement(true),
                iconAllowOverlap(true),
                iconOffset(new Float[]{0f, -4f})
            )), new Style.OnStyleLoaded() {
              @Override
              public void onStyleLoaded(@NonNull Style style) {

                IsochroneSeekbarActivity.this.mapboxMap = mapboxMap;

                // Initialize the Seekbar slider
                final SeekBar liveWithinMinutesSeekbar =
                    findViewById(R.id.isochrone_minute_seekbar_slider);
                liveWithinMinutesSeekbar.setMax(60);
                liveWithinMinutesSeekbar.incrementProgressBy(5);
                liveWithinMinutesSeekbar.setProgress(30);

                // Initialize the TextView, which will update as the slider is moved
                // back and forth.
                final TextView liveWithinMinTextView = findViewById(R.id.minutes_textview);
                liveWithinMinTextView.setText(String.format(getString(R.string.minutes_textview),
                    liveWithinMinutesSeekbar.getProgress()));

                makeIsochroneApiCall(new LatLng(DOWNTOWN_VIENNA.latitude(), DOWNTOWN_VIENNA.longitude()),
                    liveWithinMinutesSeekbar.getProgress());

                liveWithinMinutesSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                  @Override
                  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    progress = progress / 5;
                    progress = progress * 5;
                    liveWithinMinTextView.setText(String.format(getString(R.string.minutes_textview), progress));
                  }

                  @Override
                  public void onStartTrackingTouch(SeekBar seekBar) {
                    // Not needed in this example.
                  }

                  @Override
                  public void onStopTrackingTouch(SeekBar seekBar) {
                    makeIsochroneApiCall(new LatLng(DOWNTOWN_VIENNA.latitude(),
                        DOWNTOWN_VIENNA.longitude()), seekBar.getProgress());
                  }
                });

                initFillLayer(style);
              }
            });
      }
    });
  }

  /**
   * Make a request to the Mapbox Isochrone API
   *
   * @param mapClickPoint The center point of the isochrone. It is part of the API request.
   * @param contourMin    The number of travel minutes to calculate Isochrone information for.
   */
  private void makeIsochroneApiCall(@NonNull LatLng mapClickPoint, @NonNull Integer contourMin) {

    MapboxIsochrone mapboxIsochroneRequest = MapboxIsochrone.builder()
        .accessToken(getString(R.string.access_token))
        .profile(IsochroneCriteria.PROFILE_DRIVING)
        .addContoursMinutes(contourMin)
        .polygons(true)
        .addContoursColors("5a42f4")
        .denoise(.4f)
        .generalize(10f)
        .coordinates(Point.fromLngLat(mapClickPoint.getLongitude(), mapClickPoint.getLatitude()))
        .build();

    mapboxIsochroneRequest.enqueueCall(new Callback<FeatureCollection>() {
      @Override
      public void onResponse(Call<FeatureCollection> call, Response<FeatureCollection> response) {
        // Redraw Isochrone information based on response body
        if (response.body() != null) {

          FeatureCollection featureCollection = response.body();

          mapboxMap.getStyle(new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {
              GeoJsonSource fillLayerSource = style.getSourceAs(
                  RESPONSE_FILL_LAYER_GEOJSON_SOURCE_ID);
              if (fillLayerSource != null && featureCollection.features() != null
                && featureCollection.features().size() > 0) {
                fillLayerSource.setGeoJson(featureCollection);

                // Move the camera from the map in case it's too zoomed in.
                // This is here so that the isochrone information can be seen if the camera
                // is too close to the map.
                LatLngBounds latLngBounds = new LatLngBounds.Builder()
                  .includes(createLatLngsForCameraBounds(featureCollection.features()))
                  .build();

                mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(
                  latLngBounds, 50), 2000);
              }
            }
          });
        }
      }

      @Override
      public void onFailure(Call<FeatureCollection> call, Throwable throwable) {
        Timber.d("Request failed: %s", throwable.getMessage());
      }
    });
  }

  /**
   * Use the {@link TurfMeta#coordAll(Feature, boolean)} method and the Isochrone API
   * response to build a list of {@link LatLng}. The list will be used to create a
   * camera bounds so that the camera can adjust to the size of the visible Isochrone
   * API data.
   *
   * @param featureList list from the body of the Isochrone API response.
   * @return a list of {@link LatLng}.
   */
  private List<LatLng> createLatLngsForCameraBounds(List<Feature> featureList) {
    List<LatLng> latLngList = new ArrayList<>(featureList.size());
    for (Feature singleFeature : featureList) {
      for (Point singlePoint : TurfMeta.coordAll(singleFeature, false)) {
        latLngList.add(new LatLng((singlePoint.latitude()), singlePoint.longitude()));
      }
    }
    return latLngList;
  }

  /**
   * Add a FillLayer so that that polygon returned by the Isochrone API response can be displayed
   */
  private void initFillLayer(@NonNull Style style) {
    // Create and style a FillLayer based on information in the Isochrone API response
    FillLayer isochroneFillLayer = new FillLayer(ISOCHRONE_FILL_LAYER,
        RESPONSE_FILL_LAYER_GEOJSON_SOURCE_ID);
    isochroneFillLayer.setProperties(
        fillColor(get("color")),
        fillOpacity(.7f)); // You could also pass in get("opacity")) instead of a hardcoded value
    isochroneFillLayer.setFilter(eq(geometryType(), literal("Polygon")));
    style.addLayerAbove(isochroneFillLayer, "land");
  }

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

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

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

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

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

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

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