Skip to content

SDK Android

Configure gradle and AndroidManifest

    implementation "androidx.recyclerview:recyclerview:1.2.1"
    implementation "androidx.cardview:cardview:1,0,0"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "com.google.android.gms:play-services-location:21.0.1"
    implementation "com.jakewharton:butterknife:10.2.3"
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'com.github.vietmap-company:maps-sdk-android:1.0.0'
    implementation 'com.github.vietmap-company:maps-sdk-navigation-ui-android:1.1.0'
    implementation 'com.github.vietmap-company:maps-sdk-navigation-android:1.1.0'
    implementation 'com.github.vietmap-company:vietmap-services-core:1.0.0'
    implementation 'com.github.vietmap-company:vietmap-services-directions-models:1.0.1'
    implementation 'com.github.vietmap-company:vietmap-services-turf-android:1.0.2'
    implementation 'com.github.vietmap-company:vietmap-services-android:1.1.1'
    implementation 'com.squareup.picasso:picasso:2.8'
    implementation 'com.github.vietmap-company:vietmap-services-geojson-android:1.0.0'
    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.2.0'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.google.code.gson:gson:2.10.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
    implementation 'com.google.android.material:material:1.7.0'
Configure the JitPack repository in the settings.gradle file.
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // add the following two lines to the repositories (at file setting.gradle)
        maven { url 'https://plugins.gradle.org/m2' }
        maven { url 'https://jitpack.io' }
    }
}
For older projects, add the following code to the build.gradle file in the project module:
allprojects {
    repositories {
        google()
        maven { url "https://jitpack.io" }
    }
}

Change the compileSdkVersion and targetSdkVersion to version 33.

compileSdk 33
targetSdk 33

Add the following permissions to the AndroidManifest.xml file:

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Configure project

Add the following color codes to the res/values/colors.xml file:

    <color name="colorPrimary">#8D64F9</color>
    <color name="colorPrimaryDark">#7845F3</color>
    <color name="colorAccent">#F56FA3</color>
    <color name="red">#FF0000</color>

Create styles.xml at res/values

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomNavigationMapRoute" parent="NavigationMapRoute">
    <item name="upcomingManeuverArrowBorderColor">@color/red</item>
</style>

<style name="CustomNavigationView" parent="NavigationViewLight">
    <item name="navigationViewRouteStyle">@style/CustomNavigationMapRoute</item>
</style>

<style name="customInstructionView">
    <item name="navigationViewLocationLayerStyle">@style/NavigationLocationLayerStyle</item>
    <item name="navigationViewRouteOverviewDrawable">@drawable/ic_route_preview</item>
</style>

<style name="CustomInstructionView" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="navigationViewPrimary">@color/mapbox_navigation_view_color_primary</item>
    <item name="navigationViewSecondary">@color/mapbox_navigation_view_color_secondary</item>
    <item name="navigationViewAccent">@color/mapbox_navigation_view_color_accent</item>
    <item name="navigationViewPrimaryText">@color/mapbox_navigation_view_color_secondary</item>
    <item name="navigationViewSecondaryText">@color/mapbox_navigation_view_color_accent_text</item>
    <item name="navigationViewDivider">@color/mapbox_navigation_view_color_divider</item>

    <item name="navigationViewListBackground">@color/mapbox_navigation_view_color_list_background</item>

    <item name="navigationViewBannerBackground">@color/mapbox_navigation_view_color_banner_background</item>
    <item name="navigationViewBannerPrimaryText">@color/mapbox_navigation_view_color_banner_primary_text</item>
    <item name="navigationViewBannerSecondaryText">@color/mapbox_navigation_view_color_banner_secondary_text</item>
    <item name="navigationViewBannerManeuverPrimary">@color/mapbox_navigation_view_color_banner_maneuver_primary</item>
    <item name="navigationViewBannerManeuverSecondary">@color/mapbox_navigation_view_color_banner_maneuver_secondary</item>

    <item name="navigationViewProgress">@color/mapbox_navigation_view_color_progress</item>
    <item name="navigationViewProgressBackground">@color/mapbox_navigation_view_color_progress_background</item>

    <item name="navigationViewRouteStyle">@style/NavigationMapRoute</item>

    <item name="navigationViewLocationLayerStyle">@style/mapbox_LocationLayer</item>

    <item name="navigationViewDestinationMarker">@drawable/map_marker_light</item>

    <item name="navigationViewRouteOverviewDrawable">@drawable/ic_route_preview</item>

    <item name="navigationViewMapStyle">@string/navigation_guidance_day</item>
</style>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<style name="TestNavigationViewDark" parent="NavigationViewDark">
    <!-- Map style URL -->
    <item name="navigationViewMapStyle">
        YOUR STYLE URL HERE
    </item>
</style>

<style name="TestNavigationViewLight" parent="NavigationViewLight">
    <!-- Map style URL -->
    <item name="navigationViewMapStyle">
        YOUR STYLE URL HERE
    </item>
</style>
</resources>

Add these following code into string.xml file

    <string name="title_mock_navigation">Giả bộ Điều hướng</string>
    <string name="description_mock_navigation">Giả bộ phiên điều hướng dùng máy định vị giả.</string>

    <string name="title_off_route_detection">Nhận ra Lạc đường</string>
    <string name="description_off_route_detection">Sử dụng lớp RouteUtils để xác định người dùng bị lạc đường.</string>

    <string name="title_reroute">Tìm Đường đi Mới</string>
    <string name="description_reroute">Thử chức năng tìm đường đi mới trong SDK Điều hướng</string>

    <string name="title_navigation_route_ui">Tuyến đường trên Bản đồ Điều hướng</string>
    <string name="description_navigation_route_ui">Vẽ tuyến đường trên bản đồ</string>

    <string name="title_navigation_launcher">Trình khởi động Điều hướng</string>
    <string name="description_navigation_launcher">Trải nghiệm giao diện người dùng  thể xen vào</string>

    <string name="title_end_navigation">Kết thúc Điều hướng</string>
    <string name="description_end_navigation">Cho biết cách kết thúc điều hướng dùng NavigationView</string>

    <string name="title_dual_navigation_map">Đôi Bản đồ Điều hướng</string>
    <string name="description_dual_navigation_map">Chỉ cách thêm NavigationView  MapView vào cùng bố cục</string>

    <string name="title_waypoint_navigation">Điều hướng giữa các Tọa độ điểm</string>
    <string name="description_waypoint_navigation">Điều hướng giữa các tọa độ điểm</string>

    <string name="title_embedded_navigation">Điều hướng được Nhúng</string>
    <string name="description_embedded_navigation">Điều hướng trong khung nhìn chứa các khung nhìn khác</string>

    <string name="title_fragment_navigation">NavigationView thực hiện bằng Fragment</string>
    <string name="description_fragment_navigation">NavigationView thực hiện bằng Fragment</string>

    <string name="settings">Thiết lập</string>
    <string name="simulate_route"> phỏng Tuyến đường</string>
    <string name="language">Ngôn ngữ</string>
    <string name="unit_type">Hệ Đo lường</string>
    <string name="route_profile">Chế độ</string>

    <string name="error_route_not_available">Tuyến đường hiện tại không  sẵn</string>
    <string name="error_select_longer_route">Vui lòng chọn một tuyến đường dài hơn</string>
    <string name="error_valid_route_not_found">Không tìm thấy tuyến đi được.</string>
    <string name="explanation_long_press_waypoint">Chạm lâu vào bản đồ để thả ghim tọa độ điểm</string>

    <string name="title_navigation_ui">Navigation UI</string>
    <string name="description_navigation_ui">Showcase a Navigation UI session. Optional with simulation.</string>


    <string name="title_component_navigation">MapboxNavigation with UI components</string>
    <string name="description_component_navigation">MapboxNavigation with UI components</string>

    <string name="unit_type_key" translatable="false">unit_type</string>
    <string name="simulate_route_key" translatable="false">simulate_route</string>
    <string name="language_key" translatable="false">language</string>
    <string name="route_profile_key" translatable="false">route_profile</string>
    <string name="default_locale" translatable="false">default_for_device</string>
    <string name="default_unit_type" translatable="false">default_for_device</string>
    <string name="current_night_mode" translatable="false">current_night_mode</string>

    <string name="new_location"> độ: %1$s Kinh độ: %2$s</string>
    <string name="map_view_style_url" translatable="false">YOUR_STYLE_URL_HERE</string>

    <string name="user_location_permission_explanation">ng dụng này cần sử dụng quyền vị trí để hoạt động chính xác.</string>
    <string name="user_location_permission_not_granted">Bạn chưa cung cấp quyền vị trí.</string>

Note: add the styleUrl at the location "YOUR_STYLE_URL_HERE" for the map_view_style_url key to run navigation.

Create a NavigationActivity to utilize the SDK

Create a new activity named VietMapNavigationActivity.

To create a new activity named VietMapNavigationActivity in your Android project, follow these steps:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/vietmapNavigation">

    <com.mapbox.services.android.navigation.ui.v5.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:visibility="invisible"
        app:maplibre_cameraZoom="16"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintTop_toTopOf="parent"
        app:navigationDarkTheme="@style/NavigationViewDark"
        app:navigationLightTheme="@style/NavigationViewLight"/>

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:maplibre_cameraZoom="16"
        android:visibility="visible"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHeight_percent="1"/>

    <ProgressBar
        android:id="@+id/loading"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/launchNavigation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:src="@drawable/ic_navigation"
        android:tint="@android:color/white"
        android:visibility="invisible"
        app:backgroundTint="@color/colorPrimary"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
VietMapNavigationActivity needs to implement several listener classes below to capture events and handle them during the SDK navigation process.
public class VietMapNavigationMapActivity extends AppCompatActivity implements 
        OnNavigationReadyCallback,
        ProgressChangeListener,
        NavigationListener,
        Callback<DirectionsResponse>,
        OnMapReadyCallback,
        MapboxMap.OnMapClickListener,
        MapboxMap.OnMapLongClickListener,
        MapboxMap.OnMoveListener,
        OnRouteSelectionChangeListener,
        OffRouteListener, 
        RouteListener, NavigationEventListener 
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //Hàm Mapbox.getInstance cần được gọi ngay khi khởi tạo activity
        Mapbox.getInstance(this);
        super.onCreate(savedInstanceState);
    }
}
Note
* OnNavigationReadyCallback: Listens when the SDK starts navigation.
* ProgressChangeListener(location, routeProgress): Continuously listens to the user's current location, current route information, next route, and remaining distance that the user needs to travel.
* NavigationListener: Includes three functions:
 - onCancelNavigation: Listens when the user cancels the navigation.
 - onNavigationFinished: Listens when the user completes the journey.
* onNavigationRunning: Listens when the user is actively navigating.
* Callback(DirectionsResponse): Returns the result when the getRoute operation is completed.
* OnMapReadyCallback: Listens when the map initialization is completed and applies the style to the map.
* MapboxMap.OnMapClickListener, MapboxMap.OnMapLongClickListener, MapboxMap.OnMoveListener: Listen to map events such as click, long click, and move.
* OnRouteSelectionChangeListener(DirectionsRoute newRouteSelected):
* onNewPrimaryRouteSelected: Listens when the user selects a different route from the current route and returns the newly selected route.
* OffRouteListener: Listens when the user deviates from the intended route and needs to find an alternative route based on the user's current location.
* userOffRoute(Location currentLocation): Called when the user deviates from the intended route, providing the current location to find a new route.
* RouteListener: Listens when the user arrives at the destination.
* onArrival(): Called when the user reaches the destination.

Declare the necessary variables:

    private static final int DEFAULT_CAMERA_ZOOM = 20;
    private ConstraintLayout customUINavigation;
    private NavigationView navigationView;
    private MapView mapView;
    private ProgressBar loading;
    private FloatingActionButton launchNavigationFab;
    private Point origin = Point.fromLngLat(106.675789, 10.759050);
    private Point destination = Point.fromLngLat(106.686777, 10.775056);
    private DirectionsRoute route;
    private boolean isNavigationRunning;
    private MapboxNavigation mapboxNavigation;
    private LocationEngine locationEngine;
    private NavigationMapRoute mapRoute;
    private MapboxMap mapboxMap;
    private ConstraintSet navigationMapConstraint;
    private ConstraintSet navigationMapExpandedConstraint;
    private boolean[] constraintChanged;
    private LocationComponent locationComponent;
    private ReplayRouteLocationEngine mockLocationEngine;
    private FusedLocationProviderClient fusedLocationClient;
    private int BEGIN_ROUTE_MILESTONE = 1001;
    private boolean reRoute = false;
    private boolean isArrived = false;
    private NavigationViewOptions.Builder mapviewNavigationOptions;
onCreate, initializing the navigation screen
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // Thêm đoạn code sau vào hàm onCreate
        CustomNavigationNotification customNotification = new CustomNavigationNotification(this);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            customNotification.createNotificationChannel(this);
        }
        MapboxNavigationOptions options = MapboxNavigationOptions.builder()
                .navigationNotification(customNotification)
                .build();
        mapboxNavigation = new MapboxNavigation(this, options);
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        initializeViews(savedInstanceState);
        navigationView.initialize(this);
        navigationMapConstraint = new ConstraintSet();
        navigationMapConstraint.clone(customUINavigation);
        navigationMapExpandedConstraint = new ConstraintSet();
        navigationMapExpandedConstraint.clone(this, R.layout.vietmap_navigation_expand);
        constraintChanged = new boolean[]{false};
    }
initializeViews
    private void initializeViews(@Nullable Bundle savedInstanceState) {
        setContentView(R.layout.activity_viet_map_navigation);
        customUINavigation = findViewById(R.id.vietmapNavigation);
        mapView = findViewById(R.id.mapView);
        navigationView = findViewById(R.id.navigationView);
        loading = findViewById(R.id.loading);
        launchNavigationFab = findViewById(R.id.launchNavigation);
        navigationView.onCreate(savedInstanceState);
        mapView.onCreate(savedInstanceState);
        launchNavigationFab.setOnClickListener(v -> {
            expandCollapse();
            launchNavigation();
        });
        mapView.getMapAsync(this);
    }
onMapReady
    @Override
    public void onMapReady(@NonNull MapboxMap mapboxMap) {
        this.mapboxMap = mapboxMap;
        mapboxMap.setStyle(new Style.Builder().fromUri(YOUR_STYLE_URL_HERE), style -> {
            initLocationEngine();
            getCurrentLocation();
            enableLocationComponent(style);
            initMapRoute();
        });
        this.mapboxMap.addOnMapClickListener(this);
    }
    private void initLocationEngine() {
        mockLocationEngine = new ReplayRouteLocationEngine();
        locationEngine = LocationEngineProvider.getBestLocationEngine(this);
        long DEFAULT_INTERVAL_IN_MILLISECONDS = 5000;
        long DEFAULT_MAX_WAIT_TIME = 30000;
        LocationEngineRequest request = new LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
                .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
                .setMaxWaitTime(DEFAULT_MAX_WAIT_TIME)
                .build();

        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            mockLocationEngine.assignLastLocation(origin);
            return;
        }
    }

    private void initMapRoute() {

        mapRoute = new NavigationMapRoute(mapView, mapboxMap);
        mapRoute.setOnRouteSelectionChangeListener(this);
        mapRoute.addProgressChangeListener(new MapboxNavigation(this));
    }


    private void getCurrentLocation() {
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            fusedLocationClient.getLastLocation()
                    .addOnSuccessListener(this, location -> {
                        if (location != null) {
                            origin = Point.fromLngLat(location.getLongitude(), location.getLatitude());
                        }
                    });
            return;
        }
    }
    private void enableLocationComponent(Style style) {
        locationComponent = mapboxMap.getLocationComponent();

        if (locationComponent != null) {
            locationComponent.activateLocationComponent(
                    LocationComponentActivationOptions.builder(this, style).build()
            );
            if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            locationComponent.setLocationComponentEnabled(true);
            locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
            locationComponent.zoomWhileTracking(DEFAULT_CAMERA_ZOOM);
            locationComponent.setRenderMode(RenderMode.GPS);
            locationComponent.setLocationEngine(locationEngine);
        }
    }
Create layout xml vietmap_navigation_expand
<?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"
    android:id="@+id/vietmapNavigationExpand"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mapbox.services.android.navigation.ui.v5.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:visibility="visible"
        app:maplibre_cameraZoom="15"
        app:layout_constraintHeight_percent="1"
        app:layout_constraintTop_toTopOf="@+id/vietmapNavigationExpand"
        app:layout_constraintBottom_toBottomOf="@+id/vietmapNavigationExpand"
        app:navigationDarkTheme="@style/NavigationViewDark"
        app:navigationLightTheme="@style/NavigationViewLight"/>

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:maplibre_cameraZoom="15"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/vietmapNavigationExpand"
        app:layout_constraintHeight_percent="0"/>

    <ProgressBar
        android:id="@+id/loading"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/launchNavigation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:src="@drawable/ic_navigation"
        android:tint="@android:color/white"
        android:visibility="gone"
        app:backgroundTint="@color/colorPrimary"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
expandCollapse
    private void expandCollapse() {
        TransitionManager.beginDelayedTransition(customUINavigation);
        ConstraintSet constraint;
        if (constraintChanged[0]) {
            constraint = navigationMapConstraint;
        } else {
            constraint = navigationMapExpandedConstraint;
        }
        constraint.applyTo(customUINavigation);
        constraintChanged[0] = !constraintChanged[0];
    }
stopNavigationFunction
    void stopNavigationFunction(){
        navigationView.stopNavigation();
        mapboxNavigation.stopNavigation();
        launchNavigationFab.show();
    }
onCancelNavigation(): Listening function when the user stops navigation
    @Override
    public void onCancelNavigation() {
        isNavigationRunning=false;
        expandCollapse();
        stopNavigationFunction();
    }
onRunning and onNavigationReady(): Listening for changes in the navigation state
    @Override
    public void onRunning(boolean b) {
        isNavigationRunning = b;
    }


    @Override
    public void onNavigationReady(boolean b) {

        isNavigationRunning = b;
    }
onNewPrimaryRouteSelected(): Listening for changes when user chooses alternative route
     @Override
    public void onNewPrimaryRouteSelected(DirectionsRoute directionsRoute) {
        route=directionsRoute;
    }

Create CustomNavigationNotification - Sending notifications to the user along each route:

public class CustomNavigationNotification implements NavigationNotification {

    private static final int CUSTOM_NOTIFICATION_ID = 91234821;
    private static final String STOP_NAVIGATION_ACTION = "stop_navigation_action";
    private final Notification customNotification;
    private final NotificationCompat.Builder customNotificationBuilder;
    private final NotificationManager notificationManager;
    private BroadcastReceiver stopNavigationReceiver;
    private int numberOfUpdates;

    public CustomNavigationNotification(Context applicationContext) {
        notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
        customNotificationBuilder = new NotificationCompat.Builder(applicationContext, NAVIGATION_NOTIFICATION_CHANNEL)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle("Custom Navigation Notification")
                .setContentText("Display your own content here!")
                .setContentIntent(createPendingStopIntent(applicationContext));
        customNotification = customNotificationBuilder.build();
    }

    @Override
    public Notification getNotification() {
        return customNotification;
    }

    @Override
    public int getNotificationId() {
        return CUSTOM_NOTIFICATION_ID;
    }

    @Override
    public void updateNotification(RouteProgress routeProgress) {
        // Update the builder with a new number of updates
        customNotificationBuilder.setContentText("Number of updates: " + numberOfUpdates++);
        notificationManager.notify(CUSTOM_NOTIFICATION_ID, customNotificationBuilder.build());
    }

    @Override
    public void onNavigationStopped(Context context) {
        try {
            context.unregisterReceiver(stopNavigationReceiver);
        }catch(Exception e){}
        notificationManager.cancel(CUSTOM_NOTIFICATION_ID);
    }

    public void register(BroadcastReceiver stopNavigationReceiver, Context applicationContext) {
        this.stopNavigationReceiver = stopNavigationReceiver;
        applicationContext.registerReceiver(stopNavigationReceiver, new IntentFilter(STOP_NAVIGATION_ACTION));
    }

    private PendingIntent createPendingStopIntent(Context context) {
        Intent stopNavigationIntent = new Intent(STOP_NAVIGATION_ACTION);
        return PendingIntent.getBroadcast(context, 0, stopNavigationIntent, PendingIntent.FLAG_IMMUTABLE);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    public void createNotificationChannel(Context context) {
        NotificationChannel chan = new NotificationChannel(NAVIGATION_NOTIFICATION_CHANNEL, "CustomNavigationNotification", NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager service = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (service != null) {
            service.createNotificationChannel(chan);
        }
    }
}

After calling the fetchRoute function, you will receive the result in the listener as follows:

    @Override
    public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
        if (validRouteResponse(response)) {
            if (reRoute) {
                route = response.body().routes().get(0);
                initNavigationOptions();
                navigationView.updateCameraRouteOverview();
                mapboxNavigation.addNavigationEventListener(this);
                mapboxNavigation.startNavigation(route);
                navigationView.startNavigation(this.mapviewNavigationOptions.build());
                reRoute = false;
                isArrived=false;
            } else {
                launchNavigationFab.show();
                route = response.body().routes().get(0);
                mapRoute.addRoutes(response.body().routes());
                if (isNavigationRunning) {
                    launchNavigation();
                }
            }
        }
    }

Start navigation

After successfully fetching the route, the next step is to configure some options to start the navigation

void initNavigationOptions(){
        MapboxNavigationOptions navigationOptions = MapboxNavigationOptions.builder()
                .build();
        mapviewNavigationOptions =NavigationViewOptions.builder()
                .navigationListener(this)
                .routeListener(this)
                .navigationOptions(navigationOptions)
                .locationEngine(locationEngine)
                .shouldSimulateRoute(false)
                .progressChangeListener(progressChangeListener)
                .directionsRoute(route)
                .onMoveListener(this);
    }

The progressChangeListener function returns two pieces of information: location (the current location of the user) and routeProgress (information about the current route the user is traversing, including the next maneuver, remaining distance, etc.).

    private ProgressChangeListener progressChangeListener = (location, routeProgress) -> {
        System.out.println("Progress Changing");
    };
    private boolean validRouteResponse(Response<DirectionsResponse> response) {
        return response.body() != null && !response.body().routes().isEmpty();
    }

initNavigationOptions will be called before starting the navigation.

    private void launchNavigation() {
        launchNavigationFab.hide();
        navigationView.setVisibility(View.VISIBLE);
        mapboxNavigation.addOffRouteListener(this);
        initNavigationOptions();
        mapboxNavigation.startNavigation(route);
        navigationView.startNavigation(this.mapviewNavigationOptions.build());
        isArrived=false;
    }

  • The launchNavigationfunction is called on a button click event, as specified by the user.

  • In the launchNavigation function, there are two startNavigation methods being invoked:

  • The startNavigation method of the mapboxNavigation instance is called. This method serves as a controller to listen to the navigation state and provides access to all the information about the ongoing trip.
  • The startNavigation method of the navigationView is called to begin displaying the navigation UI on the screen.
        @Override
        public void userOffRoute(Location location) {
            if(isArrived) return;
                reRoute = true;
                fetchRoute(Point.fromLngLat(location.getLongitude(), location.getLatitude()), destination);
        }  
    
    userOffRoute function listens for when the user deviates from the returned route and then finds a more suitable alternative route based on the current user's direction of travel.
        @Override
        public void onProgressChange(Location location, RouteProgress routeProgress) {
    
        }
    
    onProgressChange function listens for the user's movement and continuously updates information about the route the user is traveling on, remaining distance, etc.
        @Override
        public void onArrival() {
            if(isArrived) return;
            //Xử lý thông báo và kết thúc dẫn đường tại đây
            isArrived=true;
        }
    
    onArrival function listens for when the user has arrived at the destination and allows you to create custom notifications or alerts for the user.
        @Override
        public boolean onMapClick(@NonNull LatLng latLng) {
            getCurrentLocation();
            destination = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude());
            if (origin != null) {
                fetchRoute(origin, destination);
            }
            return false;
        }
    

onMapClick function receives the current location and retrieves the route from the current location to the location selected by the user.

Add the following callback functions to ensure proper initialization and memory management, as well as handling user actions. The NavigationView component should be linked to the activity's lifecycle using the provided callbacks. This allows the NavigationView to correctly handle the activity's lifecycle and provide corresponding responses.

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

    @Override
        public void onResume() {
        super.onResume();
        navigationView.onResume();
        mapView.onResume();
        if (locationEngine != null) {
        }
    }

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

    @Override
    public void onBackPressed() {
        stopNavigationFunction();

        if (!navigationView.onBackPressed()) {
            super.onBackPressed();
        }
    }

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

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        navigationView.onRestoreInstanceState(savedInstanceState);
    }

    @Override
    public void onPause() {
        super.onPause();
        navigationView.onPause();
        mapView.onPause();
        if (locationEngine != null) {
        }
    }

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        navigationView.onDestroy();
        mapView.onDestroy();
        if (locationEngine != null) {
        }
    }

In MainActivity, add a function to check location permissions and a button to navigate to the navigation screen

public class MainActivity extends AppCompatActivity implements PermissionsListener {

    private PermissionsManager permissionsManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =  findViewById(R.id.pushToNavigationScreen);
        Intent it = new Intent(this, VietMapNavigationActivity.class);
        button.setOnClickListener(view -> startActivity(it));
        permissionsManager = new PermissionsManager(this);
        if (!PermissionsManager.areLocationPermissionsGranted(this)) {
            permissionsManager.requestLocationPermissions(this);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public void onExplanationNeeded(List<String> permissionsToExplain) {
        Toast.makeText(this, "This app needs location permissions in order to show its functionality.",
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void onPermissionResult(boolean granted) {
        if (granted) {
        } else {
            Toast.makeText(this, "You didn't grant location permissions.",
                    Toast.LENGTH_LONG).show();
        }
    }
}

In the activity_main.xml file, add a layout for the button above

    <Button
        android:id="@+id/pushToNavigationScreen"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start to VietMapNavigationScreen"
        tools:ignore="MissingConstraints" />

Customise UI

    navigationView.initViewConfig(true);
onCreate function, add the following code snippet above to hide the default UI elements, leaving only the map and navigation components. The provided information for the trip will be displayed in full. Add this code into layout xml file in VietmapNavigationActivity
    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        android:id="@+id/navigationAction"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent">
        <Button
            android:id="@+id/overViewRouteButton"
            android:layout_width="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/stopNavigation"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/stopNavigation"
            android:layout_gravity="top"
            android:layout_margin="16dp"
            android:text="Over View"
            app:layout_anchorGravity="top|left"
            android:visibility="gone"
            android:textColor="@color/black"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <Button
            android:id="@+id/recenterBtnCustom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_gravity="top"
            android:layout_margin="16dp"
            android:text="Recenter"
            android:visibility="gone"
            android:textColor="@color/black"
            app:layout_anchorGravity="top|left"
            app:layout_constraintStart_toStartOf="parent" />
        <Button
            android:id="@+id/stopNavigation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/black"
            app:layout_constraintTop_toBottomOf="@id/overViewRouteButton"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_gravity="top"
            android:layout_margin="16dp"
            android:text="Stop"
            app:layout_anchorGravity="top|left"
            app:layout_constraintStart_toStartOf="parent" />
    </androidx.appcompat.widget.LinearLayoutCompat>
Declare three buttons for performing actions like zooming to the midpoint, viewing the entire route, and canceling navigation
    private Button recenterButton;
    private Button overViewRouteButton;
    private Button stopNavigation;

Listener and execution functions in the custom UI screen

Initialize the NavigationPresenter variable in the onCreate

NavigationPresenter navigationPresenter = navigationView.getNavigationPresenter();

Create controller for manage functions

recenterFunction

    recenterButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            navigationPresenter.onRecenterClick();
            changeNavigationActionState(true);
        }
    });
routeOverViewButton(): Overview of the route
    overViewRouteButton.setOnClickListener(view -> {
        navigationPresenter.onRouteOverviewClick();
        changeNavigationActionState(false);
    });
stopNavigation(): In order to end navigation
    stopNavigation.setOnClickListener(view -> {
        changeNavigationActionState(false);
        expandCollapse();
        stopNavigationFunction();
    });
Adjust stopNavigation by using this:
    void stopNavigationFunction(){
        navigationView.stopNavigation();
        mapboxNavigation.stopNavigation();
        launchNavigationFab.show();
        //Thêm 3 dòng code dưới đây 
        recenterButton.setVisibility(View.GONE);
        overViewRouteButton.setVisibility(View.GONE);
        stopNavigation.setVisibility(View.GONE);
    }

recenterButton(): To listen to map movement events and display a button to return to the route when the user moves the map

    @Override
    public void onMoveBegin(@NonNull MoveGestureDetector moveGestureDetector) {
        changeNavigationActionState(false);
    }

changeNavigationActionState(): To change the state of buttons

    void changeNavigationActionState(boolean isNavigationRunning) {
        if (!isNavigationRunning) {
            overViewRouteButton.setVisibility(View.GONE);
            recenterButton.setVisibility(View.VISIBLE);
            stopNavigation.setVisibility(View.GONE);
        } else {
            overViewRouteButton.setVisibility(View.VISIBLE);
            recenterButton.setVisibility(View.GONE);
            stopNavigation.setVisibility(View.VISIBLE);
        }
    }

initializeViews()

private void initializeViews(@Nullable Bundle savedInstanceState) {
        setContentView(R.layout.activity_viet_map_navigation);
        customUINavigation = findViewById(R.id.vietmapNavigation);
        mapView = findViewById(R.id.mapView);
        navigationView = findViewById(R.id.navigationView);
        loading = findViewById(R.id.loading);
        launchNavigationFab = findViewById(R.id.launchNavigation);
        navigationView.onCreate(savedInstanceState);
        mapView.onCreate(savedInstanceState);
        launchNavigationFab.setOnClickListener(v -> {
            expandCollapse();
            launchNavigation();
        });
        mapView.getMapAsync(this);
        /// Thêm 3 dòng dưới đây
        overViewRouteButton = findViewById(R.id.overViewRouteButton);
        stopNavigation = findViewById(R.id.stopNavigation);
        recenterButton = findViewById(R.id.recenterBtnCustom);
    }
Add this code into launchNavigation
    private void launchNavigation() {
    ...
        changeNavigationActionState(true);
    ...
    }
Adjust stopNavigation
    void stopNavigationFunction(){
        navigationView.stopNavigation();
        mapboxNavigation.stopNavigation();
        recenterButton.setVisibility(View.GONE);
        overViewRouteButton.setVisibility(View.GONE);
        stopNavigation.setVisibility(View.GONE);
        launchNavigationFab.show();
    }

API key and styleURL

To ensure that the application doesn't crash when running, you need to add the styleUrl and apiKey provided by VietMap at the following locations:

  • Add styleUrl in the src/values/strings.xml file

  • Add styleUrl in the onMapReady method

  • Add apiKey in the fetchRoute method

facebook
Tổng đài hỗ trợ
089.616.4567
facebook Chat Facebook zalo Chat Zalo