net.fabiszewski.ulogger.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for net.fabiszewski.ulogger.MainActivity.java

Source

/*
 * Copyright (c) 2017 Bartek Fabiszewski
 * http://www.fabiszewski.net
 *
 * This file is part of logger-android.
 * Licensed under GPL, either version 3, or any later.
 * See <http://www.gnu.org/licenses/>
 */

package net.fabiszewski.ulogger;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * Main activity of ulogger
 *
 */

public class MainActivity extends AppCompatActivity {

    public static final String UPDATED_PREFS = "extra_updated_prefs";

    private final String TAG = MainActivity.class.getSimpleName();

    private final static int LED_GREEN = 1;
    private final static int LED_RED = 2;
    private final static int LED_YELLOW = 3;

    private final static int PERMISSION_LOCATION = 1;
    private final static int PERMISSION_WRITE = 2;
    private final static int RESULT_PREFS_UPDATED = 1;

    private String pref_units;
    private long pref_minTimeMillis;
    private boolean pref_liveSync;

    private final static double KM_MILE = 0.621371;

    private static boolean syncError = false;
    private boolean isUploading = false;
    private TextView syncErrorLabel;
    private TextView syncLabel;
    private TextView syncLed;
    private TextView locLabel;
    private TextView locLed;

    private DbAccess db;
    private static String TXT_START;
    private static String TXT_STOP;
    private Button toggleButton;

    /**
     * Initialization
     * @param savedInstanceState Saved state
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        updatePreferences();
        TXT_START = getString(R.string.button_start);
        TXT_STOP = getString(R.string.button_stop);
        setContentView(R.layout.activity_main);
        Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
        setSupportActionBar(myToolbar);
        toggleButton = (Button) findViewById(R.id.toggle_button);
        syncErrorLabel = (TextView) findViewById(R.id.sync_error);
        syncLabel = (TextView) findViewById(R.id.sync_status);
        syncLed = (TextView) findViewById(R.id.sync_led);
        locLabel = (TextView) findViewById(R.id.location_status);
        locLed = (TextView) findViewById(R.id.loc_led);
    }

    /**
     * On resume
     */
    @Override
    protected void onResume() {
        super.onResume();
        if (Logger.DEBUG) {
            Log.d(TAG, "[onResume]");
        }

        db = DbAccess.getInstance();
        db.open(this);
        String trackName = db.getTrackName();
        if (trackName != null) {
            updateTrackLabel(trackName);
        }

        if (LoggerService.isRunning()) {
            toggleButton.setText(TXT_STOP);
            setLocLed(LED_GREEN);
        } else {
            toggleButton.setText(TXT_START);
            setLocLed(LED_RED);
        }
        registerBroadcastReceiver();
        updateStatus();
    }

    /**
     * On pause
     */
    @Override
    protected void onPause() {
        if (Logger.DEBUG) {
            Log.d(TAG, "[onPause]");
        }
        unregisterReceiver(mBroadcastReceiver);
        if (db != null) {
            db.close();
        }
        super.onPause();
    }

    /**
     * On destroy
     */
    @Override
    protected void onDestroy() {
        if (Logger.DEBUG) {
            Log.d(TAG, "[onDestroy]");
        }
        super.onDestroy();
    }

    /**
     * Create main menu
     * @param menu Menu
     * @return Always true
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    /**
     * Main menu options
     * @param item Selected option
     * @return True if handled
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

        case R.id.menu_settings:
            Intent i = new Intent(MainActivity.this, SettingsActivity.class);
            startActivityForResult(i, RESULT_PREFS_UPDATED);
            return true;
        case R.id.menu_about:
            showAbout();
            return true;
        case R.id.menu_export:
            startExport();
            return true;

        default:
            return super.onOptionsItemSelected(item);

        }
    }

    /**
     * Callback on permission request result
     * Called after user granted/rejected location permission
     *
     * @param requestCode Permission code
     * @param permissions Permissions
     * @param grantResults Result
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
            @NonNull int[] grantResults) {
        // onPause closed db
        db.open(this);
        switch (requestCode) {
        case PERMISSION_LOCATION:
            if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // onPause closed db
                db.open(this);
                startLogger();
            }
            break;
        case PERMISSION_WRITE:
            if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                startExport();
            }
            break;
        }
        db.close();
    }

    /**
     * Callback on activity result.
     * Called after user updated preferences
     *
     * @param requestCode Activity code
     * @param resultCode Result
     * @param data Data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case RESULT_PREFS_UPDATED:
            // Preferences updated
            updatePreferences();
            if (LoggerService.isRunning()) {
                // restart logging
                Intent intent = new Intent(MainActivity.this, LoggerService.class);
                intent.putExtra(UPDATED_PREFS, true);
                startService(intent);
            }
            break;
        }
    }

    /**
     * Reread user preferences
     */
    private void updatePreferences() {
        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        pref_units = prefs.getString("prefUnits", getString(R.string.pref_units_default));
        pref_minTimeMillis = Long
                .parseLong(prefs.getString("prefMinTime", getString(R.string.pref_mintime_default))) * 1000;
        pref_liveSync = prefs.getBoolean("prefLiveSync", false);
    }

    /**
     * Called when the user clicks the Start/Stop button
     * @param view View
     */
    public void toggleLogging(@SuppressWarnings("UnusedParameters") View view) {
        if (LoggerService.isRunning()) {
            stopLogger();
        } else {
            startLogger();
        }
    }

    /**
     * Start logger service
     */
    private void startLogger() {
        // start tracking
        if (db.getTrackName() != null) {
            Intent intent = new Intent(MainActivity.this, LoggerService.class);
            startService(intent);
        } else {
            showEmptyTrackNameWarning();
        }
    }

    /**
     * Stop logger service
     */
    private void stopLogger() {
        // stop tracking
        Intent intent = new Intent(MainActivity.this, LoggerService.class);
        stopService(intent);
    }

    /**
     * Start export service
     */
    private void startExport() {
        if (db.countPositions() > 0) {
            Intent exportIntent = new Intent(MainActivity.this, GpxExportService.class);
            startService(exportIntent);
            showToast(getString(R.string.export_started));
        } else {
            showToast(getString(R.string.nothing_to_export));
        }
    }

    /**
     * Called when the user clicks the New track button
     * @param view View
     */
    public void newTrack(@SuppressWarnings("UnusedParameters") View view) {
        if (LoggerService.isRunning()) {
            showToast(getString(R.string.logger_running_warning));
        } else if (db.needsSync()) {
            showNotSyncedWarning();
        } else {
            showTrackDialog();
        }
    }

    /**
     * Called when the user clicks the Upload button
     * @param view View
     */
    public void uploadData(@SuppressWarnings("UnusedParameters") View view) {
        if (!SettingsActivity.isValidServerSetup(this)) {
            showToast(getString(R.string.provide_user_pass_url), Toast.LENGTH_LONG);
        } else if (db.needsSync()) {
            Intent syncIntent = new Intent(MainActivity.this, WebSyncService.class);
            startService(syncIntent);
            showToast(getString(R.string.uploading_started));
            isUploading = true;
        } else {
            showToast(getString(R.string.nothing_to_synchronize));
        }
    }

    /**
     * Called when the user clicks the track text view
     * @param view View
     */
    public void trackSummary(@SuppressWarnings("UnusedParameters") View view) {
        final TrackSummary summary = db.getTrackSummary();
        if (summary == null) {
            showToast(getString(R.string.no_positions));
            return;
        }

        @SuppressLint("InflateParams")
        View summaryView = getLayoutInflater().inflate(R.layout.summary, null, false);
        AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
        alertDialog.setTitle(getString(R.string.track_summary));
        alertDialog.setView(summaryView);
        alertDialog.setIcon(R.drawable.ic_equalizer_white_24dp);
        alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.ok),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();

        final TextView summaryDistance = (TextView) alertDialog.findViewById(R.id.summary_distance);
        final TextView summaryDuration = (TextView) alertDialog.findViewById(R.id.summary_duration);
        final TextView summaryPositions = (TextView) alertDialog.findViewById(R.id.summary_positions);
        double distance = (double) summary.getDistance() / 1000;
        String unitName = getString(R.string.unit_kilometer);
        if (pref_units.equals(getString(R.string.pref_units_imperial))) {
            distance *= KM_MILE;
            unitName = getString(R.string.unit_mile);
        }
        final NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(2);
        final String distanceString = nf.format(distance);
        summaryDistance.setText(getString(R.string.summary_distance, distanceString, unitName));
        final long h = summary.getDuration() / 3600;
        final long m = summary.getDuration() % 3600 / 60;
        summaryDuration.setText(getString(R.string.summary_duration, h, m));
        int positionsCount = (int) summary.getPositionsCount();
        if (needsPluralFewHack(positionsCount)) {
            summaryPositions.setText(getResources().getString(R.string.summary_positions_few, positionsCount));
        } else {
            summaryPositions.setText(
                    getResources().getQuantityString(R.plurals.summary_positions, positionsCount, positionsCount));
        }
    }

    /**
     * Display toast message
     * @param text Message
     */
    private void showToast(CharSequence text) {
        showToast(text, Toast.LENGTH_SHORT);
    }

    /**
     * Display toast message
     * @param text Message
     * @param duration Duration
     */
    private void showToast(CharSequence text, int duration) {
        Context context = getApplicationContext();
        Toast toast = Toast.makeText(context, text, duration);
        toast.show();
    }

    /**
     * Display About dialog
     */
    private void showAbout() {
        @SuppressLint("InflateParams")
        View view = getLayoutInflater().inflate(R.layout.about, null, false);
        AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
        alertDialog.setTitle(getString(R.string.app_name));
        alertDialog.setView(view);
        alertDialog.setIcon(R.drawable.ic_ulogger_logo_24dp);
        alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.ok),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();
        final TextView versionLabel = (TextView) alertDialog.findViewById(R.id.about_version);
        versionLabel.setText(getString(R.string.about_version, BuildConfig.VERSION_NAME));
        final TextView descriptionLabel = (TextView) alertDialog.findViewById(R.id.about_description);
        final TextView description2Label = (TextView) alertDialog.findViewById(R.id.about_description2);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            descriptionLabel.setText(fromHtmlDepreciated(getString(R.string.about_description)));
            description2Label.setText(fromHtmlDepreciated(getString(R.string.about_description2)));
        } else {
            descriptionLabel.setText(
                    Html.fromHtml(getString(R.string.about_description), android.text.Html.FROM_HTML_MODE_LEGACY));
            description2Label.setText(
                    Html.fromHtml(getString(R.string.about_description2), android.text.Html.FROM_HTML_MODE_LEGACY));
        }
    }

    /**
     * Depreciated fromHtml method for build version < 24
     * @param text Message text
     * @return Text with parsed html
     */
    @SuppressWarnings("deprecation")
    private static CharSequence fromHtmlDepreciated(String text) {
        return Html.fromHtml(text);
    }

    /**
     * Display warning before deleting not synchronized track
     */
    private void showNotSyncedWarning() {
        AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
        alertDialog.setTitle(getString(R.string.warning));
        alertDialog.setMessage(getString(R.string.notsync_warning));
        alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.ok),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        showTrackDialog();
                    }
                });
        alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();
    }

    /**
     * Display warning if track name is not set
     */
    private void showEmptyTrackNameWarning() {
        showToast(getString(R.string.empty_trackname_warning));
    }

    /**
     * Display track name dialog
     */
    private void showTrackDialog() {
        final Dialog dialog = new Dialog(MainActivity.this);
        dialog.setTitle(R.string.title_newtrack);
        dialog.setContentView(R.layout.newtrack_dialog);
        final EditText editText = (EditText) dialog.findViewById(R.id.newtrack_edittext);
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss", Locale.getDefault());
        sdf.setTimeZone(TimeZone.getDefault());
        final String dateSuffix = sdf.format(Calendar.getInstance().getTime());
        final String autoName = "Auto_" + dateSuffix;
        editText.setText(autoName);
        editText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editText.selectAll();
            }
        });
        dialog.show();

        final Button submit = (Button) dialog.findViewById(R.id.newtrack_button_submit);
        submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String trackName = editText.getText().toString();
                if (trackName.length() == 0) {
                    return;
                }
                db.newTrack(trackName);
                LoggerService.resetUpdateRealtime();
                updateTrackLabel(trackName);
                updateStatus();
                dialog.cancel();
            }
        });

        final Button cancel = (Button) dialog.findViewById(R.id.newtrack_button_cancel);
        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.cancel();
            }
        });
    }

    /**
     * Update track name label
     * @param trackName Track name
     */
    private void updateTrackLabel(String trackName) {
        final TextView trackLabel = (TextView) findViewById(R.id.newtrack_label);
        trackLabel.setText(trackName);
    }

    /**
     * Update location tracking status label
     * @param lastUpdateRealtime Real time of last location update
     */
    private void updateLocationLabel(long lastUpdateRealtime) {
        // get last location update time
        String timeString;
        long timestamp = 0;
        long elapsed = 0;
        long dbTimestamp;
        if (lastUpdateRealtime > 0) {
            elapsed = (SystemClock.elapsedRealtime() - lastUpdateRealtime);
            timestamp = System.currentTimeMillis() - elapsed;
        } else if ((dbTimestamp = db.getLastTimestamp()) > 0) {
            timestamp = dbTimestamp * 1000;
            elapsed = System.currentTimeMillis() - timestamp;
        }

        if (timestamp > 0) {
            final Date updateDate = new Date(timestamp);
            final Calendar calendar = Calendar.getInstance();
            calendar.setTime(updateDate);
            final Calendar today = Calendar.getInstance();
            SimpleDateFormat sdf;

            if (calendar.get(Calendar.YEAR) == today.get(Calendar.YEAR)
                    && calendar.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) {
                sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
            } else {
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
            }
            sdf.setTimeZone(TimeZone.getDefault());
            timeString = String.format(getString(R.string.label_last_update), sdf.format(updateDate));
        } else {
            timeString = "-";
        }
        locLabel.setText(timeString);
        // Change led if more than 2 update periods elapsed since last location update
        if (LoggerService.isRunning() && (timestamp == 0 || elapsed > pref_minTimeMillis * 2)) {
            setLocLed(LED_YELLOW);
        }
    }

    /**
     * Update synchronization status label and led
     * @param unsynced Count of not synchronized positions
     */
    private void updateSyncStatus(int unsynced) {
        String text;
        if (unsynced > 0) {
            if (needsPluralFewHack(unsynced)) {
                text = getString(R.string.label_positions_behind_few, unsynced);
            } else {
                text = getResources().getQuantityString(R.plurals.label_positions_behind, unsynced, unsynced);
            }
            if (syncError) {
                setSyncLed(LED_RED);
            } else {
                setSyncLed(LED_YELLOW);
            }
        } else {
            text = getString(R.string.label_synchronized);
            setSyncLed(LED_GREEN);
        }

        syncLabel.setText(text);
    }

    /**
     * Update location tracking and synchronization status
     */
    private void updateStatus() {
        updateLocationLabel(LoggerService.lastUpdateRealtime());
        // get sync status
        int count = db.countUnsynced();
        String error = db.getError();
        if (error != null) {
            if (Logger.DEBUG) {
                Log.d(TAG, "[sync error: " + error + "]");
            }
            syncError = true;
            syncErrorLabel.setText(error);
        } else if (syncError) {
            syncError = false;
            syncErrorLabel.setText(null);
        }
        updateSyncStatus(count);
    }

    /**
     * Set status led color
     * @param led Led text view
     * @param color Color (red, yellow or green)
     */
    private void setLedColor(TextView led, int color) {
        Drawable l;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            l = led.getCompoundDrawables()[0];
        } else {
            l = led.getCompoundDrawablesRelative()[0];
        }
        switch (color) {
        case LED_RED:
            l.setColorFilter(ContextCompat.getColor(this, R.color.colorRed), PorterDuff.Mode.SRC_ATOP);
            break;

        case LED_GREEN:
            l.setColorFilter(ContextCompat.getColor(this, R.color.colorGreen), PorterDuff.Mode.SRC_ATOP);
            break;

        case LED_YELLOW:
            l.setColorFilter(ContextCompat.getColor(this, R.color.colorYellow), PorterDuff.Mode.SRC_ATOP);
            break;
        }
        l.invalidateSelf();
    }

    /**
     * Set synchronization status led color
     * Red - synchronization error
     * Yellow - synchronization delay
     * Green - synchronized
     * @param color Color
     */
    private void setSyncLed(int color) {
        if (Logger.DEBUG) {
            Log.d(TAG, "[setSyncLed " + color + "]");
        }
        setLedColor(syncLed, color);
    }

    /**
     * Set location tracking status led color
     * Red - tracking off
     * Yellow - tracking on, long time since last update
     * Green - tracking on, recently updated
     * @param color Color
     */
    private void setLocLed(int color) {
        if (Logger.DEBUG) {
            Log.d(TAG, "[setLocLed " + color + "]");
        }
        setLedColor(locLed, color);
    }

    /**
     * Register broadcast receiver for synchronization
     * and tracking status updates
     */
    private void registerBroadcastReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(LoggerService.BROADCAST_LOCATION_STARTED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_STOPPED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_UPDATED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_DISABLED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_GPS_DISABLED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_NETWORK_DISABLED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_GPS_ENABLED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_NETWORK_ENABLED);
        filter.addAction(LoggerService.BROADCAST_LOCATION_PERMISSION_DENIED);
        filter.addAction(GpxExportService.BROADCAST_WRITE_PERMISSION_DENIED);
        filter.addAction(GpxExportService.BROADCAST_EXPORT_FAILED);
        filter.addAction(GpxExportService.BROADCAST_EXPORT_DONE);
        filter.addAction(WebSyncService.BROADCAST_SYNC_DONE);
        filter.addAction(WebSyncService.BROADCAST_SYNC_FAILED);
        registerReceiver(mBroadcastReceiver, filter);
    }

    /**
     * Broadcast receiver
     */
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Logger.DEBUG) {
                Log.d(TAG, "[broadcast received " + intent + "]");
            }
            if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_UPDATED)) {
                updateLocationLabel(LoggerService.lastUpdateRealtime());
                setLocLed(LED_GREEN);
                if (!pref_liveSync) {
                    updateSyncStatus(db.countUnsynced());
                }
            } else if (intent.getAction().equals(WebSyncService.BROADCAST_SYNC_DONE)) {
                final int unsyncedCount = db.countUnsynced();
                updateSyncStatus(unsyncedCount);
                setSyncLed(LED_GREEN);
                // reset error flag and label
                if (syncError) {
                    syncErrorLabel.setText(null);
                    syncError = false;
                }
                // show message if manual uploading
                if (isUploading && unsyncedCount == 0) {
                    showToast(getString(R.string.uploading_done));
                    isUploading = false;
                }
            } else if (intent.getAction().equals((WebSyncService.BROADCAST_SYNC_FAILED))) {
                updateSyncStatus(db.countUnsynced());
                setSyncLed(LED_RED);
                // set error flag and label
                String message = intent.getStringExtra("message");
                syncErrorLabel.setText(message);
                syncError = true;
                // show message if manual uploading
                if (isUploading) {
                    showToast(getString(R.string.uploading_failed) + "\n" + message, Toast.LENGTH_LONG);
                    isUploading = false;
                }
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_STARTED)) {
                toggleButton.setText(TXT_STOP);
                showToast(getString(R.string.tracking_started));
                setLocLed(LED_YELLOW);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_STOPPED)) {
                toggleButton.setText(TXT_START);
                showToast(getString(R.string.tracking_stopped));
                setLocLed(LED_RED);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_GPS_DISABLED)) {
                showToast(getString(R.string.gps_disabled_warning), Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_NETWORK_DISABLED)) {
                showToast(getString(R.string.net_disabled_warning), Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_DISABLED)) {
                showToast(getString(R.string.location_disabled), Toast.LENGTH_LONG);
                setLocLed(LED_RED);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_NETWORK_ENABLED)) {
                showToast(getString(R.string.using_network), Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_GPS_ENABLED)) {
                showToast(getString(R.string.using_gps), Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(GpxExportService.BROADCAST_EXPORT_DONE)) {
                showToast(getString(R.string.export_done), Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(GpxExportService.BROADCAST_EXPORT_FAILED)) {
                String message = getString(R.string.export_failed);
                if (intent.hasExtra("message")) {
                    message += "\n" + intent.getStringExtra("message");
                }
                showToast(message, Toast.LENGTH_LONG);
            } else if (intent.getAction().equals(LoggerService.BROADCAST_LOCATION_PERMISSION_DENIED)) {
                showToast(getString(R.string.location_permission_denied), Toast.LENGTH_LONG);
                setLocLed(LED_RED);
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, PERMISSION_LOCATION);
            } else if (intent.getAction().equals(GpxExportService.BROADCAST_WRITE_PERMISSION_DENIED)) {
                showToast(getString(R.string.write_permission_denied), Toast.LENGTH_LONG);
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSION_WRITE);
            }
        }
    };

    /**
     * Check if count matches "few" plurals and language is Polish
     * Hack for API <= 10 where "few" plural is missing for some languages
     * todo: this simple hack currently supports only language "pl"
     *
     * @param i Count
     * @return True if hack is needed, false otherwise
     */
    private boolean needsPluralFewHack(int i) {
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
                && Locale.getDefault().getLanguage().equalsIgnoreCase("pl") && (i % 10 >= 2 && i % 10 <= 4)
                && (i % 100 < 12 || i % 100 > 14);
    }
}