us.dustinj.locationstore.LocationService.java Source code

Java tutorial

Introduction

Here is the source code for us.dustinj.locationstore.LocationService.java

Source

/**
 *  LocationStore - Copyright (C) 2013  Dustin Johnson
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package us.dustinj.locationstore;

import us.dustinj.locationstore.io.HttpStatusResponseHandler;
import us.dustinj.locationstore.io.LocationExporter;
import us.dustinj.locationstore.io.RestInterface;
import us.dustinj.locationstore.io.SimpleWebCommand;
import us.dustinj.utilities.CommonUtilities;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.google.android.gcm.GCMRegistrar;

public class LocationService extends Service implements LocationListener, HttpStatusResponseHandler {

    private LocationManager m_locationManager;
    private ConnectivityManager m_connectivityManager;
    private LocationDatabase m_locationDatabase;
    private LocationAggregator m_locationAggregator;
    private AppSettings m_appSettings;
    private ExportStateMachine m_exportStateMachine;
    private ForcedFixStateMachine m_forcedFixStateMachine;
    private GcmRegistrationStateMachine m_gcmRegistrationStateMachine;
    private final IBinder m_binder = new LocationBinder();

    public class LocationBinder extends Binder {
        LocationService getService() {
            return LocationService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(this.getClass().getSimpleName(), "onBind");
        return m_binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(this.getClass().getSimpleName(), "onStartCommand");

        Tick();

        return START_STICKY;
    }

    public synchronized void Tick() {
        Log.d(this.getClass().getSimpleName(), "Tick");

        m_locationAggregator.Tick();
        m_exportStateMachine.sendMessage(ExportStateMachine.Tick);
        m_forcedFixStateMachine.sendMessage(ForcedFixStateMachine.Tick);
        m_gcmRegistrationStateMachine.sendMessage(GcmRegistrationStateMachine.Tick);
    }

    public synchronized void ForceLocationFix() {
        Log.d(this.getClass().getSimpleName(), "ForceLocationFix");

        m_forcedFixStateMachine.sendMessage(ForcedFixStateMachine.ForceOneTimeFix);
        m_exportStateMachine.sendMessage(ExportStateMachine.ForceQuickUpload);
        Tick();
    }

    @Override
    public void onCreate() {
        Log.d(this.getClass().getSimpleName(), "onCreate");

        m_appSettings = AppSettings.GetSettings(this);
        m_locationDatabase = new LocationDatabase(this);
        m_locationAggregator = new LocationAggregator(m_appSettings, m_locationDatabase);
        m_exportStateMachine = ExportStateMachine.makeStateMachine(this, m_appSettings.GetTimers());
        m_forcedFixStateMachine = ForcedFixStateMachine.makeStateMachine(this, m_appSettings);
        m_gcmRegistrationStateMachine = GcmRegistrationStateMachine.makeStateMachine(this,
                m_appSettings.GetTimers());

        m_locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        m_locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 1000, (float) 0.1, this);

        m_connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);

        LocalBroadcastManager.getInstance(this).registerReceiver(m_gcmRegistrationChangeReceiver,
                new IntentFilter("gcm_registrationIdChanged"));
        LocalBroadcastManager.getInstance(this).registerReceiver(m_gcmMessageIntentReceiver,
                new IntentFilter("gcm_messageReceived"));

        GCMRegistrar.checkDevice(this);
        GCMRegistrar.checkManifest(this);
        final String regId = GCMRegistrar.getRegistrationId(this);
        if (regId.equals("")) {
            GCMRegistrar.register(this, CommonUtilities.SENDER_ID);
        } else {
            Log.v(this.getClass().getSimpleName(), "Already registered");
        }

        Log.d(this.getClass().getSimpleName(), "regId: " + regId);
    }

    @Override
    public void onDestroy() {
        Log.d(this.getClass().getSimpleName(), "onDestroy");

        m_appSettings.Close();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(m_gcmRegistrationChangeReceiver);
        LocalBroadcastManager.getInstance(this).unregisterReceiver(m_gcmMessageIntentReceiver);

        super.onDestroy();
    }

    public boolean IsBatteryAboveThreshold() {
        return getBatteryPercent() > m_appSettings.GetForcedFixBatteryCutoff();
    }

    private float getBatteryPercent() {
        Intent batteryStatus = this.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);

        return level / (float) scale * 100;
    }

    public boolean TurnOnGPS() {
        Log.d(this.getClass().getSimpleName(), "TurnOnGPS");
        String allowedLocationProviders = Settings.Secure.getString(getContentResolver(),
                Settings.Secure.LOCATION_PROVIDERS_ALLOWED);

        if (allowedLocationProviders == null || !allowedLocationProviders.contains(LocationManager.GPS_PROVIDER)) {
            Log.d(this.getClass().getSimpleName(), "GPS is off");
            return false;
        }

        m_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, (float) 0.1, this);

        return true;
    }

    public void TurnOffGPS() {
        Log.d(this.getClass().getSimpleName(), "TurnOffGPS");
        m_locationManager.removeUpdates(this);
        m_locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 1000, (float) 0.1, this);
    }

    public void FlushLocationAggregator() {
        m_locationAggregator.Flush();
        Tick();
    }

    public boolean IsNetworkConnected() {
        NetworkInfo info = m_connectivityManager.getActiveNetworkInfo();
        boolean connected = info != null && info.isConnectedOrConnecting();

        Log.d(this.getClass().getSimpleName(), "IsNetworkConnected - canGetInfo: "
                + (info != null ? "True" : "False") + "  connected: " + (connected ? "True" : "False"));

        return connected;
    }

    private final BroadcastReceiver m_gcmMessageIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String messageID = intent.getStringExtra("messageID");

            Log.i(LocationService.this.getClass().getSimpleName(),
                    "Received GCM Message: messageID = " + messageID);

            if (messageID != null) {
                if (messageID.equals("ForceLocationFix")) {
                    ForceLocationFix();
                }
            }
        }
    };

    // Our handler for received Intents from GCM.
    private final BroadcastReceiver m_gcmRegistrationChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            SetGcmRegistrationID(intent.getStringExtra("registrationID"));
        }
    };

    public void SetGcmRegistrationID(String registrationID) {
        m_appSettings.SetGcmRegistrationID(registrationID);
        m_appSettings.SetGcmRegistrationIDCommunicatedToServer(false);

        Tick();
    }

    public boolean IsGcmRegistrationIDCommunicatedToServer() {
        return m_appSettings.IsGcmRegistrationIDCommunicatedToServer();
    }

    public void SendGcmRegistrationIdUpdate() {
        Log.d("test", RestInterface.SetGcmRegistrationID(m_appSettings, m_appSettings.GetGcmRegistrationID()));
        new SimpleWebCommand(this,
                RestInterface.SetGcmRegistrationID(m_appSettings, m_appSettings.GetGcmRegistrationID()),
                new HttpStatusResponseHandler() {

                    @Override
                    public void onResponseReceived(int returnCode) {
                        Log.d(LocationService.this.getClass().getSimpleName(),
                                "SendGcmRegistrationIdUpdate - Response: " + returnCode);

                        if (returnCode == 200) {
                            m_gcmRegistrationStateMachine
                                    .sendMessage(GcmRegistrationStateMachine.RegistrationSuccess);
                            m_appSettings.SetGcmRegistrationIDCommunicatedToServer(true);
                        } else {
                            m_gcmRegistrationStateMachine
                                    .sendMessage(GcmRegistrationStateMachine.RegistrationFailure);
                        }
                    }
                }).execute();
    }

    public boolean ExportLocations() {
        if (m_locationDatabase.GetRecordCount() > 0) {
            new LocationExporter(this, this).execute();
            return true;
        }

        return false;
    }

    @Override
    public void onResponseReceived(int returnCode) {
        Log.d(this.getClass().getSimpleName(), "onResponseReceived");

        if (returnCode == 200) {
            m_exportStateMachine.sendMessage(ExportStateMachine.ExportSuccess);
        } else {
            m_exportStateMachine.sendMessage(ExportStateMachine.ExportFailure);
        }

        Tick();
    }

    @Override
    public void onLocationChanged(Location loc) {
        ExtendedLocation eLoc = new ExtendedLocation(loc);

        eLoc.setBatteryPercent(getBatteryPercent());
        if (eLoc.hasAccuracy()) {
            if (eLoc.getAccuracy() <= m_appSettings.GetAccuracyThresholdM()
                    || m_appSettings.GetAccuracyThresholdM() == 0) {
                m_locationAggregator.InsertLocation(eLoc);
                m_forcedFixStateMachine.sendMessage(ForcedFixStateMachine.ReceivedLocation);
            } else {
                Log.d(this.getClass().getSimpleName(),
                        "Rejected location as accuracy was above threshold: " + eLoc.getAccuracy() + " meters");
            }
        } else {
            Log.d(this.getClass().getSimpleName(), "Rejected location as its accuracy was not available");
        }

        if (eLoc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
            m_forcedFixStateMachine.sendMessage(ForcedFixStateMachine.ReceivedGPSFix);
        }

        Tick();
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.d(this.getClass().getSimpleName(), "onProviderDisabled");
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.d(this.getClass().getSimpleName(), "onProviderEnabled");
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.d(this.getClass().getSimpleName(), "onStatusChanged");
    }
}