co.ldln.android.MapFragment.java Source code

Java tutorial

Introduction

Here is the source code for co.ldln.android.MapFragment.java

Source

/*
 * Copyright (c) 2017 LDLN
 *
 * This file is part of LDLN's Responder for Android.
 *
 * Responder for Android is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or any
 * later version.
 *
 * Responder for Android is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LDLN Responder for Android.  If not, see <http://www.gnu.org/licenses/>.
 */

package co.ldln.android;

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.mousebird.maply.ComponentObject;
import com.mousebird.maply.GlobeMapFragment;
import com.mousebird.maply.MBTiles;
import com.mousebird.maply.MapController;
import com.mousebird.maply.MapboxVectorTileSource;
import com.mousebird.maply.MaplyBaseController;
import com.mousebird.maply.MarkerInfo;
import com.mousebird.maply.Point2d;
import com.mousebird.maply.QuadPagingLayer;
import com.mousebird.maply.ScreenMarker;
import com.mousebird.maply.SelectedObject;
import com.mousebird.maply.SphericalMercatorCoordSystem;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import co.ldln.android.sdk.LDLN;
import co.ldln.android.sdk.PlaintextObject;

/**
 * Fragment that appears in the "content_frame", shows the login information
 */
public class MapFragment extends GlobeMapFragment
        implements MapController.GestureDelegate, LDLN.ListSyncableObjectsListener {
    private MainActivity mActivity;
    private ArrayList<ComponentObject> markers = new ArrayList<>();

    public MapFragment() {
        // Empty constructor required for fragment subclasses
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        return baseControl.getContentView();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mActivity = (MainActivity) getActivity();
    }

    @Override
    protected MapDisplayType chooseDisplayType() {
        return MapDisplayType.Map;
    }

    @Override
    protected void controlHasStarted() {
        checkPermissionsAndLoadOfflineMaps();
    }

    private void checkPermissionsAndLoadOfflineMaps() {

        // Here, thisActivity is the current activity
        if (ContextCompat.checkSelfPermission(mActivity,
                Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity,
                    Manifest.permission.READ_EXTERNAL_STORAGE)) {

                // Show an expanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.

            } else {

                // No explanation needed, we can request the permission.

                requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, 100);

                // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                // app-defined int constant. The callback method gets the
                // result of the request.
                // TODO: Determine if this is ever actually needed on any devices/OSs
            }
        } else {
            // Permission was previously granted, can just continue to loading the maps
            loadOfflineMaps();
        }
    }

    private void loadOfflineMaps() {
        // Find the mbtiles file in a LDLN subfolder of the SDK card
        // Note: if there is more than one file, the following logic will
        // find the last alphabetically-ordered mbtiles file in that folder
        // and use that.
        String filePath = null;
        File sdCardRoot = Environment.getExternalStorageDirectory();
        File dir = new File(sdCardRoot, "LDLN");
        for (File f : dir.listFiles()) {
            if (f.isFile()) {
                String name = f.getName();
                //Log.d("LDLN Dir", name);
                if (name.substring(name.lastIndexOf('.') + 1).equals("mbtiles"))
                    filePath = dir + "/" + name;
            }
        }

        // Load the map
        if (filePath != null) {
            // Load the map and create a suitable object for including in the map as a layer
            File file = new File(filePath);
            MBTiles mbTiles = new MBTiles(file);
            LDLNVectorSimpleStyleGenerator vectorStyleSimpleGenerator = new LDLNVectorSimpleStyleGenerator(
                    mActivity, mapControl);
            MapboxVectorTileSource mapboxVectorTileSource = new MapboxVectorTileSource(mbTiles,
                    vectorStyleSimpleGenerator);
            QuadPagingLayer quadPagingLayer = new QuadPagingLayer(mapControl, new SphericalMercatorCoordSystem(),
                    mapboxVectorTileSource);

            // Set number of threads to use
            quadPagingLayer.setSimultaneousFetches(8);
            quadPagingLayer.setUseParentTileBounds(true);
            quadPagingLayer.setTileHeightRange(0.0062, 100);

            // Set controller to be gesture delegate.
            // Needed to allow selection.
            mapControl.gestureDelegate = this;

            // Set the color of the land
            mapControl.setClearColor(Color.LTGRAY);

            // Add the layer
            mapControl.addLayer(quadPagingLayer);

            // Stop the map from being able to rotate
            mapControl.setAllowRotateGesture(false);

            // Insert markers and center
            insertMarkersAndCenter();

            // Provide some instructions for using the map
            Toast.makeText(mActivity,
                    "Map is loaded!\n-Pan & Zoom like any map\n- Longtap to create pins\n- Tap pins to see details",
                    Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
        case 100: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // storage-related task you need to do.
                loadOfflineMaps();
            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
        }
    }

    @Override
    public void userDidTap(MapController mapController, Point2d loc, Point2d screenLoc) {
        // Log the various data points associated with the tap
        //Log.d("Map Tapped", "Loc | x: " + loc.getX() + " y: " + loc.getY() + " z: " + mapControl.getPositionGeo().getZ());
        //Log.d("Map Tapped", "Latlon | lon: " + loc.toDegrees().getX() + " lat: " + loc.toDegrees().getY());
        //Log.d("Map Tapped", "ScreenLoc | x: " + screenLoc.getX() + " y: " + screenLoc.getY());
    }

    @Override
    public void userDidLongPress(MapController mapController, SelectedObject[] selObjs, Point2d loc,
            Point2d screenLoc) {
        // Log the various data points associated with the tap
        //Log.d("Map Longpressed", "Loc | x: " + loc.getX() + " y: " + loc.getY() + " z: " + mapControl.getPositionGeo().getZ());
        //Log.d("Map Longpressed", "Latlon | lon: " + loc.toDegrees().getX() + " lat: " + loc.toDegrees().getY());
        //Log.d("Map Longpressed", "ScreenLoc | x: " + screenLoc.getX() + " y: " + screenLoc.getY());
        if (selObjs != null) {
            //Log.d("Map Longpressed", selObjs.toString());
        } else {
            Bundle args = new Bundle();
            args.putString("map_location_lat", String.valueOf(loc.toDegrees().getY()));
            args.putString("map_location_lon", String.valueOf(loc.toDegrees().getX()));
            args.putString("schema_key", "poi"); // TODO: REMOVE HARDCODED KEY, PROVIDE SELECTION!
            mActivity.openFragment(FragmentId.SYNCABLE_OBJECT_CREATE, args);
        }
        super.userDidLongPress(mapController, selObjs, loc, screenLoc);
    }

    @Override
    public void userDidSelect(MapController mapControl, SelectedObject[] selObjs, Point2d loc, Point2d screenLoc) {
        // Log the various data points associated with the selection
        //Log.d("Map Selected", "Loc | x: " + loc.getX() + " y: " + loc.getY() + " z: " + mapControl.getPositionGeo().getZ());
        //Log.d("Map Selected", "Latlon | lon: " + loc.toDegrees().getX() + " lat: " + loc.toDegrees().getY());
        //Log.d("Map Selected", "ScreenLoc | x: " + screenLoc.getX() + " y: " + screenLoc.getY());

        // Handle selection of specific object types
        if (selObjs != null) {
            //Log.d("Map Selected", selObjs.toString());
            if (selObjs[0].selObj.getClass().equals(LDLNScreenMarker.class)) {
                LDLNScreenMarker marker = (LDLNScreenMarker) selObjs[0].selObj;
                LDLNMarkerAttributes properties = (LDLNMarkerAttributes) marker.userObject;
                Toast.makeText(mActivity, properties.toString() + "\nlon: " + loc.toDegrees().getX() + "\nlat: "
                        + loc.toDegrees().getY(), Toast.LENGTH_SHORT).show();
            }
        }
        super.userDidSelect(mapControl, selObjs, loc, screenLoc);
    }

    private void insertMarker(double lat, double lon, String title, String description, int icon_id,
            boolean recenterMap) {
        // Prepare the features needed for constructing the marker
        Bitmap icon = BitmapFactory.decodeResource(getActivity().getResources(), icon_id);
        LDLNMarkerAttributes properties = new LDLNMarkerAttributes(title, description);

        // Create the marker and add it to the array to be added to the map
        ScreenMarker marker = new LDLNScreenMarker(lat, lon, 45, icon, properties);

        // Add your marker to the map controller.
        MarkerInfo markerInfo = new MarkerInfo();
        ComponentObject markersComponentObject = mapControl.addScreenMarker(marker, markerInfo,
                MaplyBaseController.ThreadMode.ThreadAny);

        // Keep list of objects for removal.
        // ComponentObject is your handle to the marker in the map controller.
        // You can use this to enable, disable, and remove your marker from the map.
        markers.add(markersComponentObject);

        // Center the map if asked to
        if (recenterMap) {
            Point2d centerPt = Point2d.FromDegrees(lon, lat);
            mapControl.animatePositionGeo(centerPt.getX(), centerPt.getY(), mapControl.getPositionGeo().getZ(),
                    0.6);
        }
    }

    void insertMarkersAndCenter() {
        // Insert Markers
        LDLN.listSyncableObjects(mActivity, this, "poi"); // TODO: GET RID OF HARDCODED SCHEMA KEY!
    }

    @Override
    public void onListSyncableObjectsResult(List<PlaintextObject> plaintextObjects) {
        if (plaintextObjects != null) {
            // Remove all existing markers
            while (markers.size() > 0) {
                mapControl.removeObject(markers.remove(0), MaplyBaseController.ThreadMode.ThreadAny);
            }

            // Place new markers
            for (PlaintextObject po : plaintextObjects) {
                HashMap<String, String> hashMap = po.getKeyValueMap();
                if (hashMap.containsKey("Map Location") && !hashMap.get("Map Location").equals("")) {
                    String[] mapLocationBits = hashMap.get("Map Location").split(",");
                    double lat = Double.parseDouble(mapLocationBits[0]);
                    double lon = Double.parseDouble(mapLocationBits[1]);
                    String title = hashMap.get("Title");
                    String desc = hashMap.get("Note");
                    insertMarker(lat, lon, title, desc, R.drawable.ic_city, true);
                }
            }

            // Manually center the map after all stored pins have been created
            double centerLat = Double.valueOf(LDLNProperties.getProperty(mActivity, "map.center.lat"));
            double centerLon = Double.valueOf(LDLNProperties.getProperty(mActivity, "map.center.lon"));
            double centerZoom = Double.valueOf(LDLNProperties.getProperty(mActivity, "map.center.zoom"));
            Point2d centerPt = Point2d.FromDegrees(centerLon, centerLat);
            mapControl.setPositionGeo(centerPt.getX(), centerPt.getY(), centerZoom);
        }
    }

    @Override
    public void onListSyncableObjectsError(String error) {
        Log.e("Map Pins Error", "Error listing syncable objects: " + error);
    }
}