rkuenzi-epfl/Wnder

View on GitHub
app/src/main/java/com/github/wnder/guessLocation/GuessLocationActivity.java

Summary

Maintainability
C
1 day
Test Coverage
B
80%
package com.github.wnder.guessLocation;

import android.Manifest;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import androidx.core.app.ActivityCompat;

import com.github.wnder.R;
import com.github.wnder.Score;
import com.github.wnder.Utils;
import com.github.wnder.picture.Picture;
import com.github.wnder.picture.PicturesDatabase;
import com.github.wnder.scoreboard.ScoreboardActivity;
import com.github.wnder.tour.FirebaseTourDatabase;
import com.github.wnder.tour.TourDatabase;
import com.github.wnder.user.GlobalUser;
import com.github.wnder.user.GuestUser;
import com.github.wnder.user.User;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
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.Property;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.inject.Inject;

import dagger.hilt.android.AndroidEntryPoint;

import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static com.github.wnder.guessLocation.MapBoxHelper.addArrowToStyle;
import static com.github.wnder.guessLocation.MapBoxHelper.addGuessToStyle;
import static com.github.wnder.guessLocation.MapBoxHelper.addPictureToStyle;
import static com.github.wnder.guessLocation.MapBoxHelper.drawCircle;
import static com.github.wnder.guessLocation.MapBoxHelper.updatePositionByLineAnimation;
import static com.github.wnder.guessLocation.MapBoxHelper.zoomFromKilometers;

/**
 * Location activity
 */
@AndroidEntryPoint
public class GuessLocationActivity extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnMapClickListener, MapboxMap.OnCameraMoveListener {

    //Define all necessary and recurrent strings
    public static final String EXTRA_GUESS_MODE = "guess_mode";
    public static final String EXTRA_PICTURE_TO_GUESS = "picture_to_guess";
    public static final String EXTRA_PICTURE_ID = "picture_id";
    public static final String EXTRA_TOUR_ID = "tour_id";

    private static final int CAMERA_PADDING = 100;
    private static final long CAMERA_ANIMATION_DURATION = 200; //0.2 secondes
    private static final long GET_POSITION_FROM_GPS_PERIOD = 1000; //1 secondes
    private static final double MAX_LAT = 90;
    private static final double ARRIVED_DISTANCE = 600; //meters

    //Button
    private Button nextGuessButton;

    //Mapbox setup
    private MapView mapView;
    private MapboxMap mapboxMap;
    private LatLng cameraPosition;
    private LatLng guessPosition;
    private LatLng picturePosition;
    private GeoJsonSource guessSource;
    private GeoJsonSource arrowSource;
    private GeoJsonSource pictureSource;
    private ValueAnimator guessAnimator;
    private ValueAnimator arrowAnimator;
    private int guessMode;
    private boolean guessConfirmed;
    private boolean guessPossible;
    private Picture picToGuess;
    private List<String> tourIDs;
    private int tourIndex = 0;
    private User user;

    private SensorManager sensorManager;
    private SensorEventListener listener;

    private Timer gpsTimer;
    private TimerTask gpsTimerTask;

    private boolean compassMode;
    private boolean mapClickOnCompassMode;
    private GuessLocationCompass compass;

    @Inject
    public PicturesDatabase picturesDb;

    public TourDatabase TourDb;

    /**
     * Executed on activity creation
     *
     * @param savedInstanceState instance state
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Initialisation of class variables (MapBox creation)
        Mapbox.getInstance(this, getString(R.string.mapbox_access_token));

        setContentView(R.layout.activity_guess_location);

        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);

        user = GlobalUser.getUser();

        //Get extras
        Intent intent = getIntent();
        Bundle extras = intent.getExtras();

        guessMode = extras.getInt(EXTRA_GUESS_MODE);
        picToGuess = extras.getParcelable(EXTRA_PICTURE_TO_GUESS);
        if (guessMode == R.string.guess_tour_mode) {
            String tourID = extras.getString(EXTRA_TOUR_ID);
            findViewById(R.id.compassMode).setVisibility(INVISIBLE);
            TourDb = new FirebaseTourDatabase(this);
            TourDb.getTourPics(tourID).thenAccept(ls -> tourIDs = ls);
        }

        double gpsLat = user.getPositionFromGPS((LocationManager) getSystemService(Context.LOCATION_SERVICE), GuessLocationActivity.this).getLatitude();
        double gpsLng = user.getPositionFromGPS((LocationManager) getSystemService(Context.LOCATION_SERVICE), GuessLocationActivity.this).getLongitude();

        cameraPosition = new LatLng(gpsLat, gpsLng);
        guessPosition = new LatLng(cameraPosition);

        picturePosition = new LatLng(picToGuess.getPicLat(), picToGuess.getPicLng());

        //Starting mode
        compassMode = false;
        mapClickOnCompassMode = false;
        guessConfirmed = false;
        guessPossible = false;

        compass = new GuessLocationCompass(findViewById(R.id.hotbarView), picturePosition);
        setupZoomAnimation();

        //Buttons
        nextGuessButton = findViewById(R.id.backToGuessPreview);
        nextGuessButton.setVisibility(INVISIBLE);

        nextGuessButton.setOnClickListener(id -> nextGuess());
        findViewById(R.id.compassMode).setOnClickListener(id -> compassButton());
        findViewById(R.id.confirmButton).setOnClickListener(id -> confirmButton());

        //Sensor initialization
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        listener = createMapSensorListener();

        //Timer setup
        gpsTimer = new Timer(true);
        gpsTimerTask = createGpsTimerTask();
    }

    /**
     * Create the TimerTask that update the location of the user with the GPS
     */
    private TimerTask createGpsTimerTask() {
        final Handler handler = new Handler();
        LocationManager locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        return new TimerTask() {
            @Override
            public void run() {
                handler.post(() -> {
                    if(!locMan.isProviderEnabled(LocationManager.GPS_PROVIDER) || ActivityCompat.checkSelfPermission(GuessLocationActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
                        gpsTimer.cancel();
                    }
                    Location loc = user.getPositionFromGPS(locMan, GuessLocationActivity.this);
                    if (compassMode) {
                        LatLng destinationPoint = new LatLng(loc.getLatitude(), loc.getLongitude());
                        updatePositionByLineAnimation(guessSource, guessAnimator, guessPosition, destinationPoint);
                        guessPosition = updatePositionByLineAnimation(arrowSource, arrowAnimator, guessPosition, destinationPoint);
                    }
                    guessPossible = guessPosition.distanceTo(picturePosition) < ARRIVED_DISTANCE;
                });
            }
        };
    }

    /**
     * Create the sensor for the MapBox map the listen to the phone rotations
     */
    private SensorEventListener createMapSensorListener() {
        return new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent event) {
                mapboxMap.setCameraPosition(GuessLocationSensor.calculateNewPosition(event, mapboxMap, picturePosition, guessPosition));
            }

            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }
        };
    }

    /**
     * To call in onCreate to setup the zoom animation correctly.
     */
    private void setupZoomAnimation() {
        //Setup image preview
        //littleImage, encapsulated in littleCard, is the image shown when zoomed out
        //bigImage, encapsulated in bigCard, is the image shown when zoomed in
        CardView littleCard = findViewById(R.id.imageToGuessCard);
        CardView bigCard = findViewById(R.id.imageToGuessCardZoomedIn);
        bigCard.setVisibility(INVISIBLE);
        ImageView littleImage = findViewById(R.id.imageToGuess);
        ImageView bigImage = findViewById(R.id.imageToGuessZoomedIn);
        picturesDb.getBitmap(picToGuess.getUniqueId()).thenAccept(bmp -> {
            littleImage.setImageBitmap(bmp);
            littleImage.setVisibility(VISIBLE);
            bigImage.setImageBitmap(bmp);
        });
        //Setup zoom animation
        int zoomAnimationTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
        List<View> toHide = new ArrayList<>();
        if (guessMode != R.string.guess_tour_mode) {
            toHide.add(findViewById(R.id.compassMode));
        }
        toHide.add(findViewById(R.id.confirmButton));
        new GuessLocationZoom(littleCard, bigCard, findViewById(R.id.guessLocationLayout), zoomAnimationTime, toHide);
    }

    /**
     * Executed when map is ready
     *
     * @param mapboxMap MapboxMap for mapbox
     */
    @Override
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {
        this.mapboxMap = mapboxMap;

        //Set camera position
        CameraPosition position = new CameraPosition.Builder()
                .target(cameraPosition)
                .zoom(zoomFromKilometers(cameraPosition, user.getRadius()))
                .build();
        this.mapboxMap.setCameraPosition(position);

        //Get guess source
        guessSource = new GeoJsonSource(String.valueOf(R.string.GUESS_SOURCE_ID), Point.fromLngLat(guessPosition.getLongitude(), guessPosition.getLatitude()));
        arrowSource = new GeoJsonSource(String.valueOf(R.string.ORANGE_ARROW_SOURCE_ID), Point.fromLngLat(guessPosition.getLongitude(), guessPosition.getLatitude()));
        pictureSource = new GeoJsonSource(String.valueOf(R.string.PICTURE_SOURCE_ID), Point.fromLngLat(picturePosition.getLongitude(), picturePosition.getLatitude()));

        //Set mapbox style
        mapboxMap.getUiSettings().setCompassEnabled(false); //Hide the default mapbox compass because we use our compass

        mapboxMap.setStyle(Style.SATELLITE_STREETS, this::onStyleLoaded);

        mapboxMap.addOnMapClickListener(GuessLocationActivity.this);
        mapboxMap.addOnCameraMoveListener(GuessLocationActivity.this);

        gpsTimer.scheduleAtFixedRate(gpsTimerTask, GET_POSITION_FROM_GPS_PERIOD, GET_POSITION_FROM_GPS_PERIOD);
    }

    /**
     * To be executed when the style has loaded
     *
     * @param style on the mapbox map
     */
    private void onStyleLoaded(Style style) {

        addGuessToStyle(this, style, guessSource);
        addArrowToStyle(this, style, arrowSource, mapboxMap);
        addPictureToStyle(this, style, pictureSource);

        if (guessMode == R.string.guess_tour_mode) {
            compassButton();
        }
        else{
            drawCircle(GuessLocationActivity.this, mapboxMap, cameraPosition);
        }
    }

    /**
     * To execute when a map is clicked
     *
     * @param point point where the map is clicked
     * @return true
     */
    @Override
    public boolean onMapClick(@NonNull LatLng point) {
        if (guessConfirmed) {
            return true;
        }

        //Display a pop up to explain that you cannot move the guess marker by clicking while in compass mode
        if (compassMode) {
            if (!mapClickOnCompassMode) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setCancelable(true);
                builder.setTitle(R.string.mapClickOnCompassMode_confirm_title);
                builder.setMessage(R.string.mapClickOnCompassMode_confirm_message);

                //What to do when OK is pressed
                builder.setPositiveButton("Ok", (DialogInterface dialog, int which) -> mapClickOnCompassMode = true);

                AlertDialog dialog = builder.create();
                dialog.show();
            }
            return true;
        }

        updatePositionByLineAnimation(guessSource, guessAnimator, guessPosition, point);
        guessPosition = updatePositionByLineAnimation(arrowSource, arrowAnimator, guessPosition, point);
        return true;
    }

    /**
     * To execute when a map is Moved
     */
    @Override
    public void onCameraMove() {
        if (compassMode) compass.updateCompass(mapboxMap, guessPosition, compassMode);
    }

    private void enableCompassMode() {
        FloatingActionButton compassModeButtonView = findViewById(R.id.compassMode);

        List<Sensor> list = sensorManager.getSensorList(Sensor.TYPE_ROTATION_VECTOR);

        if (list.isEmpty()) {
            //We can't use the sensor, so we inform the user
            AlertDialog.Builder builder = new AlertDialog.Builder(this);

            builder.setCancelable(true).setTitle(R.string.SensorNotAvailableTitle).setMessage(R.string.SensorNotAvailable)
                    .setPositiveButton("Ok", (DialogInterface dialog, int which) -> compassMode = false);
            AlertDialog dialog = builder.create();
            dialog.show();
        } else {
            compass.updateCompass(mapboxMap, guessPosition, compassMode);
            sensorManager.registerListener(listener, list.get(0), SensorManager.SENSOR_DELAY_NORMAL);
            gpsTimerTask.run();
            mapboxMap.getUiSettings().setRotateGesturesEnabled(false);
            compassModeButtonView.setImageResource(R.drawable.ic_outline_explore_24);
        }
    }

    /**
     * Switch between compass mode and normal mode
     */
    private void compassButton() {
        //If the map didn't load yet do not switch mode
        if (mapboxMap.getStyle() == null) {
            return;
        }

        //change mode
        compassMode = !compassMode;

        //if we're now in compass mode, enable it and switch icon
        if (compassMode) {
            enableCompassMode();
            ((FloatingActionButton) findViewById(R.id.compassMode)).setImageResource(R.drawable.ic_outline_explore_24);
        }
        //else, update it and disable the map click
        else {
            compass.updateCompass(mapboxMap, guessPosition, compassMode);
            sensorManager.unregisterListener(listener);
            mapboxMap.getUiSettings().setRotateGesturesEnabled(true);
            ((FloatingActionButton) findViewById(R.id.compassMode)).setImageResource(R.drawable.ic_outline_explore_off_24);
        }
    }

    /**
     * Confirm button
     */
    private void confirmButton() {
        //If the map style didn't load yet, wait.
        if (mapboxMap.getStyle() == null) {
            return;
        }

        if (guessMode == R.string.guess_tour_mode) {
            confirmTourMode();
        } else {
            confirmSimpleMode();
        }
    }

    private void confirmSimpleMode() {
        //If guess has been confirmed, confirm button becomes button leading to scoreboard
        if (guessConfirmed) {
            startScoreBoard();
            return;
        }

        if (compassMode) {
            compassButton();
        }

        guessConfirmed = true;

        //don't show little image anymore and disable the compass button
        findViewById(R.id.imageToGuessCard).setVisibility(INVISIBLE);
        findViewById(R.id.compassMode).setClickable(false);
        findViewById(R.id.compassMode).setVisibility(INVISIBLE);

        //Once guess has been confirmed, confirm button becomes button leading to scoreboard
        ((FloatingActionButton) findViewById(R.id.confirmButton)).setImageResource(R.drawable.ic_baseline_list_24);

        //Send guess and update karma
        if (!picToGuess.getUniqueId().equals(Utils.UNINITIALIZED_ID) && !(user instanceof GuestUser)) {
            Location guessedLocation = new Location("");
            guessedLocation.setLatitude(guessPosition.getLatitude());
            guessedLocation.setLongitude(guessPosition.getLongitude());
            MapBoxHelper.onMapSnapshotAvailable(this.getApplicationContext(), guessPosition, picturePosition, (mapSnapshot) -> {
                picturesDb.sendUserGuess(picToGuess.getUniqueId(), user.getName(), guessedLocation, mapSnapshot);
            });
        }
        picturesDb.updateKarma(picToGuess.getUniqueId(), 1);

        showActualLocation(computeScoreText(), true);

        //Animate the next guess button
        nextGuessButton.setVisibility(VISIBLE);
        Animation button_animation = AnimationUtils.loadAnimation(this, R.anim.next_guess_button_anim);
        nextGuessButton.startAnimation(button_animation);
    }

    /**
     * Goes to the next guest preview activity
     */
    private void nextGuess() {
        Intent intent = new Intent(this, GuessPreviewActivity.class);
        startActivity(intent);
        finish();
    }

    private void confirmTourMode() {
        TextView scoreText = findViewById(R.id.scoreText);

        if (!guessPossible) {
            //Show pop up warning about skipping the picture
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setCancelable(true);
            builder.setTitle(R.string.tour_mode_confirm_while_far_title);
            builder.setMessage(R.string.tour_mode_confirm_while_far_text);

            //What to do when OK is pressed
            builder.setPositiveButton("Yes", (DialogInterface dialog, int which) -> {
                if (tourIndex >= tourIDs.size()-1) {
                    guessPossibleConfirmTourMode();
                } else {
                    tourIndex += 1;
                    nextGuessTourMode();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

        } else {
            guessPossibleConfirmTourMode();
        }
    }

    private void guessPossibleConfirmTourMode() {
        //If guess has been confirmed, go to next guess or end the tour
        if (guessConfirmed) {
            nextGuessTourMode();
            return;
        }

        tourIndex += 1;
        guessConfirmed = true;

        String animatedText = getString(R.string.tour_score_text, tourIndex, tourIDs.size());

        //If it was the last picture replace the confirm button by the next guess button
        if (tourIndex >= tourIDs.size()) {
            findViewById(R.id.confirmButton).setVisibility(INVISIBLE);

            //Animate the next guess button
            nextGuessButton.setVisibility(VISIBLE);
            Animation button_animation = AnimationUtils.loadAnimation(this, R.anim.next_guess_button_anim);
            nextGuessButton.startAnimation(button_animation);

            animatedText = getString(R.string.finished_tour);
        }

        findViewById(R.id.imageToGuessCard).setVisibility(INVISIBLE);

        //Animate MapBox
        showActualLocation(animatedText, false);
    }

    private void nextGuessTourMode() {
        //Make the confirm button unavailable during the change/download of the next image
        findViewById(R.id.confirmButton).setClickable(false);

        guessConfirmed = false;
        guessPossible = false;

        //Animate the text
        TextView scoreText = findViewById(R.id.scoreText);
        scoreText.setVisibility(VISIBLE);
        Animation invisible_score_animation = AnimationUtils.loadAnimation(this, R.anim.invisible_score_anim);
        scoreText.startAnimation(invisible_score_animation);

        //Update little image and show it
        picturesDb.getBitmap(tourIDs.get(tourIndex)).thenAccept(bmp -> {
            picturesDb.getLocation(tourIDs.get(tourIndex)).thenAccept(loc -> {
                picturePosition = new LatLng(loc.getLatitude(), loc.getLongitude());
                ((ImageView) (findViewById(R.id.imageToGuess))).setImageBitmap(bmp);
                ((ImageView) (findViewById(R.id.imageToGuessZoomedIn))).setImageBitmap(bmp);

                //Update compass
                compass = new GuessLocationCompass(findViewById(R.id.hotbarView), picturePosition);
                compass.updateCompass(mapboxMap, guessPosition, true);

                //Make the confirm button available again
                findViewById(R.id.confirmButton).setClickable(true);
            });
        });

        findViewById(R.id.imageToGuessCard).setVisibility(VISIBLE);
    }

    /**
     * Shows the real location of the picture
     */
    @SuppressLint("SetTextI18n")
    private void showActualLocation(String animatedBoardText, Boolean pictureIconVisibility) {
        //Make the icon of the picture visible
        if (pictureIconVisibility) {
            Style style = mapboxMap.getStyle();
            style.getLayer(String.valueOf(R.string.PICTURE_LAYER_ID)).setProperties(PropertyFactory.visibility(Property.VISIBLE));
        }

        //Animate camera position to englobe the picture and the guess position
        double latDiff = Math.abs(guessPosition.getLatitude() - picturePosition.getLatitude());
        double latMax = Math.max(guessPosition.getLatitude(), picturePosition.getLatitude());
        double latMin = Math.min(guessPosition.getLatitude(), picturePosition.getLatitude());
        LatLng topPosition = new LatLng(Math.min(latMax + latDiff, MAX_LAT), guessPosition.getLongitude());
        LatLng downPosition = new LatLng(Math.max(latMin - latDiff, -MAX_LAT), guessPosition.getLongitude());

        LatLngBounds latLngBounds = new LatLngBounds.Builder().include(guessPosition).include(picturePosition).include(topPosition).include(downPosition).build();
        mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, CAMERA_PADDING), (int) CAMERA_ANIMATION_DURATION);

        //Animate the text
        TextView scoreText = findViewById(R.id.scoreText);
        scoreText.setText(animatedBoardText);
        scoreText.setVisibility(VISIBLE);
        Animation score_animation = AnimationUtils.loadAnimation(this, R.anim.score_anim);
        scoreText.startAnimation(score_animation);
    }

    private String computeScoreText() {
        //Set the score text
        double distanceFromPicture = guessPosition.distanceTo(picturePosition);

        String dText = getString(R.string.guess_location_distance_meter, (int) distanceFromPicture);
        if (distanceFromPicture > 10000) {
            dText = getString(R.string.guess_location_distance_kilometer,(int) distanceFromPicture / 1000);
        }

        double score = Score.calculationScore(distanceFromPicture, user.getRadius() * 1000, user.getRadius());
        TextView scoreText = findViewById(R.id.scoreText);
        return getString(R.string.score, (int) score) + "\n" + dText;
    }

    /**
     * Start the ScoreBoard activity
     */
    private void startScoreBoard() {
        Intent intent = new Intent(this, ScoreboardActivity.class);
        intent.putExtra(ScoreboardActivity.EXTRA_PICTURE_ID,picToGuess.getUniqueId());
        startActivity(intent);
    }

    //Necessary overwrites for MapView lifecycle methods
    /**
     * start mapbox
     */
    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    /**
     * resume mapbox
     */
    @Override
    protected void onResume() {
        super.onResume();
        mapView.onResume();
    }

    /**
     * pause mapbox
     */
    @Override
    protected void onPause() {
        super.onPause();
        mapView.onPause();
    }

    /**
     * stop mapbox
     */
    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    /**
     * save mapbox instance state
     * @param outState output
     */
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    /**
     * when memory is low
     */
    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    /**
     * on mapbox destruction
     */
    @Override
    protected void onDestroy() {
        //unregister listener!
        sensorManager.unregisterListener(listener);
        super.onDestroy();
        mapView.onDestroy();
        gpsTimer.cancel();
    }
}