org.broeuschmeul.android.gps.usb.provider.driver.USBGpsProviderService.java Source code

Java tutorial

Introduction

Here is the source code for org.broeuschmeul.android.gps.usb.provider.driver.USBGpsProviderService.java

Source

/*
 * Copyright (C) 2016 Oliver Bell
 * Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
 * Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
 * Copyright (C) 2011, 2012 UsbGPS4Droid Project
 * 
 * This file is part of UsbGPS4Droid.
 *
 * UsbGPS4Droid 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.
 * 
 * UsbGPS4Droid 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 UsbGPS4Droid. If not, see <http://www.gnu.org/licenses/>.
 */

package org.broeuschmeul.android.gps.usb.provider.driver;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.GpsStatus.NmeaListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
import android.support.v4.app.NotificationCompat;

import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
import org.broeuschmeul.android.gps.usb.provider.R;
import org.broeuschmeul.android.gps.usb.provider.ui.GpsInfoActivity;
import org.broeuschmeul.android.gps.usb.provider.ui.USBGpsSettingsFragment;

/**
 * A Service used to replace Android internal GPS with a USB GPS and/or write GPS NMEA data in a File.
 *
 * @author Herbert von Broeuschmeul &
 * @author Oliver Bell
 */
public class USBGpsProviderService extends Service implements USBGpsManager.NmeaListener, LocationListener {

    public static final String ACTION_START_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.provider.action.START_TRACK_RECORDING";
    public static final String ACTION_STOP_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.provider.action.STOP_TRACK_RECORDING";
    public static final String ACTION_START_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.action.START_GPS_PROVIDER";
    public static final String ACTION_STOP_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.action.STOP_GPS_PROVIDER";
    public static final String ACTION_CONFIGURE_SIRF_GPS = "org.broeuschmeul.android.gps.usb.provider.action.CONFIGURE_SIRF_GPS";
    public static final String ACTION_ENABLE_SIRF_GPS = "org.broeuschmeul.android.gps.usb.provider.action.ENABLE_SIRF_GPS";

    public static final String PREF_START_GPS_PROVIDER = "startGps";
    public static final String PREF_START_ON_BOOT = "startOnBoot";
    public static final String PREF_GPS_LOCATION_PROVIDER = "gpsLocationProviderKey";
    public static final String PREF_REPLACE_STD_GPS = "replaceStdtGps";
    public static final String PREF_FORCE_ENABLE_PROVIDER = "forceEnableProvider";
    public static final String PREF_MOCK_GPS_NAME = "mockGpsName";
    public static final String PREF_CONNECTION_RETRIES = "connectionRetries";
    public static final String PREF_TRACK_RECORDING = "trackRecording";
    public static final String PREF_TRACK_FILE_DIR = "trackFileDirectory";
    public static final String PREF_TRACK_FILE_PREFIX = "trackFilePrefix";
    public static final String PREF_GPS_DEVICE = "usbDevice";
    public static final String PREF_GPS_DEVICE_VENDOR_ID = "usbDeviceVendorId";
    public static final String PREF_GPS_DEVICE_PRODUCT_ID = "usbDeviceProductId";
    public static final String PREF_GPS_DEVICE_SPEED = "gpsDeviceSpeed";
    public static final String PREF_TOAST_LOGGING = "showToasts";
    public static final String PREF_SET_TIME = "setTime";
    public static final String PREF_ABOUT = "about";

    /**
     * Tag used for log messages
     */
    private static final String LOG_TAG = USBGpsProviderService.class.getSimpleName();

    public static final String PREF_SIRF_GPS = "sirfGps";
    public static final String PREF_SIRF_ENABLE_GGA = "enableGGA";
    public static final String PREF_SIRF_ENABLE_RMC = "enableRMC";
    public static final String PREF_SIRF_ENABLE_GLL = "enableGLL";
    public static final String PREF_SIRF_ENABLE_VTG = "enableVTG";
    public static final String PREF_SIRF_ENABLE_GSA = "enableGSA";
    public static final String PREF_SIRF_ENABLE_GSV = "enableGSV";
    public static final String PREF_SIRF_ENABLE_ZDA = "enableZDA";
    public static final String PREF_SIRF_ENABLE_SBAS = "enableSBAS";
    public static final String PREF_SIRF_ENABLE_NMEA = "enableNMEA";
    public static final String PREF_SIRF_ENABLE_STATIC_NAVIGATION = "enableStaticNavigation";

    private USBGpsManager gpsManager = null;
    private PrintWriter writer;
    private File trackFile;
    private boolean preludeWritten = false;

    private boolean debugToasts = false;

    /**
     * Will start the service if set so in settings when the device boots
     */
    public static class BootReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(final Context context, Intent intent) {
            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
            if (BuildConfig.DEBUG)
                Log.d(LOG_TAG, intent.getAction());

            if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)
                    && sharedPreferences.getBoolean(PREF_START_ON_BOOT, false)) {
                new Handler(context.getMainLooper()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (BuildConfig.DEBUG)
                            Log.d(LOG_TAG, "Boot start");
                        context.startService(new Intent(context, USBGpsProviderService.class)
                                .setAction(ACTION_START_GPS_PROVIDER));
                    }
                }, 2000);
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor edit = sharedPreferences.edit();

        debugToasts = sharedPreferences.getBoolean(PREF_TOAST_LOGGING, false);

        int vendorId = sharedPreferences.getInt(PREF_GPS_DEVICE_VENDOR_ID,
                USBGpsSettingsFragment.DEFAULT_GPS_VENDOR_ID);
        int productId = sharedPreferences.getInt(PREF_GPS_DEVICE_PRODUCT_ID,
                USBGpsSettingsFragment.DEFAULT_GPS_PRODUCT_ID);

        int maxConRetries = Integer.parseInt(sharedPreferences.getString(PREF_CONNECTION_RETRIES,
                this.getString(R.string.defaultConnectionRetries)));

        log("prefs device addr: " + vendorId + " - " + productId);

        if (ACTION_START_GPS_PROVIDER.equals(intent.getAction())) {
            if (gpsManager == null) {
                String mockProvider = LocationManager.GPS_PROVIDER;
                if (!sharedPreferences.getBoolean(PREF_REPLACE_STD_GPS, true)) {
                    mockProvider = sharedPreferences.getString(PREF_MOCK_GPS_NAME,
                            getString(R.string.defaultMockGpsName));
                }
                gpsManager = new USBGpsManager(this, vendorId, productId, maxConRetries);
                boolean enabled = gpsManager.enable();

                if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, false) != enabled) {
                    edit.putBoolean(PREF_START_GPS_PROVIDER, enabled);
                    edit.apply();
                }

                if (enabled) {
                    gpsManager.enableMockLocationProvider(mockProvider);

                    PendingIntent launchIntent = PendingIntent.getActivity(this, 0,
                            new Intent(this, GpsInfoActivity.class), PendingIntent.FLAG_CANCEL_CURRENT);

                    sharedPreferences.edit().putInt(getString(R.string.pref_disable_reason_key), 0).apply();

                    Notification notification = new NotificationCompat.Builder(this).setContentIntent(launchIntent)
                            .setSmallIcon(R.drawable.ic_stat_notify).setAutoCancel(true)
                            .setContentTitle(getString(R.string.foreground_service_started_notification_title))
                            .setContentText(getString(R.string.foreground_gps_provider_started_notification))
                            .build();

                    startForeground(R.string.foreground_gps_provider_started_notification, notification);

                    showToast(R.string.msg_gps_provider_started);

                    if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, false)) {
                        startTracking();
                    }
                } else {
                    stopSelf();
                }

            } else {
                // We received a start intent even though it's already running so restart
                stopSelf();
                startService(new Intent(this, USBGpsProviderService.class).setAction(intent.getAction()));
            }

        } else if (ACTION_START_TRACK_RECORDING.equals(intent.getAction())) {
            startTracking();

        } else if (ACTION_STOP_TRACK_RECORDING.equals(intent.getAction())) {
            if (gpsManager != null) {
                gpsManager.removeNmeaListener(this);
                endTrack();
                showToast(this.getString(R.string.msg_nmea_recording_stopped));
            }
            if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)) {
                edit.putBoolean(PREF_TRACK_RECORDING, false);
                edit.commit();
            }

        } else if (ACTION_STOP_GPS_PROVIDER.equals(intent.getAction())) {
            if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)) {
                edit.putBoolean(PREF_START_GPS_PROVIDER, false);
                edit.commit();
            }
            stopSelf();

        } else if (ACTION_CONFIGURE_SIRF_GPS.equals(intent.getAction())
                || ACTION_ENABLE_SIRF_GPS.equals(intent.getAction())) {
            if (gpsManager != null) {
                Bundle extras = intent.getExtras();
                if (extras != null) {
                    gpsManager.enableSirfConfig(extras);
                } else {
                    gpsManager.enableSirfConfig(sharedPreferences);
                }
            }
        }
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        USBGpsManager manager = gpsManager;
        gpsManager = null;
        if (manager != null) {
            if (manager.getDisableReason() != 0) {
                showToast(getString(R.string.msg_gps_provider_stopped_by_problem,
                        getString(manager.getDisableReason())));
            } else {
                showToast(R.string.msg_gps_provider_stopped);
            }
            manager.removeNmeaListener(this);
            manager.disableMockLocationProvider();
            manager.disable();
        }
        endTrack();
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor edit = sharedPreferences.edit();

        if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)) {
            edit.putBoolean(PREF_START_GPS_PROVIDER, false);
            edit.apply();
        }

        super.onDestroy();
    }

    /**
     * Checks if the applications has the given runtime permission
     */
    private boolean hasPermission(String perm) {
        return (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this, perm));
    }

    private void startTracking() {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor edit = sharedPreferences.edit();
        if (trackFile == null) {
            if (gpsManager != null) {
                if (hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    beginTrack();
                    gpsManager.addNmeaListener(this);
                    if (!sharedPreferences.getBoolean(PREF_TRACK_RECORDING, false)) {
                        edit.putBoolean(PREF_TRACK_RECORDING, true);
                        edit.apply();
                    }

                    showToast(R.string.msg_nmea_recording_started);

                } else {
                    Toast.makeText(this, "UsbGps logger - No storage permission", Toast.LENGTH_SHORT).show();

                    edit.putBoolean(PREF_TRACK_RECORDING, false).apply();
                }

            } else {
                endTrack();
            }

        } else {
            showToast(R.string.msg_nmea_recording_already_started);
        }
    }

    private void showToast(int messageId) {
        if (debugToasts) {
            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
        }
    }

    private void showToast(String message) {
        if (debugToasts) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        }
    }

    private void beginTrack() {
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat fmt = new SimpleDateFormat("_yyyy-MM-dd_HH-mm-ss'.nmea'");

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        String trackDirName = sharedPreferences.getString(PREF_TRACK_FILE_DIR,
                this.getString(R.string.defaultTrackFileDirectory));
        String trackFilePrefix = sharedPreferences.getString(PREF_TRACK_FILE_PREFIX,
                this.getString(R.string.defaultTrackFilePrefix));
        trackFile = new File(trackDirName, trackFilePrefix + fmt.format(new Date()));
        log("Writing the prelude of the NMEA file: " + trackFile.getAbsolutePath());
        File trackDir = trackFile.getParentFile();
        try {
            if ((!trackDir.mkdirs()) && (!trackDir.isDirectory())) {
                Log.e(LOG_TAG, "Error while creating parent dir of NMEA file: " + trackDir.getAbsolutePath());
            }
            writer = new PrintWriter(new BufferedWriter(new FileWriter(trackFile)));
            preludeWritten = true;
        } catch (IOException e) {
            Log.e(LOG_TAG, "Error while writing the prelude of the NMEA file: " + trackFile.getAbsolutePath(), e);
            // there was an error while writing the prelude of the NMEA file, stopping the service...
            stopSelf();
        }
    }

    private void endTrack() {
        if (trackFile != null && writer != null) {
            log("Ending the NMEA file: " + trackFile.getAbsolutePath());
            preludeWritten = false;
            writer.close();
            trackFile = null;
        }
    }

    private void addNMEAString(String data) {
        if (!preludeWritten) {
            beginTrack();
        }
        log("Adding data in the NMEA file: " + data);
        if (trackFile != null && writer != null) {
            writer.print(data);
        }
    }

    /* (non-Javadoc)
     * @see android.app.Service#onBind(android.content.Intent)
     */
    @Override
    public IBinder onBind(Intent intent) {
        log("trying access IBinder");
        return null;
    }

    @Override
    public void onLocationChanged(Location location) {

    }

    @Override
    public void onProviderDisabled(String provider) {
        log("The GPS has been disabled.....stopping the NMEA tracker service.");
        stopSelf();
    }

    @Override
    public void onProviderEnabled(String provider) {

    }

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

    }

    @Override
    public void onNmeaReceived(long timestamp, String data) {
        addNMEAString(data);
    }

    private void log(String message) {
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, message);
    }
}