Alvaro2112/Money_run

View on GitHub
app/src/main/java/sdp/moneyrun/ui/map/OfflineMapDownloaderActivity.java

Summary

Maintainability
A
1 hr
Test Coverage
A
91%
package sdp.moneyrun.ui.map;

import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

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.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 java.util.Objects;

import sdp.moneyrun.R;
import sdp.moneyrun.map.LocationCheckObjectivesCallback;
import sdp.moneyrun.map.TrackedMap;
import sdp.moneyrun.ui.menu.MenuActivity;
import sdp.moneyrun.user.User;

@SuppressWarnings("FieldCanBeLocal")
public class OfflineMapDownloaderActivity extends TrackedMap {

    // JSON encoding/decoding
    public static final String JSON_CHARSET = "UTF-8";
    public static final String JSON_FIELD_REGION_NAME = "FIELD_REGION_NAME";
    private final float LAT_OFFSET = 0.1f;
    private final float LONG_OFFSET = 0.1f;
    private final int MAX_ZOOM = 15;
    private final int MIN_ZOOM = 9;
    private boolean isEndNotified = false;
    private boolean hasStartedDownload = false;
    private ProgressBar progressBar;
    private MapView mapView;
    private OfflineManager offlineManager;
    private Button exitButton;
    private User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Objects.requireNonNull(getSupportActionBar()).hide();
        Mapbox.getInstance(this, getString(R.string.mapbox_access_token));
        createMap(savedInstanceState, R.id.mapView_downloader, R.layout.activity_offline_map_downloader);
        setContentView(R.layout.activity_offline_map_downloader);

        user = (User) getIntent().getSerializableExtra("user");
        mapView = findViewById(R.id.mapView_downloader);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this::onMapReady);

        exitButton = findViewById(R.id.downloader_exit);
        addExitButton();

    }

    private void addExitButton() {
        exitButton.setOnClickListener(v -> {
            Intent mainIntent = new Intent(OfflineMapDownloaderActivity.this, MenuActivity.class);
            mainIntent.putExtra("user", user);
            startActivity(mainIntent);
            finish();
        });
    }


    /**
     * @param mapboxMap the map where everything will be done
     *                  this overrides the OnMapReadyCallback in the implemented interface
     *                  We set up the symbol manager here, it will allow us to add markers and other visual stuff on the map
     *                  Then we setup the location tracking
     */
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {

        callback = new LocationCheckObjectivesCallback(this);

        mapboxMap.setStyle(Style.MAPBOX_STREETS, this::enableLocationComponent);
        this.mapboxMap = mapboxMap;
    }


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

    public boolean getIsEndNotified() {
        return isEndNotified;
    }

    public boolean getHasStartedDownload() {
        return hasStartedDownload;
    }

    // Progress bar methods
    private void startProgress() {
        // Start and show the progress bar
        isEndNotified = false;
        hasStartedDownload = true;
        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) {
        if (isEndNotified) {
            return;
        }
        isEndNotified = true;
        progressBar.setIndeterminate(false);
        progressBar.setVisibility(View.GONE);
        Context context = getApplicationContext();

        Toast.makeText(context, message, Toast.LENGTH_LONG).show();
        deleteOlderMaps();
    }

    /**
     * @param location the center of the downloaded map
     *                 The map will be downloaded when the location provider updates the location so that we download the map where the user is.
     */
    @Override
    public void checkObjectives(@NonNull Location location) {

        if (isEndNotified || hasStartedDownload)
            return;

        offlineManager = OfflineManager.getInstance(OfflineMapDownloaderActivity.this);

        // Create a bounding box for the offline region
        LatLng northeast = new LatLng(location.getLatitude() + LAT_OFFSET, location.getLongitude() + LONG_OFFSET);
        LatLng southwest = new LatLng(location.getLatitude() - LAT_OFFSET, location.getLongitude() - LONG_OFFSET);

        LatLngBounds latLngBounds = new LatLngBounds.Builder()
                .include(northeast) // Northeast
                .include(southwest) // Southwest
                .build();

        // Define the offline region
        OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
                mapboxMap.getStyle().getUri(),
                latLngBounds,
                MIN_ZOOM,
                MAX_ZOOM,
                OfflineMapDownloaderActivity.this.getResources().getDisplayMetrics().density);

        // Set the metadata
        byte[] metadata = setMetaData();

        // Create the region asynchronously
        if (metadata != null)
            createOfflineRegion(metadata, definition);

    }

    public void createOfflineRegion(@NonNull byte[] metadata, @NonNull OfflineTilePyramidRegionDefinition definition) {
        offlineManager.createOfflineRegion(
                definition,
                metadata,
                new OfflineManager.CreateOfflineRegionCallback() {
                    @Override
                    public void onCreate(@NonNull OfflineRegion offlineRegion) {
                        offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);

                        progressBar = findViewById(R.id.progress_bar_map_downloader);
                        startProgress();
                        setOfflineRegionObserver(offlineRegion);


                    }

                    @Override
                    public void onError(String error) {
                        Toast.makeText(OfflineMapDownloaderActivity.this.getApplicationContext(), error, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    public void setOfflineRegionObserver(@NonNull OfflineRegion offlineRegion) {
        offlineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
            @Override
            public void onStatusChanged(@NonNull OfflineRegionStatus status) {
                double percentage = status.getRequiredResourceCount() >= 0
                        ? (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :
                        0.0;

                if (status.isComplete()) {
                    endProgress(getString(R.string.offline_end_progress_success));
                } else if (status.isRequiredResourceCountPrecise()) {
                    setPercentage((int) Math.round(percentage));
                }
            }

            @Override
            public void onError(@NonNull OfflineRegionError error) {
                Toast.makeText(OfflineMapDownloaderActivity.this.getApplicationContext(), error.getReason(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void mapboxTileCountLimitExceeded(long limit) {
                Toast.makeText(OfflineMapDownloaderActivity.this.getApplicationContext(), getString(R.string.tile_limit_exceeded), Toast.LENGTH_SHORT).show();

            }
        });
    }

    @Nullable
    public byte[] setMetaData() {
        byte[] metadata;

        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(JSON_FIELD_REGION_NAME, getString(R.string.offline_map_name));
            String json = jsonObject.toString();
            metadata = json.getBytes(JSON_CHARSET);
        } catch (Exception exception) {
            metadata = null;
        }

        return metadata;
    }


    /**
     * Deletes all offline regions except the last from the device
     */
    private void deleteOlderMaps() {
        offlineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
            @Override
            public void onList(@NonNull OfflineRegion[] offlineRegions) {

                if (offlineRegions.length > 1) {
                    // delete the last item in the offlineRegions list which will be yosemite offline map
                    for (int i = 0; i < offlineRegions.length - 1; ++i) {
                        Toast.makeText(OfflineMapDownloaderActivity.this.getApplicationContext(), offlineRegions[i].getDefinition().getBounds().toString(), Toast.LENGTH_SHORT).show();

                        offlineRegions[i].delete(new OfflineRegion.OfflineRegionDeleteCallback() {
                            @Override
                            public void onDelete() {

                                Toast.makeText(
                                        OfflineMapDownloaderActivity.this,
                                        getString(R.string.offline_map_deleted),
                                        Toast.LENGTH_LONG
                                ).show();
                            }

                            @Override
                            public void onError(String error) {
                            }
                        });
                    }
                }
            }

            @Override
            public void onError(String error) {
            }
        });
    }

    @Override
    public void onBackPressed() {
    }

}