com.pokescanner.MapsActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.pokescanner.MapsActivity.java

Source

/*
 *
 *     This program 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
 *     (at your option) any later version.
 *
 *     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.pokescanner;

import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.multidex.MultiDex;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.pokescanner.events.ForceRefreshEvent;
import com.pokescanner.events.PublishProgressEvent;
import com.pokescanner.events.RestartRefreshEvent;
import com.pokescanner.helper.PokemonListLoader;
import com.pokescanner.helper.Settings;
import com.pokescanner.loaders.MapObjectsLoader;
import com.pokescanner.objects.FilterItem;
import com.pokescanner.objects.Gym;
import com.pokescanner.objects.PokeStop;
import com.pokescanner.objects.Pokemons;
import com.pokescanner.objects.User;
import io.realm.Realm;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.joda.time.DateTime;
import org.joda.time.Instant;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;

import static com.pokescanner.helper.Generation.getCorners;
import static com.pokescanner.helper.Generation.hexagonal_number;
import static com.pokescanner.helper.Generation.makeHexScanMap;

public class MapsActivity extends AppCompatActivity
        implements OnMapReadyCallback, GoogleMap.OnCameraChangeListener {

    FloatingActionButton button;
    ProgressBar progressBar;
    private GoogleMap mMap;
    Toolbar toolbar;

    LocationManager locationManager;
    Location currentLocation;

    User user;
    Realm realm;

    List<LatLng> scanMap = new ArrayList<>();
    ArrayList<FilterItem> filterItems = new ArrayList<>();

    private ArrayList<Marker> pokeMarkers = new ArrayList<>();
    private ArrayList<Marker> locationMarkers = new ArrayList<>();
    Circle mBoundingBox = null;

    PokemonListLoader pokemonListLoader;
    SharedPreferences sharedPreferences;
    private MapObjectsLoader mapObjectsLoader;

    int pos = 1;
    //Used for determining Scan status
    boolean SCANNING_STATUS = false;
    //Default size for our scan grid
    int scanValue = 5;
    //Used for our refreshing of the map
    Subscription pokeonRefresher, gymstopRefresher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MultiDex.install(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        realm = Realm.getDefaultInstance();

        //So if our realm has no users then we'll send our user back to the login screen
        //otherwise set our user and move on!
        if (realm.where(User.class).findAll().size() != 0) {
            user = realm.copyFromRealm(realm.where(User.class).findFirst());
        } else {
            Toast.makeText(MapsActivity.this, "No login!", Toast.LENGTH_SHORT).show();
            logOut();
        }

        //Load our shared prefs for our scan value
        sharedPreferences = getSharedPreferences(getString(R.string.shared_key), Context.MODE_PRIVATE);

        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        //Start our location manager so we can center our map
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        //This class is used to load and save our filters
        pokemonListLoader = new PokemonListLoader(this);

        try {
            //let's try and load our filters
            filterItems.addAll(pokemonListLoader.getPokelist());
        } catch (IOException e) {
            showToast(R.string.ERROR_FILTERS);
            e.printStackTrace();
        }

        button = (FloatingActionButton) findViewById(R.id.btnSearch);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        toolbar = (Toolbar) findViewById(R.id.toolbar);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PokeScan();
            }
        });
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle("");
        toolbar.setOverflowIcon(ContextCompat.getDrawable(MapsActivity.this, R.drawable.ic_settings_black_24dp));

        // Point the map's listeners at the listeners implemented by the cluster
        // manager.
    }

    public void PokeScan() {
        if (SCANNING_STATUS) {
            stopPokeScan();
        } else {
            pos = 1;
            //Load our scan value
            scanValue = sharedPreferences.getInt("scanvalue", 5);
            System.out.println(scanValue);
            //Our refresh rate to Milliseconds
            int millis = SettingsController.getSettings(this).getServerRefresh() * 1000;
            showProgressbar(true);
            progressBar.setProgress(0);
            scanMap = makeHexScanMap(mMap.getCameraPosition().target, scanValue, 1, new ArrayList<LatLng>());
            mapObjectsLoader = new MapObjectsLoader(user, scanMap, millis, this);
            mapObjectsLoader.start();
        }
    }

    private void stopPokeScan() {
        try {
            mapObjectsLoader.interrupt();
            mapObjectsLoader.join(500);
            showProgressbar(false);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void showToast(int resString) {
        Toast.makeText(MapsActivity.this, getString(resString), Toast.LENGTH_SHORT).show();
    }

    public void createBoundingBox() {
        if (scanMap.size() > 0) {
            if (mBoundingBox != null)
                mBoundingBox.remove();
            //To create a circle we need to get the corners
            List<LatLng> corners = getCorners(scanMap);
            //Once we have the corners lets create two locations
            Location location = new Location("");
            //set the latitude/longitude
            location.setLatitude(corners.get(0).latitude);
            location.setLongitude(corners.get(0).longitude);

            Location location1 = new Location("");
            //set the laditude/longitude
            location1.setLatitude(scanMap.get(0).latitude);
            location1.setLongitude(scanMap.get(0).longitude);

            float distance = location.distanceTo(location1);

            mBoundingBox = mMap.addCircle(new CircleOptions().center(scanMap.get(0)).radius(distance));
            //mMap.addPolygon(new PolygonOptions().addAll(getCorners(scanMap)));
        }
    }

    public void createMarkerList() {
        if (BuildConfig.DEBUG) {
            for (LatLng temp : scanMap) {
                mMap.addMarker(new MarkerOptions().position(temp));
            }
        }
    }

    @Override
    @SuppressWarnings({ "MissingPermission" })
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        if (doWeHavePermission()) {
            //Set our map stuff
            mMap.setMyLocationEnabled(true);
            mMap.setOnCameraChangeListener(this);
            //Add padding for map buttons (ex. my location button) as we have Toolbar at top
            mMap.setPadding(0, 0, com.pokescanner.helper.DrawableUtils.convertToPixels(MapsActivity.this, 36), 0);
            //Let's find our location and set it!
            Criteria criteria = new Criteria();
            String provider = locationManager.getBestProvider(criteria, true);
            currentLocation = locationManager.getLastKnownLocation(provider);

            //Center camera function
            centerCamera();
            startRefresher();
        }
    }

    public void centerCamera() {
        if (currentLocation != null && doWeHavePermission()) {
            LatLng target = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
            CameraPosition position = this.mMap.getCameraPosition();

            CameraPosition.Builder builder = new CameraPosition.Builder();
            builder.zoom(15);
            builder.target(target);

            this.mMap.animateCamera(CameraUpdateFactory.newCameraPosition(builder.build()));
        }
    }

    public void showProgressbar(boolean status) {
        if (status) {
            progressBar.setVisibility(View.VISIBLE);
            button.setImageDrawable(ContextCompat.getDrawable(MapsActivity.this, R.drawable.ic_pause_white_24dp));
            SCANNING_STATUS = true;
        } else {
            progressBar.setVisibility(View.INVISIBLE);
            button.setImageDrawable(
                    ContextCompat.getDrawable(MapsActivity.this, R.drawable.ic_track_changes_white_24dp));
            SCANNING_STATUS = false;
        }
    }

    //I don't think we're using this
    public boolean isGPSEnabled() {
        LocationManager cm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        return cm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }

    public void searchRadiusDialog() {
        scanValue = sharedPreferences.getInt("scanvalue", 5);

        final Dialog dialog = new Dialog(this);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.dialog_search_radius);

        final SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekBar);
        Button btnSave = (Button) dialog.findViewById(R.id.btnAccept);
        Button btnCancel = (Button) dialog.findViewById(R.id.btnCancel);
        final TextView tvNumber = (TextView) dialog.findViewById(R.id.tvNumber);
        final TextView tvEstimate = (TextView) dialog.findViewById(R.id.tvEstimate);
        tvNumber.setText(String.valueOf(scanValue));
        tvEstimate.setText(getString(R.string.timeEstimate) + " " + getTimeEstimate(scanValue));
        seekBar.setProgress(scanValue);
        seekBar.setMax(12);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                tvNumber.setText(String.valueOf(i));
                tvEstimate.setText(getString(R.string.timeEstimate) + " " + getTimeEstimate(i));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int saveValue = seekBar.getProgress();
                if (saveValue == 0) {
                    //We don't want a value of 0, No one likes 0 :{
                    scanValue = 1;
                } else {
                    scanValue = saveValue;
                }
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putInt("scanvalue", saveValue);
                editor.apply();
                dialog.dismiss();
            }
        });

        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.dismiss();
            }
        });
        dialog.show();
    }

    public String getTimeEstimate(int val) {
        int calculatedValue = hexagonal_number(val) * SettingsController.getSettings(this).getServerRefresh()
                * 1000;
        long millis = calculatedValue;
        DateTime dt = new DateTime(millis);
        DateTimeFormatter fmt = DateTimeFormat.forPattern("mm:ss");
        return fmt.print(dt);
    }

    public void startDialogActivity() {
        Intent filterIntent = new Intent(MapsActivity.this, FilterActivity.class);
        startActivity(filterIntent);
    }

    public void reloadFilters() {
        try {
            filterItems.clear();
            filterItems.addAll(pokemonListLoader.getPokelist());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void logOut() {
        pokeonRefresher.unsubscribe();
        gymstopRefresher.unsubscribe();

        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.where(User.class).findAll().deleteAllFromRealm();
                Intent intent = new Intent(MapsActivity.this, LoginActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                finish();
            }
        });
    }

    //Map related Functions
    public void refreshMap() {
        LatLngBounds curScreen = mMap.getProjection().getVisibleRegion().latLngBounds;

        createMapObjects();

        //Before we refresh we want to remove the old markets so lets do that first
        for (Marker marker : pokeMarkers) {
            marker.remove();
        }
        //Clear our array
        pokeMarkers.clear();

        //load our array
        ArrayList<Pokemons> pokemons = new ArrayList<Pokemons>(
                realm.copyFromRealm(realm.where(Pokemons.class).findAll()));

        //get our icon scale from our settings
        int scale = SettingsController.getSettings(this).getScale();

        for (int i = 0; i < pokemons.size(); i++) {
            Pokemons pokemon = pokemons.get(i);
            //If our pokemon is contained within the bounds of the map then lets render him!
            if (curScreen.contains(new LatLng(pokemon.getLatitude(), pokemon.getLongitude()))) {
                //Has our pokemon expired?
                if (pokemon.getDate().isAfter(new Instant())) {
                    //And is he filtered?
                    if (realm.copyFromRealm(
                            realm.where(FilterItem.class).equalTo("Number", pokemon.getNumber()).findFirst())
                            .isFiltered()) {
                        //INTENTIONALLY LEFT EMPTY
                    } else {
                        //Render him!
                        pokeMarkers.add(mMap.addMarker(pokemon.getMarker(this, scale)));
                    }
                } else {
                    //If he has expired lets purge him from the database
                    realm.beginTransaction();
                    realm.where(Pokemons.class).equalTo("encounterid", pokemon.getEncounterid()).findAll()
                            .deleteAllFromRealm();
                    realm.commitTransaction();
                }
            }
        }

    }

    public void refreshGyms() {
        //The the map bounds
        LatLngBounds curScreen = mMap.getProjection().getVisibleRegion().latLngBounds;

        //Before we refresh we want to remove the old markets so lets do that first
        for (Marker marker : locationMarkers) {
            marker.remove();
        }
        //Clear our array
        locationMarkers.clear();
        //Once we refresh our markers lets go ahead and load our pokemans
        ArrayList<Gym> gyms = new ArrayList<Gym>(realm.copyFromRealm(realm.where(Gym.class).findAll()));
        ArrayList<PokeStop> pokestops = new ArrayList<PokeStop>(
                realm.copyFromRealm(realm.where(PokeStop.class).findAll()));

        for (int i = 0; i < gyms.size(); i++) {
            Gym gym = gyms.get(i);
            LatLng pos = new LatLng(gym.getLatitude(), gym.getLongitude());
            if (curScreen.contains(pos)) {
                locationMarkers.add(mMap.addMarker(gym.getMarker(this)));
            }
        }

        boolean showAllStops = !Settings.get(this).isShowOnlyLured();

        for (int i = 0; i < pokestops.size(); i++) {
            PokeStop pokestop = pokestops.get(i);
            LatLng pos = new LatLng(pokestop.getLatitude(), pokestop.getLongitude());
            if (curScreen.contains(pos)) {
                if (pokestop.isHasLureInfo() || showAllStops) {
                    locationMarkers.add(mMap.addMarker(pokestop.getMarker(this)));
                }
            }
        }
    }

    public void createMapObjects() {
        if (SettingsController.getSettings(this).isBoundingBoxEnabled()) {
            createBoundingBox();
        }
        //createMarkerList();
    }

    public void startRefresher() {
        if (pokeonRefresher != null)
            pokeonRefresher.unsubscribe();
        if (gymstopRefresher != null)
            gymstopRefresher.unsubscribe();

        //Using RX java we setup an interval to refresh the map
        pokeonRefresher = Observable.interval(SettingsController.getSettings(this).getMapRefresh(),
                TimeUnit.SECONDS, AndroidSchedulers.mainThread()).subscribe(new Action1<Long>() {
                    @Override
                    public void call(Long aLong) {
                        //System.out.println("Refreshing Pokemons");
                        refreshMap();
                    }
                });

        gymstopRefresher = Observable.interval(30, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
                .subscribe(new Action1<Long>() {
                    @Override
                    public void call(Long aLong) {
                        //System.out.println("Refreshing Gyms");
                        refreshGyms();
                    }
                });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void forceRefreshEvent(ForceRefreshEvent event) {
        refreshGyms();
        refreshMap();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onRestartRefreshEvent(RestartRefreshEvent event) {
        startRefresher();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPublishProgressEvent(PublishProgressEvent event) {
        if (event.getProgress() != -1) {
            float progress = (float) event.getProgress() * 100 / scanMap.size();
            progressBar.setProgress((int) progress);
            if (Math.round(progress) == scanMap.size()) {
                showProgressbar(false);
            }
        }
    }

    public boolean doWeHavePermission() {
        return ContextCompat.checkSelfPermission(this,
                android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    protected void onResume() {
        super.onResume();
        realm = Realm.getDefaultInstance();
        reloadFilters();
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        EventBus.getDefault().unregister(this);
        super.onStop();
    }

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

    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(android.view.MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            onBackPressed();
            break;
        case R.id.action_search_radius:
            searchRadiusDialog();
            break;
        case R.id.action_filter:
            startDialogActivity();
            break;
        case R.id.action_settings:
            SettingsController.showSettingDialog(MapsActivity.this);
            break;
        case R.id.action_logout:
            logOut();
            break;
        case R.id.action_donate:
            Uri uri = Uri.parse("https://www.paypal.me/brianestrada"); // missing 'http://' will cause crashed
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            startActivity(intent);
            break;
        default:
            break;
        }
        return true;
    }
}