pw.dedominic.csc311_final_project.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for pw.dedominic.csc311_final_project.MainActivity.java

Source

/*
 * Copyright (c) 2015. Anthony DeDominic
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pw.dedominic.csc311_final_project;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.Collections;
import java.util.Vector;

/**
 * The entry point of the app.
 */
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
    /** list of players in a list view */
    private ArrayAdapter<String> PLAYER_LIST;
    private ListView mListView;

    private TextView mTextView;

    /** map view of nearby points */
    private MapView mMapView;

    /** Various Handlers for input output of network services */
    private HttpHandler mHttpHandler = new HttpHandler();
    private HttpGetHandler mHttpGetHandler = new HttpGetHandler();
    private UploadLocation mUploadLocation = new UploadLocation();
    private BluetoothListener mBluetoothListener = new BluetoothListener();
    private InNodeTimer mInNodeTimer = new InNodeTimer();

    private HttpService mHttpService = new HttpService(mHttpHandler);

    // -999 means uninitialized
    private double PLAYER_LATITUDE = -999;
    private double PLAYER_LONGITUDE = -999;
    private String PROVIDER;

    private String USER_NAME;
    private String TEAM_NAME;
    private String MAC_ADDR;

    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothService mBluetoothService;

    // location services
    LocationManager mLocationManager;

    // locationListener
    LocationListener mLocationListener;

    // Determines if game is ready to run
    private boolean IS_READY = false;

    // current node player is in
    private int CURRENT_NODE = -999;
    private String CURRENT_NODE_OWNERSHIP = "NOT_OWNED";
    private int COUNTDOWN_TIME = -99;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        MAC_ADDR = mBluetoothAdapter.getAddress();

        // ready GPS unit
        mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        mLocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                PLAYER_LATITUDE = location.getLatitude();
                PLAYER_LONGITUDE = location.getLongitude();
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {

            }

            @Override
            public void onProviderEnabled(String provider) {

            }

            @Override
            public void onProviderDisabled(String provider) {

            }
        };
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
        if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
            mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocationListener);
            PROVIDER = LocationManager.NETWORK_PROVIDER;
        } else {
            PROVIDER = LocationManager.GPS_PROVIDER;
        }

        mHttpService.recreateGetUserNameTask();
        mHttpService.getUserName(MAC_ADDR);
    }

    /**
     * Pertains to List View's array adapter PLAYER_LIST.
     * Clicking on a user's name will initiate a battle with said
     * player if a bluetooth connection can be made
     *
     * @param adapterView parent view
     * @param view the entry in the list view clicked
     * @param arg2 not used, required
     * @param arg3 not used, required
     */
    public void onItemClick(AdapterView<?> adapterView, View view, int arg2, long arg3) {
        String view_string = ((TextView) view).getText().toString();

        String MAC_ADDRESS = view_string.substring(view_string.length() - 17);
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(MAC_ADDRESS);
        mBluetoothService.join(device);

        Intent intent = new Intent(this, BattleActivity.class);
        intent.putExtra(Constants.INTENT_MAC_ADDRESS, MAC_ADDRESS);

        startActivity(intent);
    }

    /** If user is challenged, this triggers the battle sequence */
    public void connectionStarted() {
        Intent intent = new Intent(this, BattleActivity.class);
        intent.putExtra(Constants.INTENT_MAC_ADDRESS, mBluetoothService.getConnectedAddress());
        mBluetoothService.killAll();
        startActivity(intent);
    }

    /**
     * When Login activity finishes, this method is called
     *
     * @param req_code code the activity was called with.
     * @param result_code if the result was a success or a failure
     * @param data includes any values pertaining to the request.
     */
    public void onActivityResult(int req_code, int result_code, Intent data) {
        if (result_code == Activity.RESULT_OK) {
            USER_NAME = data.getStringExtra(Constants.INTENT_USER_NAME_KEY);
            TEAM_NAME = data.getStringExtra(Constants.INTENT_TEAM_KEY);
            initializeViews();
        }
    }

    /**
     * This function is called after Login View finishes.
     * This function builds the UI views that make up this activity.
     */
    public void initializeViews() {
        // list view init
        PLAYER_LIST = new ArrayAdapter<String>(this, R.layout.activity_text);
        mListView = (ListView) findViewById(R.id.listView);
        mListView.setAdapter(PLAYER_LIST);
        mListView.setOnItemClickListener(this);

        mTextView = (TextView) findViewById(R.id.textView);

        // map view init
        mMapView = (MapView) findViewById(R.id.mapView);
        mMapView.update_map();

        // get data now
        mHttpGetHandler.handleMessage(Message.obtain());
        mUploadLocation.handleMessage(Message.obtain());

        mBluetoothService = new BluetoothService(mBluetoothListener);
        mBluetoothService.listen();

        mInNodeTimer.handleMessage(Message.obtain());
    }

    /**
     * Haversine formula to get distances of two points.
     * Points are given in Latitude and Longitude.
     *
     * @param lat1 latitude of point one
     * @param lon1 longitude of point one
     * @param lat2 latitude of point two
     * @param lon2 longitude of point two
     */
    private double getDistance(double lat1, double lat2, double lon1, double lon2) {
        double[] lat_rads = new double[2];
        lat_rads[0] = lat1 * Math.PI / 180;
        lat_rads[1] = lat2 * Math.PI / 180;

        double delta_lat = (lat2 - lat1) * Math.PI / 180;
        double delta_lon = (lon2 - lon1) * Math.PI / 180;

        double a = Math.pow(Math.sin(delta_lat / 2), 2)
                + Math.cos(lat_rads[0]) * Math.cos(lat_rads[1]) * Math.pow(Math.sin(delta_lon / 2), 2);

        double b = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return ((double) Constants.APPROX_RAD_EARTH) * b;
    }

    private void processLogin(String username) {
        if (username.equals("User does not exist")) {
            Intent intent = new Intent(this, LoginActivity.class);
            startActivityForResult(intent, 0);
            return;
        }

        String[] split_username_team = username.split(",");
        USER_NAME = split_username_team[0];
        TEAM_NAME = split_username_team[1];
        initializeViews();
    }

    /**
     * Function that takes CSV from HttpService and converts it to, sorted, meaningful data
     */
    private void processCSV(String raw_data) {
        // split by line
        String[] CSV = raw_data.split("\n");

        // resizeable array, sortable
        Vector<CSVData> Strings = new Vector<>();

        for (String string : CSV) {
            CSVData entry = new CSVData();
            String[] split_string = string.split(",");
            entry.username = split_string[0];
            entry.teamname = split_string[1];
            entry.latitude = Double.parseDouble(split_string[2]);
            entry.longitude = Double.parseDouble(split_string[3]);
            entry.distance = getDistance(entry.latitude, PLAYER_LATITUDE, entry.longitude, PLAYER_LONGITUDE);
            entry.MAC_ADDR = split_string[4];

            Strings.add(entry);
        }

        // sorts by distance
        Collections.sort(Strings);

        PLAYER_LIST.clear(); // remove the old
        mMapView.clear_map();
        mMapView.setCenterPoint(PLAYER_LATITUDE, PLAYER_LONGITUDE);
        for (CSVData entry : Strings) {
            PLAYER_LIST.add(entry.username + "\n" + Double.toString(entry.distance) + "\n" + entry.MAC_ADDR);

            int color;
            if (entry.teamname.equals(TEAM_NAME)) {
                color = 0xAA00FF00;
            } else {
                color = 0xAAFF0000;
            }

            mMapView.addGeoPoint(entry.latitude, entry.longitude, color, true);
        }
        mMapView.update_map();
    }

    private void processNodeList(String nodes) {
        // strings indexed by line
        String[] CSV = nodes.split("\n");

        for (String string : CSV) {
            CSVData entry = new CSVData();
            String[] values = string.split(",");
            entry.nodeID = Integer.parseInt(values[0]);
            entry.teamname = values[3];
            entry.latitude = Double.parseDouble(values[1]);
            entry.longitude = Double.parseDouble(values[2]);
            entry.distance = getDistance(entry.latitude, PLAYER_LATITUDE, entry.longitude, PLAYER_LONGITUDE);

            int color;
            if (entry.teamname.equals("NOT_OWNED")) {
                color = 0xAA000000;
            } else if (entry.teamname.equals(TEAM_NAME)) {
                color = 0xAA00FF00;
            } else {
                color = 0xAAFF0000;
            }

            mMapView.addGeoPoint(entry.latitude, entry.longitude, color, false);

            if (entry.distance <= 250) {
                CURRENT_NODE = entry.nodeID;
                CURRENT_NODE_OWNERSHIP = entry.teamname;
            } else if (entry.nodeID == CURRENT_NODE) {
                CURRENT_NODE = -999;
                Toast.makeText(getApplicationContext(), "Exiting Node...", Toast.LENGTH_LONG).show();
            }
        }

        mMapView.update_map();
    }

    /**
     * processes a string which contains a message to player or team from a desktop player.
     *
     * @param the_message A string with a message, latitude and longitude
     */
    private void processMsg(String the_message) {
        String[] split_string = the_message.split("\n");
        String[] entry = split_string[0].split(",");

        double distance = getDistance(Double.parseDouble(entry[1]), Double.parseDouble(entry[2]), PLAYER_LATITUDE,
                PLAYER_LONGITUDE);
        mTextView.setText(entry[0] + "\n" + Double.toString(distance));

        mMapView.setMESSAGE_POINT(Double.parseDouble(entry[1]), Double.parseDouble(entry[2]));
        mMapView.update_map();
    }

    /**
     * A temporary storage unit to allow for sorting of
     * incoming CSV data.
     * This class sorts by shortest distance from the player in meters.
     */
    private class CSVData implements Comparable<CSVData> {
        // user related
        public String username;
        public String MAC_ADDR;

        // node related
        public int nodeID;

        public double distance;
        public String teamname;

        // GeoLocation, for MapView
        public double latitude;
        public double longitude;

        /**
         * Allows for comparison by distance
         *
         * @param another entry that is being compared against
         * @return -1 for less than, 0 for equals, 1 for greater than
         */
        public int compareTo(CSVData another) {
            return Double.compare(this.distance, another.distance);
        }
    }

    /**
     * Basically a timer that uploads user location periodically
     */
    class UploadLocation extends Handler {
        /**
         * Checks if location is set before uploading user data
         *
         * @param msg a blank message that contains no relevant data
         */
        @Override
        public void handleMessage(Message msg) {
            if (PLAYER_LATITUDE == -999 || PLAYER_LATITUDE < -900) {
                sleep(1000 * Constants.HTTP_UPLOAD_LOCATION_DELAY);
                return;
            }
            mHttpService.recreateUploadLocationTask();
            mHttpService.uploadLocation(USER_NAME, PLAYER_LATITUDE, PLAYER_LONGITUDE, MAC_ADDR);

            sleep(1000 * Constants.HTTP_UPLOAD_LOCATION_DELAY);
        }

        public void sleep(long milliseconds) {
            removeMessages(0);
            sendMessageDelayed(obtainMessage(0), milliseconds);
        }
    }

    /**
     * Handles HttpService Events
     * e.g. when HttpService reads in a CSV from the server
     */
    class HttpHandler extends Handler {
        /**
         * HttpService will send data back to the main activity this way.
         *
         * @param msg a message that contains a string and an int describing
         *            the type of data the string holds.
         */
        @Override
        public void handleMessage(Message msg) {
            String string = (String) msg.obj;
            switch (msg.what) {
            case Constants.MESSAGE_NEW_CSV:
                if (!string.isEmpty()) {
                    processCSV(string);
                } else {
                    PLAYER_LIST.clear();
                    PLAYER_LIST.add("!!!No USERS in database!!!");
                }
                break;
            case Constants.MESSAGE_NEW_MESSAGE:
                if (!string.isEmpty()) {
                    processMsg(string);
                } else {
                    mTextView.setText("No Messages\n");
                }
                break;
            case Constants.MESSAGE_USER_NAME:
                IS_READY = true;
                processLogin(string);
                break;
            case Constants.MESSAGE_NODE_LIST:
                if (!string.isEmpty()) {
                    processNodeList(string);
                } else {
                    Toast.makeText(getApplicationContext(), "No nodes, game not started", Toast.LENGTH_LONG).show();
                }
                break;
            }
        }
    }

    /**
     * basically a timer that calls for a list of users from a server
     */
    class HttpGetHandler extends Handler {
        /**
         * Messages trigger handler to fetch server information.
         *
         * @param msg the message that was sent
         */
        @Override
        public void handleMessage(Message msg) {
            Log.e("Running Handler", "");
            if (PLAYER_LATITUDE == -999 || PLAYER_LATITUDE < -900) {
                Log.e("Waiting for Location", "");
                PLAYER_LIST.add("Waiting for Location.");
                Location location = mLocationManager.getLastKnownLocation(PROVIDER);
                PLAYER_LATITUDE = location.getLatitude();
                Log.e("Latitude", Double.toString(PLAYER_LATITUDE));
                PLAYER_LONGITUDE = location.getLongitude();
                sleep(500 * Constants.HTTP_GET_CSV_DELAY);
                return;
            }
            mHttpService.recreateCSVTask();
            mHttpService.getCSV(USER_NAME);
            mHttpService.recreateMessageTask();
            mHttpService.getMessages(USER_NAME, TEAM_NAME);
            mHttpService.recreateNodeListTask();
            mHttpService.getNodeList();

            sleep(1000 * Constants.HTTP_GET_CSV_DELAY);
        }

        /**
         * allows for timed calling of events in its separate thread
         *
         * @param milliseconds time in milliseconds to delay sending a message to self
         */
        public void sleep(long milliseconds) {
            removeMessages(0);
            sendMessageDelayed(obtainMessage(0), milliseconds);
        }
    }

    /**
     * This handler waits to be connected.
     * If connection is successful, the program goes into battle state.
     */
    private class BluetoothListener extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case Constants.CONNECTED:
                connectionStarted();
                break;
            case Constants.DISCONNECTED:
                Toast.makeText(getApplicationContext(), "Failed to Connect", Toast.LENGTH_LONG).show();
                break;
            }
        }
    }

    private class InNodeTimer extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (CURRENT_NODE == -999) {
                if (COUNTDOWN_TIME != -99) {
                    COUNTDOWN_TIME = -99;
                }
                sleep(1000);
                return;
            }

            if (CURRENT_NODE_OWNERSHIP.equals("NOT_OWNED")) {
                if (COUNTDOWN_TIME == -99) {
                    COUNTDOWN_TIME = 30;
                    Toast.makeText(getApplicationContext(), "Capturing Node in 30 seconds", Toast.LENGTH_SHORT)
                            .show();
                }
                COUNTDOWN_TIME--;

                if (COUNTDOWN_TIME == 0) {
                    COUNTDOWN_TIME = -99;
                    Toast.makeText(getApplicationContext(), "Node Captured", Toast.LENGTH_SHORT).show();
                    CURRENT_NODE_OWNERSHIP = TEAM_NAME;
                    mHttpService.recreateNodeCaptureTask();
                    mHttpService.captureNode(CURRENT_NODE, TEAM_NAME);
                }
            } else if (CURRENT_NODE_OWNERSHIP.equals(TEAM_NAME)) {
                if (COUNTDOWN_TIME == -99) {
                    Toast.makeText(getApplicationContext(), "In Friendly Node", Toast.LENGTH_SHORT).show();
                    COUNTDOWN_TIME = 0;
                }
                COUNTDOWN_TIME++;
            }

            sleep(1000);
        }

        public void sleep(long milliseconds) {
            removeMessages(0);
            sendMessageDelayed(obtainMessage(0), milliseconds);
        }
    }
}