lu.fisch.canze.activities.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for lu.fisch.canze.activities.MainActivity.java

Source

/*
CanZE
Take a closer look at your ZE car
    
Copyright (C) 2015 - The CanZE Team
http://canze.fisch.lu
    
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 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 lu.fisch.canze.activities;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
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.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.AnimationDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

import lu.fisch.canze.R;
import lu.fisch.canze.actors.Field;
import lu.fisch.canze.actors.Fields;
import lu.fisch.canze.actors.Frames;
import lu.fisch.canze.bluetooth.BluetoothManager;
import lu.fisch.canze.classes.DataLogger;
import lu.fisch.canze.classes.DebugLogger;
import lu.fisch.canze.database.CanzeDataSource;
import lu.fisch.canze.devices.BobDue;
import lu.fisch.canze.devices.Device;
import lu.fisch.canze.devices.ELM327;
import lu.fisch.canze.devices.ELM327OverHttp;
import lu.fisch.canze.fragments.ExperimentalFragment;
import lu.fisch.canze.fragments.MainFragment;
import lu.fisch.canze.fragments.TechnicalFragment;
import lu.fisch.canze.interfaces.BluetoothEvent;
import lu.fisch.canze.interfaces.DebugListener;
import lu.fisch.canze.interfaces.FieldListener;
import lu.fisch.canze.ui.AppSectionsPagerAdapter;

public class MainActivity extends AppCompatActivity
        implements FieldListener /*, android.support.v7.app.ActionBar.TabListener */ {
    public static final String TAG = "  CanZE";

    // SPP UUID service
    // private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    public final static String PREFERENCES_FILE = "lu.fisch.canze.settings";
    public final static String DATA_FILE = "lu.fisch.canze.data";

    // MAC-address of Bluetooth module (you must edit this line)
    private static String bluetoothDeviceAddress = null;
    private static String bluetoothDeviceName = null;

    // url of gateway if in use
    private static String gatewayUrl = null;

    // public final static int RECEIVE_MESSAGE      = 1;
    public final static int REQUEST_ENABLE_BT = 3;
    public final static int SETTINGS_ACTIVITY = 7;
    public final static int LEAVE_BLUETOOTH_ON = 11;

    // note that the CAR constants are stored in the option property of the field object
    // this is a short

    // public static final short CAR_MASK            = 0xff;

    public static final short CAR_NONE = 0x000;
    //public static final int CAR_ANY               = 0x0ff;
    public static final short CAR_FLUENCE = 0x001;
    public static final short CAR_ZOE_Q210 = 0x002;
    public static final short CAR_KANGOO = 0x004;
    // public static final short CAR_TWIZY          = 0x008;     // you'll never know ;-)
    public static final short CAR_X10 = 0x010; // not used
    public static final short CAR_ZOE_R240 = 0x020;
    public static final short CAR_ZOE_Q90 = 0x040;
    public static final short CAR_ZOE_R90 = 0x080;

    public static final short FIELD_TYPE_MASK = 0x700;
    //public static final short FIELD_TYPE_UNSIGNED = 0x000;
    public static final short FIELD_TYPE_SIGNED = 0x100;
    public static final short FIELD_TYPE_STRING = 0x200; // not implemented yet

    public static final short TOAST_NONE = 0;
    public static final short TOAST_ELM = 1;
    public static final short TOAST_ELMCAR = 2;

    public static final double reduction = 9.32; // update suggested by Loc Dao

    // private StringBuilder sb = new StringBuilder();
    // private String buffer = "";

    // private int count;
    // private long start;

    private boolean visible = true;
    public boolean leaveBluetoothOn = false;
    private boolean returnFromWidget = false;

    public static Fields fields = Fields.getInstance();

    public static Device device = null;

    private static MainActivity instance = null;

    public static boolean safeDrivingMode = true;
    public static boolean bluetoothBackgroundMode = false;
    public static boolean debugLogMode = false;
    public static boolean fieldLogMode = false;

    public static boolean dataExportMode = false;
    public static DataLogger dataLogger = null; // rather use singleton in onCreate

    public static int car = CAR_NONE;

    private static boolean isDriving = false;

    public static boolean milesMode = false;
    public static int toastLevel = 1;

    private DebugListener debugListener = null;

    // private Fragment actualFragment;

    static private Resources res;

    // bluetooth stuff
    private MenuItem bluetoothMenutItem = null;
    public final static int BLUETOOTH_DISCONNECTED = 21;
    public final static int BLUETOOTH_SEARCH = 22;
    public final static int BLUETOOTH_CONNECTED = 23;

    //The BroadcastReceiver that listens for bluetooth broadcasts
    private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

            if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
                //Device has disconnected

                // only resume if this activity is also visible
                if (visible) {
                    // stop reading
                    if (device != null) {
                        device.stopAndJoin();
                    }

                    // inform user
                    setTitle(TAG + " - disconnected");
                    setBluetoothState(BLUETOOTH_DISCONNECTED);
                    toast(R.string.toast_BluetoothLost);

                    // try to reconnect
                    onResume();
                }
            }
        }
    };

    public static MainActivity getInstance() {
        return instance;
    }

    public static void debug(String text) {
        Log.d(TAG, text);
        if (debugLogMode) {
            SimpleDateFormat sdf = new SimpleDateFormat(getStringSingle(R.string.format_YMDHMSs),
                    Locale.getDefault());
            DebugLogger.getInstance().log(sdf.format(Calendar.getInstance().getTime()) + ": " + text);
        }
    }

    /* TODO we should move to simply always provide the level in the toast() call instead of all those if's in the code */
    public static void toast(int level, final String message) {
        if (level > toastLevel)
            return;
        if (instance != null)
            instance.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(instance, message, Toast.LENGTH_SHORT).show();
                }
            });
    }

    public static void toast(final String message) {
        if (instance != null)
            instance.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(instance, message, Toast.LENGTH_SHORT).show();
                }
            });
    }

    public static void toast(String format, Object... arguments) {
        final String finalMessage = String.format(Locale.getDefault(), format, arguments);
        if (instance != null)
            instance.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(instance, finalMessage, Toast.LENGTH_SHORT).show();
                }
            });
    }

    public static void toast(final int resource) {
        if (instance != null)
            instance.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    final String finalMessage = getStringSingle(resource);
                    Toast.makeText(instance, finalMessage, Toast.LENGTH_SHORT).show();
                }
            });
    }

    public void loadSettings() {
        debug("MainActivity: loadSettings");
        try {
            SharedPreferences settings = getSharedPreferences(PREFERENCES_FILE, 0);
            bluetoothDeviceName = settings.getString("deviceName", null);
            bluetoothDeviceAddress = settings.getString("deviceAddress", null);
            gatewayUrl = settings.getString("gatewayUrl", null);
            // String dataFormat = settings.getString("dataFormat", "crdt");
            String deviceType = settings.getString("device", "Arduino");
            safeDrivingMode = settings.getBoolean("optSafe", true);
            bluetoothBackgroundMode = settings.getBoolean("optBTBackground", false);
            milesMode = settings.getBoolean("optMiles", false);
            dataExportMode = settings.getBoolean("optDataExport", false);
            debugLogMode = settings.getBoolean("optDebugLog", false);
            fieldLogMode = settings.getBoolean("optFieldLog", false);
            toastLevel = settings.getInt("optToast", 1);

            if (bluetoothDeviceName != null && !bluetoothDeviceName.isEmpty() && bluetoothDeviceName.length() > 4)
                BluetoothManager.getInstance()
                        .setDummyMode(bluetoothDeviceName.substring(0, 4).compareTo("HTTP") == 0);

            String carStr = settings.getString("car", "None");
            switch (carStr) {
            case "None":
                car = CAR_NONE;
                break;
            case "Zo":
            case "ZOE":
            case "ZOE Q210":
                car = CAR_ZOE_Q210;
                break;
            case "ZOE R240":
                car = CAR_ZOE_R240;
                break;
            case "ZOE Q90":
                car = CAR_ZOE_Q90;
                break;
            case "ZOE R90":
                car = CAR_ZOE_R90;
                break;
            case "Fluence":
                car = CAR_FLUENCE;
                break;
            case "Kangoo":
                car = CAR_KANGOO;
                break;
            case "X10":
                car = CAR_X10;
                break;
            }

            // as the settings may have changed, we need to reload different things

            // create a new device
            switch (deviceType) {
            case "Bob Due":
                device = new BobDue();
                break;
            case "ELM327":
                device = new ELM327();
                break;
            case "ELM327Http":
                device = new ELM327OverHttp();
                break;
            default:
                device = null;
                break;
            }

            // since the car type may have changed, reload the frame timings and fields
            Frames.getInstance().load();
            fields.load();

            if (device != null) {
                // initialise the connection
                device.initConnection();

                // register application wide fields
                // registerApplicationFields(); // now done in Fields.load
            }

            // after loading PREFERENCES we may have new values for "dataExportMode"
            dataExportMode = dataLogger.activate(dataExportMode);
        } catch (Exception e) {
            MainActivity.debug(e.getMessage());
            StackTraceElement[] st = e.getStackTrace();
            for (int i = 0; i < st.length; i++)
                MainActivity.debug(st[i].toString());
        }
    }

    public void registerApplicationFields() {
        if (safeDrivingMode) {
            // speed
            Field field = fields.getBySID("5d7.0");
            if (field != null) {
                field.addListener(MainActivity.getInstance()); // callback is onFieldUpdateEvent
                if (device != null)
                    device.addApplicationField(field, 1000); // query every second
            }
        } else {
            Field field = fields.getBySID("5d7.0");
            if (field != null) {
                field.removeListener(MainActivity.getInstance());
                if (device != null)
                    device.removeApplicationField(field);
            }
        }
    }

    protected void updateActionBar() {
        switch (viewPager.getCurrentItem()) {
        case 0:
            actionBar.setIcon(R.mipmap.ic_launcher);
            break;
        case 1:
            actionBar.setIcon(R.mipmap.fragement_technical);
            break;
        case 2:
            actionBar.setIcon(R.mipmap.fragement_experimental);
            break;
        default:
            break;
        }
    }

    private ViewPager viewPager;
    private ActionBar actionBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // always create an instance

        // needed to get strings from resources in non-Activity classes
        res = getResources();

        // dataLogger = DataLogger.getInstance();
        dataLogger = new DataLogger();

        debug("MainActivity: onCreate");

        instance = this;

        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // navigation bar
        AppSectionsPagerAdapter appSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager());
        actionBar = getSupportActionBar();
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
        //actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        viewPager = (ViewPager) findViewById(R.id.main);
        viewPager.setAdapter(appSectionsPagerAdapter);
        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                //actionBar.setSelectedNavigationItem(position);
                updateActionBar();
            }
        });
        updateActionBar();

        /*
        for (int i = 0; i < appSectionsPagerAdapter.getCount(); i++) {
        actionBar.addTab(
                actionBar.newTab()
                        .setText(appSectionsPagerAdapter.getPageTitle(i))
                        .setTabListener(MainActivity.this));
        }
        */

        // load the initial "main" fragment
        //loadFragement(new MainFragment());

        setTitle(TAG + " - not connected");
        setBluetoothState(BLUETOOTH_DISCONNECTED);

        // tabs
        //final ActionBar actionBar = getSupportActionBar();
        // Specify that tabs should be displayed in the action bar.

        // open the database
        CanzeDataSource.getInstance(getBaseContext()).open();
        // cleanup
        CanzeDataSource.getInstance().cleanUp();

        // setup cleaning (once every hour)
        Runnable cleanUpRunnable = new Runnable() {
            @Override
            public void run() {
                CanzeDataSource.getInstance().cleanUp();
            }
        };
        Handler handler = new Handler();
        handler.postDelayed(cleanUpRunnable, 60 * 1000);

        // register for bluetooth changes
        IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        this.registerReceiver(broadcastReceiver, intentFilter);

        // configure Bluetooth manager
        BluetoothManager.getInstance().setBluetoothEvent(new BluetoothEvent() {
            @Override
            public void onBeforeConnect() {
                setBluetoothState(BLUETOOTH_SEARCH);
            }

            @Override
            public void onAfterConnect(BluetoothSocket bluetoothSocket) {
                device.init(visible);
                device.registerFilters();

                // set title
                debug("MainActivity: onAfterConnect > set title");
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        setTitle(TAG + " - connected to <" + bluetoothDeviceName + "@" + bluetoothDeviceAddress
                                + ">");
                        setBluetoothState(BLUETOOTH_CONNECTED);
                    }
                });
            }

            @Override
            public void onBeforeDisconnect(BluetoothSocket bluetoothSocket) {
            }

            @Override
            public void onAfterDisconnect() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        setTitle(TAG + " - disconnected");
                    }
                });
            }
        });
        // detect hardware status
        int BT_STATE = BluetoothManager.getInstance().getHardwareState();
        if (BT_STATE == BluetoothManager.STATE_BLUETOOTH_NOT_AVAILABLE)
            toast("Sorry, but your device doesn't seem to have Bluetooth support!");
        else if (BT_STATE == BluetoothManager.STATE_BLUETOOTH_NOT_ACTIVE) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, 1);
        }

        // load settings
        // - includes the reader
        // - includes the decoder
        //loadSettings(); --> done in onResume

        // load fields from static code
        debug("Loaded fields: " + fields.size());

        // load fields
        //final SharedPreferences settings = getSharedPreferences(PREFERENCES_FILE, 0);
        (new Thread(new Runnable() {
            @Override
            public void run() {
                debug("Loading fields last field values from database");
                for (int i = 0; i < fields.size(); i++) {
                    Field field = fields.get(i);
                    field.setCalculatedValue(CanzeDataSource.getInstance().getLast(field.getSID()));
                    //debug("MainActivity: Setting "+field.getSID()+" = "+field.getValue());
                    //f.setValue(settings.getFloat(f.getUniqueID(), 0));
                }
                debug("Loading fields last field values from database (done)");
            }
        })).start();
    }

    @Override
    public void onResume() {
        debug("MainActivity: onResume");

        instance = this;

        visible = true;
        super.onResume();

        // if returning from a single widget activity, we have to leave here!
        if (returnFromWidget) {
            returnFromWidget = false;
            return;
        }

        if (!leaveBluetoothOn) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    setBluetoothState(BLUETOOTH_DISCONNECTED);
                }
            });
            (new Thread(new Runnable() {
                @Override
                public void run() {
                    reloadBluetooth();
                }
            })).start();
        }

        final SharedPreferences settings = getSharedPreferences(PREFERENCES_FILE, 0);
        if (!settings.getBoolean("disclaimer", false)) {

            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);

            // set title
            alertDialogBuilder.setTitle(R.string.prompt_Disclaimer);

            // set dialog message
            String yes = getStringSingle(R.string.prompt_Accept);
            String no = getStringSingle(R.string.prompt_Decline);

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            float width = size.x;
            // int height = size.y;
            width = width / getResources().getDisplayMetrics().scaledDensity;
            if (width <= 480) {
                yes = getStringSingle(R.string.default_Yes);
                no = getStringSingle(R.string.default_No);
            }

            alertDialogBuilder.setMessage(Html.fromHtml(getStringSingle(R.string.prompt_DisclaimerText)))
                    .setCancelable(true).setPositiveButton(yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            // if this button is clicked, close
                            SharedPreferences.Editor editor = settings.edit();
                            editor.putBoolean("disclaimer", true);
                            // editor.commit();
                            editor.apply();
                            // current activity
                            dialog.cancel();
                        }
                    }).setNegativeButton(no, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            // if this button is clicked, just close
                            // the dialog box and do nothing
                            dialog.cancel();
                            //MainActivity.this.finishAffinity(); requires API16
                            MainActivity.this.finish();
                            android.os.Process.killProcess(android.os.Process.myPid());
                            System.exit(0);
                        }
                    });

            // create alert dialog
            AlertDialog alertDialog = alertDialogBuilder.create();

            // show it
            alertDialog.show();
        }
    }

    public void reloadBluetooth() {
        reloadBluetooth(true);
    }

    public void reloadBluetooth(boolean reloadSettings) {
        // re-load the settings if asked to
        if (reloadSettings)
            loadSettings();

        // try to get a new BT thread
        BluetoothManager.getInstance().connect(bluetoothDeviceAddress, true, BluetoothManager.RETRIES_INFINITE);
    }

    @Override
    public void onPause() {
        debug("MainActivity: onPause");
        debug("MainActivity: onPause > leaveBluetoothOn = " + leaveBluetoothOn);
        visible = false;

        // stop here if BT should stay on!
        if (bluetoothBackgroundMode) {
            super.onPause();
            return;
        }

        if (!leaveBluetoothOn) {
            if (device != null)
                device.clearFields();
            debug("MainActivity: stopping BT");
            stopBluetooth();
        }

        super.onPause();
    }

    public void stopBluetooth() {
        stopBluetooth(true);
    }

    public void stopBluetooth(boolean reset) {
        if (device != null) {
            // stop the device
            debug("MainActivity: stopBluetooth > stopAndJoin");
            device.stopAndJoin();
            // remove reference
            if (reset) {
                device.clearFields();
                device.registerFilters();
            }
        }
        // disconnect BT
        debug("MainActivity: stopBluetooth > BT disconnect");
        BluetoothManager.getInstance().disconnect();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        MainActivity.debug("MainActivity: onActivityResult");
        MainActivity.debug("MainActivity: onActivityResult > requestCode = " + requestCode);
        MainActivity.debug("MainActivity: onActivityResult > resultCode = " + resultCode);

        // this must be set in any case
        leaveBluetoothOn = false;

        if (requestCode == SETTINGS_ACTIVITY) {
            // load settings
            loadSettings();
        } else if (requestCode == LEAVE_BLUETOOTH_ON) {
            MainActivity.debug("MainActivity: onActivityResult > " + LEAVE_BLUETOOTH_ON);
            returnFromWidget = true;
            // register fields this activity needs
            /*
            registerFields();
             */
        } else
            super.onActivityResult(requestCode, resultCode, data);
    }

    /*
    public void saveFields()
    {
    // safe fields
    SharedPreferences settings = getSharedPreferences(PREFERENCES_FILE, 0);
    SharedPreferences.Editor editor = settings.edit();
    for(int i=0; i<fields.size(); i++)
    {
        Field f = fields.get(i);
        editor.putFloat(f.getUniqueID(),(float) f.getRawValue());
        //debug("Setting "+f.getUniqueID()+" = "+f.getRawValue());
    }
    editor.commit();
    }
    */

    @Override
    protected void onDestroy() {
        debug("MainActivity: onDestroy");

        dataLogger.destroy(); // clean up

        if (device != null) {
            // stop the device nicely
            device.stopAndJoin();
            device.clearFields();
            device.registerFilters();
        }
        // disconnect the bluetooth
        BluetoothManager.getInstance().disconnect();

        // un-register for bluetooth changes
        this.unregisterReceiver(broadcastReceiver);

        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        // get a reference to the bluetooth action button
        bluetoothMenutItem = menu.findItem(R.id.action_bluetooth);
        // and put the right view on it
        bluetoothMenutItem.setActionView(R.layout.animated_menu_item);
        // set the correct initial state
        setBluetoothState(BLUETOOTH_DISCONNECTED);
        // get access to the image view
        ImageView imageView = (ImageView) bluetoothMenutItem.getActionView()
                .findViewById(R.id.animated_menu_item_action);
        // define an action
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        toast(getStringSingle(R.string.toast_Reconnecting));
                        stopBluetooth();
                        reloadBluetooth();
                    }
                })).start();
            }
        });

        return true;
    }

    private void setBluetoothState(int btState) {
        if (bluetoothMenutItem != null) {
            final ImageView imageView = (ImageView) bluetoothMenutItem.getActionView()
                    .findViewById(R.id.animated_menu_item_action);

            // stop the animation if there is one running
            AnimationDrawable frameAnimation;
            if (imageView.getBackground() instanceof AnimationDrawable) {
                frameAnimation = (AnimationDrawable) imageView.getBackground();
                if (frameAnimation.isRunning())
                    frameAnimation.stop();
            }

            switch (btState) {
            case BLUETOOTH_DISCONNECTED:
                imageView.setBackgroundResource(R.mipmap.bluetooth_none);
                break;
            case BLUETOOTH_CONNECTED:
                imageView.setBackgroundResource(R.mipmap.bluetooth_3);
                break;
            case BLUETOOTH_SEARCH:
                runOnUiThread(new Runnable() {
                    @SuppressLint("NewApi")
                    @Override
                    public void run() {
                        AnimationDrawable drawable = (AnimationDrawable) ContextCompat
                                .getDrawable(getApplicationContext(), R.anim.animation_bluetooth);
                        // Use setBackgroundDrawable() for API 14 and 15 and setBackground() for API 16+:
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                            imageView.setBackground(drawable);
                        } else {
                            //noinspection deprecation
                            imageView.setBackgroundDrawable(drawable);
                        }
                        AnimationDrawable frameAnimation = (AnimationDrawable) imageView.getBackground();
                        frameAnimation.start();
                    }
                });
                break;
            default:
                break;
            }
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        // start the settings activity
        if (id == R.id.action_settings) {

            if (isSafe()) {
                // run a toast
                toast(R.string.toast_WaitingSettings);

                (new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // give the toast a moment to appear
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        if (device != null) {
                            // stop the BT device
                            device.stopAndJoin();
                            device.clearFields();
                            device.registerFilters();
                            BluetoothManager.getInstance().disconnect();
                        }

                        // load the activity
                        Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
                        startActivityForResult(intent, SETTINGS_ACTIVITY);
                    }
                })).start();
                return true;
            }
        }
        // see AppSectionsPagerAdapter for the right sequence
        else if (id == R.id.action_main) {
            //loadFragement(new MainFragment());
            viewPager.setCurrentItem(0, true);
            updateActionBar();

        } else if (id == R.id.action_technical) {
            //loadFragement(new TechnicalFragment());
            viewPager.setCurrentItem(1, true);
            updateActionBar();

        } else if (id == R.id.action_experimental) {
            //loadFragement(new ExperimentalFragment());
            viewPager.setCurrentItem(2, true);
            updateActionBar();

        }
        //else if (id == R.id.action_bluetooth) {
        //}

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onFieldUpdateEvent(Field field) {
        if (field.getSID().equals("5d7.0")) {
            //debug("Speed "+field.getValue());
            isDriving = (field.getValue() > 10);
        }
    }

    public static boolean isSafe() {
        boolean safe = !isDriving || !safeDrivingMode;
        if (!safe) {
            Toast.makeText(MainActivity.instance, R.string.toast_NotWhileDriving, Toast.LENGTH_LONG).show();
        }
        return safe;
    }

    public static boolean isZOE() {
        return (car == CAR_X10 || car == CAR_ZOE_Q90 || car == CAR_ZOE_Q210 || car == CAR_ZOE_R90
                || car == CAR_ZOE_R240);
    }

    public static boolean isFluKan() {
        return (car == CAR_FLUENCE || car == CAR_KANGOO);
    }

    public static String getBluetoothDeviceAddress() {
        if ("HTTP Gateway".equals(bluetoothDeviceName))
            return gatewayUrl;
        return bluetoothDeviceAddress;
    }

    public static String getStringSingle(int resId) {
        return res.getString(resId);
    }

    public static String[] getStringList(int resId) {
        return res.getStringArray(resId);
    }

    public void setDebugListener(DebugListener debugListener) {
        this.debugListener = debugListener;
    }

    public void dropDebugMessage(String msg) {
        if (debugListener != null)
            debugListener.dropDebugMessage(msg);
    }

}