com.genesys.pokemaps.MapActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.genesys.pokemaps.MapActivity.java

Source

/*
 * Copyright (C) 2016 Primed
 *
 * 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.genesys.pokemaps;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;

import com.genesys.pokemaps.helpers.GameManager;
import com.genesys.pokemaps.helpers.LocationManager;
import com.genesys.pokemaps.helpers.LocationManager.Listener;
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.pokegoapi.api.map.fort.PokestopLootResult;
import com.pokegoapi.api.map.pokemon.NearbyPokemon;
import com.pokegoapi.api.pokemon.PokemonMetaRegistry;
import com.pokegoapi.exceptions.AsyncPokemonGoException;
import com.pokegoapi.exceptions.LoginFailedException;
import com.pokegoapi.exceptions.NoSuchItemException;
import com.pokegoapi.exceptions.RemoteServerException;
import com.pokegoapi.util.PokeDictionary;

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

public class MapActivity extends AppCompatActivity
        implements OnMapReadyCallback, Listener, GameManager.LoginListener {

    /**
     * The name of this class for use in debugging purposes.
     */
    private static final String TAG = "MapActivity";
    /**
     * Radius in which our current position can access things in meters
     * This is defined by the Pokemon GO settings; this is simply
     * for the on-screen indicator.
     */
    private static final long SCAN_RADIUS = 70;

    /**
     * The default level of zoom when the map initially launches. This is also the level of zoom
     * the camera snaps to when the locate button is clicked.
     */
    private static final int DEFAULT_ZOOM = 17;

    /**
     * The rate at which game components update in milliseconds.
     */
    private static final long GAME_REFRESH_RATE = 3000;

    /**
     * Our Google Map object. We can use this to manipulate various map options.
     */
    protected GoogleMap mMap;

    /**
     * The level of zoom the map is currently set to.
     */
    private int currentZoom = DEFAULT_ZOOM;

    /**
     * Our Pokemon GO game manager. This object handles all the heavy stuff for us.
     */
    private GameManager gameManager;

    /**
     * This is the container for our MapActivity views. It's really only used for making
     * SnackBars.
     */
    private CoordinatorLayout mapViewGroup;

    /**
     * This is the container for the nearby pokemon.
     */
    private GridLayout nearbyContainer;

    /**
     * The text that is displayed when no pokemon are nearby.
     */
    private TextView nearbyTextView;

    /**
     * Our location manager. It get our location for us and handles automatic location updates.
     */
    private LocationManager locationManager;

    /**
     * Our current location. Pretty self-explanatory.
     */
    private Location location;

    /**
     * The same as the above mentioned location object, only converted to latiude and longitude.
     */
    private LatLng position;

    /**
     * This object contains relevant data for a circle that is displayed on our map at our location.
     * It indicates the radius in which our Pokemon GO character can interact with other elements.
     */
    private Circle scanCircle;

    /**
     * Stays true until the first time it's been accessed, the becomes false. This creates a branch
     * that allows only a single passthrough.
     */
    private boolean firstLocationFlag = true;

    /**
     * Our preferences object. Do I even have to explain this one?
     */
    private SharedPreferences preferences;

    /**
     * Our System vibrator object.
     */
    private Vibrator vibrator;

    /* Overridden parent methods */

    /**
     * The first method that is called when this activity starts.
     *
     * @param savedInstanceState The instances that were saved on onStop().
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);

        // Here's where we set up our toolbar. This subsequently calls onCreateOptionsMenu().
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // 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);

        // Get our preferences.
        preferences = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE);

        // Set up our vibrator.
        vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

        setupGameManager();

        // @layout/activity_map views.
        mapViewGroup = (CoordinatorLayout) findViewById(R.id.map_viewgroup);
        nearbyContainer = (GridLayout) findViewById(R.id.nearby_container);
        nearbyTextView = (TextView) nearbyContainer.findViewById(R.id.no_nearby_text_view);

        findViewById(R.id.plus_fab).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mMap != null) {
                    mMap.animateCamera(CameraUpdateFactory.zoomIn());
                }
            }
        });

        findViewById(R.id.minus_fab).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mMap != null) {
                    mMap.animateCamera(CameraUpdateFactory.zoomOut());
                }
                gameManager.farmXP();
            }
        });

        setupLocationManager();

        startPokemoonLoop();
    }

    /**
     * Gets our game manager instance and sets the login completed listener.
     */
    private void setupGameManager() {
        gameManager = GameManager.getInstance(this);
        gameManager.setOnLoginCompletedListener(new GameManager.LoginListener() {
            @Override
            public void onLoginCompleted(GameManager.LoginResult loginResult) {
                // TODO: Do something when login is completed.
                Utils.debug(this, "Login completed on MapActivity.");
            }
        });
    }

    /**
     * Gets our location manager instance and registers this object as a listener.
     */
    private void setupLocationManager() {
        locationManager = LocationManager.getInstance(this);
        locationManager.register(this);
    }

    public void startPokemoonLoop() {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if (gameManager != null && (location != null)) {
                    long startTime = System.currentTimeMillis();
                    try {
                        // Updates our game location to match our real location.
                        gameManager.setPlayerLocation(location);

                        // Update our pokestops and add a marker on our map at its location.
                        // TODO: Add a map marker for pokestops.
                        // gameManager.updatePokestops();

                        // Cycles through our pokestops and loots them if the option is available.
                        for (PokestopLootResult lootResult : gameManager.lootPokestops()) {
                            switch (lootResult.getResult()) {
                            case SUCCESS:
                                showSnackBar("Pokestop was successfully looted. Gained "
                                        + lootResult.getExperience() + " XP");
                                Log.i(TAG, "Pokestop was successfully looted. Gained" + lootResult.getExperience()
                                        + " XP and " + lootResult.getItemsAwarded().size() + " items.");
                                Utils.vibrate(Constants.POKESTOP_VIBRATION_PATTERN, vibrator);
                                break;
                            case INVENTORY_FULL:
                                String invMsg = "Inventory too full to loot Pokestop";
                                showSnackBar(invMsg);
                                Log.i(TAG, invMsg);
                                break;
                            case IN_COOLDOWN_PERIOD:
                                String coolMsg = "Pokestop is currently in cooldown";
                                showSnackBar(coolMsg);
                                Log.i(TAG, coolMsg);
                                break;
                            default:
                                String errMsg = "Couldn't loot pokestop due to error "
                                        + lootResult.getResult().name();
                                showSnackBar(errMsg);
                                Log.i(TAG, errMsg);
                            }
                        }

                        final List<NearbyPokemon> nearbyPokemon = gameManager.getNearbyPokemon();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                for (int i = 0; i < nearbyContainer.getChildCount(); i++) {
                                    View child = nearbyContainer.getChildAt(i);
                                    if (child.getId() != R.id.no_nearby_text_view
                                            && child.getParent() == nearbyContainer) {
                                        nearbyContainer.removeViewAt(i--);
                                    }
                                }

                                if (nearbyPokemon.size() > 0) {
                                    // There are nearby Pokemon!
                                    // Set our TextView visibility to gone.
                                    nearbyTextView.setVisibility(View.GONE);
                                    for (NearbyPokemon pokemon : nearbyPokemon) {
                                        Log.i(TAG, pokemon.getPokemonId().name() + " is nearby.");

                                        ViewGroup nearbyPokemonLayout = (ViewGroup) getLayoutInflater()
                                                .inflate(R.layout.nearby_pokemon, null);

                                        ImageView pokemonImage = (ImageView) nearbyPokemonLayout.getChildAt(1);
                                        ImageView pokemonBackground = (ImageView) nearbyPokemonLayout.getChildAt(0);

                                        int id = getResources().getIdentifier(
                                                pokemon.getPokemonId().name().toLowerCase(), "drawable",
                                                getPackageName());
                                        pokemonImage.setImageResource(id);

                                        int backgroundColor;
                                        switch (PokemonMetaRegistry.getMeta(pokemon.getPokemonId())
                                                .getPokemonClass()) {
                                        case VERY_COMMON:
                                            backgroundColor = Utils.getColor(MapActivity.this,
                                                    R.color.veryCommonColor);
                                            break;
                                        case COMMON:
                                            backgroundColor = Utils.getColor(MapActivity.this, R.color.commonColor);
                                            break;
                                        case UNCOMMON:
                                            backgroundColor = Utils.getColor(MapActivity.this,
                                                    R.color.uncommonColor);
                                            break;
                                        case RARE:
                                            backgroundColor = Utils.getColor(MapActivity.this, R.color.rareColor);
                                            break;
                                        case VERY_RARE:
                                            backgroundColor = Utils.getColor(MapActivity.this,
                                                    R.color.veryRareColor);
                                            break;
                                        case EPIC:
                                            backgroundColor = Utils.getColor(MapActivity.this, R.color.epicColor);
                                            break;
                                        case LEGENDARY:
                                            backgroundColor = Utils.getColor(MapActivity.this,
                                                    R.color.legendaryColor);
                                            break;
                                        case MYTHIC:
                                            backgroundColor = Utils.getColor(MapActivity.this, R.color.mythicColor);
                                            break;
                                        default:
                                            backgroundColor = Color.WHITE;
                                        }

                                        pokemonBackground.setColorFilter(backgroundColor);

                                        nearbyContainer.addView(nearbyPokemonLayout);
                                    }
                                } else {
                                    nearbyTextView.setVisibility(View.VISIBLE);
                                }
                            }
                        });

                        // Update our pokemon and add a marker on the map at its location.
                        // TODO: Add a map marker for catchable pokemon.
                        //gameManager.updateCatchablePokemon();

                        GameManager.Catch catchResult = gameManager.catchPokemon();
                        if (catchResult != null) {
                            String pokemon = PokeDictionary.getDisplayName(
                                    catchResult.getCatchablePokemon().getPokemonId().getNumber(), Locale.ENGLISH);
                            String message;
                            switch (catchResult.getCatchResult().getStatus()) {
                            case CATCH_SUCCESS:
                                message = pokemon + " successfully captured";
                                Utils.vibrate(Constants.POKEMON_VIBRATION_PATTERN, vibrator);
                                break;
                            case CATCH_FLEE:
                                message = pokemon + " fled";
                                break;
                            case CATCH_MISSED:
                                message = pokemon + " missed";
                                break;
                            default:
                                message = "Unable to catch " + pokemon;
                            }
                            showSnackBar(message);
                            Log.i(TAG, message);
                        }

                        gameManager.updateGyms();

                    } catch (LoginFailedException e) {
                        showSnackBar("Login failed. Credentials changed");
                    } catch (RemoteServerException e) {
                        showSnackBar("Login failed. Servers may be down");
                    } catch (NoSuchItemException e) {
                        showSnackBar("Not enough pokeballs to catch pokemon");
                    } catch (AsyncPokemonGoException e) {
                        String username = preferences.getString(getString(R.string.username_preference_key), null);
                        String password = preferences.getString(getString(R.string.password_preference_key), null);
                        if (username != null || password != null) {
                            gameManager.loginPTC(username, password);
                        }
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    double scanTimeSeconds = (double) (System.currentTimeMillis() - startTime) / 1000;
                    Log.i(TAG, "World scanning completed in " + scanTimeSeconds + " seconds.");
                }
            }
        }, 0, GAME_REFRESH_RATE);
    }

    /**
     * This method inflates our options menu into our toolbar options menu from our menu xml.
     *
     * @param menu The menu to be inflated.
     * @return Whether or not the menu was inflated successfully.
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_map, menu);
        return true;
    }

    /**
     * This method handles options menu clicks. Any desired menu action should be defined here.
     *
     * @param item The menu item that was selected
     * @return Whether or not action click was successful.
     */
    @SuppressLint("CommitPrefEdits")
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            return true;
        } else if (id == R.id.action_sign_out) {
            // Clear our login data and all preferences.
            preferences.edit().clear().commit();
            // Start the main Login activity.
            Intent loginIntent = new Intent(this, LoginActivity.class);
            startActivity(loginIntent);
            // Finish this activity.
            finish();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * Calls after onCreate(), when the Activity starts.
     */
    @Override
    public void onStart() {
        locationManager.onStart();
        super.onStart();
    }

    /**
     * Calls when the activity
     */
    @Override
    public void onStop() {
        locationManager.onStop();
        super.onStop();
    }

    /* Overridden methods from OnMapReadyCallback */

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Enable map location features such as the 'show location' button.
        if (ActivityCompat.checkSelfPermission(this,
                android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            mMap.setMyLocationEnabled(true);
            mMap.getUiSettings().setMyLocationButtonEnabled(false);
        }

        mMap.getUiSettings().setScrollGesturesEnabled(false);
        mMap.getUiSettings().setZoomGesturesEnabled(false);
        mMap.getUiSettings().setCompassEnabled(false);
    }

    /**
     * Updates our scan circle to match our location
     */
    public void updateScanCircle() {
        scanCircle.setCenter(position);
    }

    /**
     * Updates the camera position to match our location
     */
    public void updateCameraLocation() {
        mMap.animateCamera(CameraUpdateFactory
                .newCameraPosition(new CameraPosition.Builder(mMap.getCameraPosition()).target(position).build()));
    }

    /**
     * This method runs when the location is received for the first time.
     */
    public void setupCamera() {
        // We might as well zoom to our location for the first time.
        mMap.animateCamera(
                CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(mMap.getCameraPosition())
                        .target(position).zoom(currentZoom).tilt(30).build()),
                1, null);

        // Also, let's add a little circle scan thing. I don't know what else to call it.
        // It's a circle that shows how far out the scan goes, so henceforth it will be known
        // as circle scan thing.
        scanCircle = mMap.addCircle(new CircleOptions().center(position).radius(SCAN_RADIUS).strokeWidth(4)
                .strokeColor(Utils.getColor(this, R.color.colorPrimary))
                .fillColor(Utils.getColorWithAlpha(Utils.getColor(this, R.color.colorPrimary), 0.3f)));
    }

    /**
     * Handy method that shows a snack bar.
     *
     * @param msg The message that is displayed in the snack bar.
     */
    public void showSnackBar(String msg) {
        Snackbar.make(mapViewGroup, msg, Snackbar.LENGTH_LONG).show();
    }

    /**
     * This method is called every time we receive a location update from our location manager.
     *
     * @param location Updated location.
     */
    @Override
    public void onLocationChanged(Location location) {
        // We can't do anything if the location is null, therefore we'll return.
        if (location == null)
            return;

        // Update our location variables.
        this.location = location;
        position = new LatLng(location.getLatitude(), location.getLongitude());

        if (firstLocationFlag) {
            // This is the first time we've gotten a location update. From here, we'll set up our
            // map indicators.
            setupCamera();
            firstLocationFlag = false;
        } else {
            updateScanCircle();
            updateCameraLocation();
        }

        // Utils.debug(this, "Location updated! " + position.latitude + ", " + position.longitude);
    }

    /**
     * This method is run whenever the user logs in from this method.
     *
     * @param loginResult The login result.
     */
    @Override
    public void onLoginCompleted(GameManager.LoginResult loginResult) {
        showSnackBar(loginResult.getMessage());
    }
}