cz.tomsuch.lampicka.activities.LampActivity.java Source code

Java tutorial

Introduction

Here is the source code for cz.tomsuch.lampicka.activities.LampActivity.java

Source

/**    Copyright (C) 2013 Marek Sebera <marek@msebera.cz>
 *
 *    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
 *    (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 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 cz.tomsuch.lampicka.activities;

import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import cz.tomsuch.lampicka.R;
import cz.tomsuch.lampicka.abstracts.CustomSeekBar;
import cz.tomsuch.lampicka.enums.BluetoothLampBacklightMode;
import cz.tomsuch.lampicka.enums.BluetoothLampColorSetMode;
import cz.tomsuch.lampicka.enums.BluetoothLampCommand;
import cz.tomsuch.lampicka.enums.BluetoothLampEffect;
import cz.tomsuch.lampicka.impl.DefaultBluetoothLamp;
import cz.tomsuch.lampicka.interfaces.BluetoothInputLineListener;
import cz.tomsuch.lampicka.interfaces.BluetoothLampCommandListener;
import cz.tomsuch.lampicka.interfaces.OnColorChangedListener;
import cz.tomsuch.lampicka.interfaces.PalleteDialogColorListener;
import cz.tomsuch.lampicka.pallete.PalleteDialog;
import cz.tomsuch.lampicka.util.BluetoothDeviceWrapper;
import cz.tomsuch.lampicka.util.BluetoothSocketListener;
import cz.tomsuch.lampicka.util.ColorPickerView;
import cz.tomsuch.lampicka.util.FixedBluetoothSocket;
import cz.tomsuch.lampicka.util.Preferences;

/**
 * Displays controls and status for selected BluetoothDevice (aka lamp).
 * 
 * For now it only runs in portrait mode
 * 
 * 
 * @author Marek Sebera <marek@msebera.cz>
 * */
public final class LampActivity extends FragmentActivity {

    /**
     * Intent extra parameter, must contain BluetoothDevice
     * */
    private BluetoothDeviceWrapper device;
    private ParcelUuid serviceUuid;
    private FixedBluetoothSocket socket;
    private BluetoothSocketListener bsl;
    private Handler handler = new Handler();
    private boolean isReceiverRegistered = false;
    private BluetoothLampEffect currentEffect = BluetoothLampEffect.NO_EFFECT;
    private BluetoothLampBacklightMode currentBacklightEffect = BluetoothLampBacklightMode.ON;
    private BluetoothLampColorSetMode currentColorSetMode = BluetoothLampColorSetMode.CONTINUOUS;
    private DefaultBluetoothLamp lamp = null;
    private ColorPickerView colorPicker;
    private Button lamp_select, lamp_connect_disconnect, lamp_effects_select, lamp_backlight_select, lamp_pallet,
            lamp_turn_off, lamp_set_with_effect;
    private TextView lamp_name, lamp_humidity, lamp_lightness, lamp_intensity, lamp_temperature;
    private CustomSeekBar lamp_color_intensity;
    protected String lamp_version, lamp_serial_number;

    /**
     * If user clicks on software or hardware back button, it finished whole
     * application according to UI-flow
     * */
    @Override
    public void onBackPressed() {
        setResult(RESULT_OK);
        finish();
    }

    /**
     * Handles input from BluetoothSocket
     * */
    private BluetoothInputLineListener bluetoothInputListener = new BluetoothInputLineListener() {

        @Override
        public void handleReceivedLine(String line) {
            handleInput(line);
        }
    };

    /**
     * Listens for selected color, if dialog is not dismissed
     * */
    private PalleteDialogColorListener palleteListener = new PalleteDialogColorListener() {

        @Override
        public void colorSelected(int color) {
            colorPicker.setColor(color);
            onColorChangedListener.colorChanged(color, true, true);
        }
    };

    /**
     * Listener for various UI controls
     * */
    private OnClickListener onClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.LAMP_EFFECT_CHOOSER:
                displayEffectChooserDialog();
                break;
            case R.id.LAMP_SET_COLOR_EFFECT_CHOOSER:
                displayLampColorSetModeChooserDialog();
                break;
            case R.id.LAMP_PALLET_CHOOSER:
                displayColorPaletteChooserDialog();
                break;
            case R.id.LAMP_BACKLIGHT_CHOOSER:
                displayBacklightChooserDialog();
                break;
            case R.id.LAMP_TURN_OFF:
                lamp.setColorHard(Color.BLACK);
                setCurrentEffect(BluetoothLampEffect.NO_EFFECT);
                break;
            case R.id.LAMP_CONNECT_DISCONNECT:
                if (socket != null) {
                    disconnect();
                } else {
                    discoverDevice();
                }
                break;
            case R.id.LAMP_CHOOSER:
                finish();
                break;
            }
        }
    };

    /**
     * Helper method to set current effect
     * */
    private void setCurrentEffect(BluetoothLampEffect effect) {
        currentEffect = effect;
        lamp_effects_select.setText(currentEffect.getName());
    }

    /**
     * Displays color chooser pallete dialog
     * */
    protected void displayColorPaletteChooserDialog() {
        PalleteDialog.attach(getSupportFragmentManager(), palleteListener);
    }

    /**
     * Listener for ColorPickerView widget
     * */
    OnColorChangedListener onColorChangedListener = new OnColorChangedListener() {

        private long lastChange = 0;
        private long minDelta = 150;

        @Override
        public void colorChanged(int color, boolean up, boolean center) {
            switch (currentColorSetMode) {
            default:
            case CONTINUOUS:
                long now = System.currentTimeMillis();
                if (now - lastChange > minDelta) {
                    setCurrentEffect(BluetoothLampEffect.NO_EFFECT);
                    lamp.setColorHard(color);
                    lastChange = now;
                }
                break;
            case HARD:
                if (up) {
                    setCurrentEffect(BluetoothLampEffect.NO_EFFECT);
                    lamp.setColorHard(color);
                }
                break;
            case TRANSITION:
                if (up) {
                    setCurrentEffect(BluetoothLampEffect.NO_EFFECT);
                    lamp.setColorWithCrossFade(color);
                }
                break;
            }
        }
    };

    /**
     * Listens to discovery of service UUIDs on given BluetoothDevice
     * */
    private BroadcastReceiver uuidsReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(BluetoothDevice.ACTION_UUID)) {
                Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
                if (uuids != null) {
                    for (Parcelable u : uuids) {
                        if (u instanceof ParcelUuid && serviceUuid == null) {
                            serviceUuid = (ParcelUuid) u;
                            Preferences.getInstance().saveLastServiceUuid(serviceUuid);
                            connectToService();
                        }
                    }
                }
            }
        }
    };

    /**
     * Listener to consume commands and execute them on device
     * */
    private BluetoothLampCommandListener bluetoothLampCommandListener = new BluetoothLampCommandListener() {
        private int errorCounter = 0;

        /**
         * Sends request to BluetoothDevice over socket. Does not fail, if the
         * socket is not yet initialized
         * 
         * @param msg
         *            Request string, must not contain CR/LF
         * */
        @Override
        public void sendData(String msg) {
            if (socket != null && socket.isConnected()) {
                if (msg != null && msg.length() > 0) {
                    OutputStream outStream;
                    try {
                        Log.d("Send Data", msg);
                        msg = msg + "\n";
                        outStream = socket.getOutputStream();
                        byte[] byteString = msg.getBytes();
                        outStream.write(byteString);
                        errorCounter = 0;
                    } catch (Throwable e) {
                        errorCounter++;
                        if (errorCounter == 3) {
                            showToast(R.string.toast_disconnected);
                            disconnect();
                        }
                    }
                }
            }
        }

        @Override
        public void sendData(BluetoothLampCommand command) {
            sendData(command.toString());
        }
    };

    /**
     * Matches info about lamp (returned data)
     * */
    private static final Pattern LAMP_INFO = Pattern.compile(
            "^0 ([-]?[0-9]|[-]?[1-9][0-9]|[-]?100):([0-9]|[1-9][0-9]|100):(10[0-2][0-4]|[1-9][1-9][0-9]|[1-9][0-9]|[0-9])$");
    private static final Pattern LAMP_NAME = Pattern.compile("^2 .*$");
    private static final Pattern LAMP_VERSION = Pattern.compile("^3 .*$");
    private static final Pattern LAMP_SERIAL = Pattern.compile("^1 [0-9]{8}$");
    private static final Pattern LAMP_SETTINGS = Pattern.compile("^4 .*$");

    /**
     * If there is discovered Service UUID, it will initialize connection,
     * creates BluetoothSocket and retrieves informations about device (X,i)
     * */
    private void connectToService() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    setStatus(R.string.lamp_connecting);
                    if (socket != null) {
                        socket.close();
                    }
                    socket = new FixedBluetoothSocket(
                            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(device.getAddress())
                                    .createRfcommSocketToServiceRecord(serviceUuid.getUuid()));

                    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                    if (adapter.isDiscovering())
                        adapter.cancelDiscovery();

                    if (socket != null) {
                        try {
                            socket.connect();
                        } catch (Exception e) {
                            e.printStackTrace();
                            disconnect();
                            showToast(R.string.toast_cannot_connect_to_bluetooth_lamp);
                            return;
                        }
                    }
                    if (socket.isConnected()) {
                        afterSocketConnected();
                        bsl = new BluetoothSocketListener(socket, bluetoothInputListener);
                        new Thread(bsl).start();
                    } else {
                        showToast(R.string.toast_could_not_connect);
                        disconnect();
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    /**
     * Lamp color set mode chooser dialog listener
     * 
     * {@link BluetoothLampColorSetMode}
     * {@link #displayLampColorSetModeChooserDialog()}
     * */
    private DialogInterface.OnClickListener lampColorSetModeListener = new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            currentColorSetMode = BluetoothLampColorSetMode.getEffectNo(which);
            lamp_set_with_effect.setText(currentColorSetMode.getName());
            dialog.dismiss();
        }
    };

    /**
     * Lamp color set mode chooser dialog builder
     * 
     * {@link BluetoothLampColorSetMode}
     * */
    protected void displayLampColorSetModeChooserDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.dialog_title_select_color_set_mode);
        String[] items = new String[BluetoothLampColorSetMode.values().length];
        int selected = 0;
        int index = 0;
        for (BluetoothLampColorSetMode effect : BluetoothLampColorSetMode.values()) {
            items[index] = getString(effect.getName());
            if (currentColorSetMode.equals(effect)) {
                selected = index;
            }
            index++;
        }
        builder.setSingleChoiceItems(items, selected, lampColorSetModeListener);
        builder.setNeutralButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.create().show();
    }

    /**
     * Effect chooser dialog listener
     * 
     * {@link BluetoothLampEffect} {@link #displayEffectChooserDialog()}
     * */
    private DialogInterface.OnClickListener effectSelectedListener = new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            currentEffect = BluetoothLampEffect.getEffectNo(which);
            lamp_effects_select.setText(currentEffect.getName());
            if (currentEffect.equals(BluetoothLampEffect.NO_EFFECT)) {
                lamp.setColorHard(Color.BLACK);
            } else {
                lamp.setEffect(currentEffect);
            }
            dialog.dismiss();
        }
    };

    /**
     * Effect chooser dialog builder
     * 
     * {@link BluetoothLampEffect}
     * */
    protected void displayEffectChooserDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.lamp_select_effect);
        String[] items = new String[BluetoothLampEffect.values().length];
        int selected = 0;
        int index = 0;
        for (BluetoothLampEffect effect : BluetoothLampEffect.values()) {
            items[index] = getString(effect.getName());
            if (currentEffect.equals(effect)) {
                selected = index;
            }
            index++;
        }
        builder.setSingleChoiceItems(items, selected, effectSelectedListener);
        builder.setNeutralButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.create().show();
    }

    /**
     * Backlight effect chooser dialog listener
     * 
     * {@link BluetoothLampBacklightMode}
     * {@link #displayBacklightChooserDialog()}
     * */
    private DialogInterface.OnClickListener backlightSelectedListener = new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            currentBacklightEffect = BluetoothLampBacklightMode.getEffectNo(which);
            lamp_backlight_select.setText(currentBacklightEffect.getName());
            lamp.setBacklightEffect(currentBacklightEffect);
            dialog.dismiss();
        }
    };
    private OnSeekBarChangeListener colorIntensityListener = new OnSeekBarChangeListener() {

        private long lastChange = 0;
        private long minDelta = 50;

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            long now = System.currentTimeMillis();
            if (now - lastChange > minDelta) {
                progress++;
                lamp.setColorIntensity(progress);
                lamp_intensity.setText(String.format(getString(R.string.lamp_intensity), progress));
                lastChange = now;
            }
        }
    };

    /**
     * Backlight effect chooser dialog builder
     * 
     * {@link BluetoothLampBacklightMode}
     * */
    protected void displayBacklightChooserDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.lamp_select_backlight_effect);
        String[] items = new String[BluetoothLampBacklightMode.values().length];
        int selected = 0;
        int index = 0;
        for (BluetoothLampBacklightMode effect : BluetoothLampBacklightMode.values()) {
            items[index] = getString(effect.getName());
            if (currentBacklightEffect.equals(effect)) {
                selected = index;
            }
            index++;
        }
        builder.setSingleChoiceItems(items, selected, backlightSelectedListener);
        builder.setNeutralButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.create().show();
    }

    /**
     * Helper method, can be called from background threads
     * 
     * @param message
     *            String to be displayed
     * */
    protected void showToast(final int message) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(LampActivity.this, message, Toast.LENGTH_SHORT).show();
            }

        });
    }

    /**
     * Executes after the socket is connected
     * */
    private void afterSocketConnected() {
        setRequestedOrientation(getScreenOrientation());
        lamp.getCurrentSettings();
        setLampIsConnected(true);
        runInfoLoop();
    }

    private int getScreenOrientation() {
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int width = dm.widthPixels;
        int height = dm.heightPixels;
        int orientation;
        // if the device's natural orientation is portrait:
        if ((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && height > width
                || (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && width > height) {
            switch (rotation) {
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_180:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            case Surface.ROTATION_270:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            default:
                Log.e("WTF", "Unknown screen orientation. Defaulting to " + "portrait.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            }
        }
        // if the device's natural orientation is landscape or if the device
        // is square:
        else {
            switch (rotation) {
            case Surface.ROTATION_0:
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            case Surface.ROTATION_90:
                orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                break;
            case Surface.ROTATION_180:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
                break;
            case Surface.ROTATION_270:
                orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
                break;
            default:
                Log.e("WTF", "Unknown screen orientation. Defaulting to " + "landscape.");
                orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                break;
            }
        }

        return orientation;
    }

    /**
     * Sets whole UI state according to lamp connect status
     * */
    private void setLampIsConnected(final boolean isConnected) {
        setUIControlsState(isConnected);
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                lamp_connect_disconnect.setEnabled(true);
                lamp_connect_disconnect.setText(isConnected ? R.string.lamp_disconnect : R.string.lamp_connect);
            }
        });
    }

    /**
     * Runs UUID discovery on device
     * */
    private void discoverDevice() {
        lamp_connect_disconnect.setEnabled(false);
        lamp_name.setText(device.getName());
        lamp_select.setEnabled(false);
        serviceUuid = Preferences.getInstance().getLastBluetoothServiceUuid();
        if (serviceUuid == null) {
            setStatus(R.string.lamp_discovering);
            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(device.getAddress()).fetchUuidsWithSdp();
        } else {
            connectToService();
        }
    }

    /**
     * Handles incoming line from BluetoothDevice
     * 
     * @param string
     *            incoming data, can be multiple lines
     * */
    public void handleInput(final String string) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                String[] lines = string.split(System.getProperty("line.separator"));
                for (String line : lines) {
                    line = line.trim();
                    Log.d("Handle Line", line);
                    if (LAMP_INFO.matcher(line).matches()) {
                        setLampInfo(line);
                        lamp.getLampVersion();
                    } else if (LAMP_NAME.matcher(line).matches()) {
                        if (line.length() > 2)
                            lamp_name.setText(line.substring(2));
                        lamp.getLampInfo();
                    } else if (LAMP_VERSION.matcher(line).matches()) {
                        lamp_version = line.substring(2);
                        lamp.getLampSerial();
                    } else if (LAMP_SERIAL.matcher(line).matches()) {
                        lamp_serial_number = line.substring(2);
                    } else if (LAMP_SETTINGS.matcher(line).matches()) {
                        applyLampSettings(line.substring(2));
                        lamp.getLampId();
                    }
                }
            }
        });
    }

    /**
     * Parses response from lamp, setting remote settings to local UI controls
     * */
    private void applyLampSettings(String substring) {
        String[] parts = substring.split(":");
        if (parts == null || parts.length != 6)
            return;
        int counter = 0;
        int red = 0, green = 0, blue = 0;
        int currentEffect = 0, currentBacklightEffect = 0, currentColorIntensity = 0;
        for (String part : parts) {
            int value = Integer.parseInt(part);
            switch (counter) {
            case 0:
                red = value;
                break;
            case 1:
                green = value;
                break;
            case 2:
                blue = value;
                break;
            case 3:
                currentBacklightEffect = value;
                break;
            case 4:
                currentEffect = value;
                break;
            case 5:
                currentColorIntensity = value;
                break;
            }
            counter++;
        }
        this.lamp_color_intensity.setProgressAndThumb(currentColorIntensity);
        this.colorPicker.setColor(Color.rgb(red, green, blue));
        this.currentEffect = BluetoothLampEffect.getEffectRawNo(currentEffect);
        this.currentBacklightEffect = BluetoothLampBacklightMode.getEffectRawNo(currentBacklightEffect);
        this.lamp_effects_select.setText(this.currentEffect.getName());
        this.lamp_backlight_select.setText(this.currentBacklightEffect.getName());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lamp = new DefaultBluetoothLamp(bluetoothLampCommandListener);
        setContentView(R.layout.activity_lamp_controls);
        setupUIControls();
        this.device = Preferences.getInstance().getLastBluetoothDevice();
        setLampIsConnected(false);
    }

    /**
     * If user returns to this activity (or comes at first), we register
     * receiver, as it does nothing itself
     * */
    @Override
    protected void onResume() {
        try {
            IntentFilter uuidsFilter = new IntentFilter(BluetoothDevice.ACTION_UUID);
            registerReceiver(uuidsReceiver, uuidsFilter);
            isReceiverRegistered = true;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        if (device != null && serviceUuid != null) {
            connectToService();
        }
        super.onResume();
    }

    /**
     * Handles activity LifeCycle, unregisters UUIDs listener and closes
     * BluetoothSocket
     * */
    @Override
    protected void onPause() {
        disconnect();
        super.onPause();
    }

    /**
     * Sums whole process of disconnecting onPause, on manual disconnect, ...
     * */
    private void disconnect() {
        setLampIsConnected(false);
        handler.postAtFrontOfQueue(new Runnable() {

            @Override
            public void run() {
                try {
                    handler.removeCallbacksAndMessages(null);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                if (isReceiverRegistered) {
                    unregisterReceiver(uuidsReceiver);
                    isReceiverRegistered = false;
                }
                try {
                    if (socket != null)
                        socket.close();
                    socket = null;
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        });
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }

    /**
     * Runs loop of ping request-response system, to keep socket active and
     * informations about device up to date
     * */
    private void runInfoLoop() {
        lamp.getLampInfo();
        handler.postDelayed(new Runnable() {

            @Override
            public void run() {
                runInfoLoop();
            }
        }, 30000);
    }

    /**
     * Helper method, to display current status of connection to device
     * 
     * @param string
     *            message to be displayed
     * */
    protected void setStatus(final int string) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                lamp_connect_disconnect.setText(string);
            }
        });
    }

    /**
     * Helper method, to display lamp informations below status line
     * 
     * @param string
     *            message to be parsed
     * */
    protected void setLampInfo(final String string) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                Matcher m = LAMP_INFO.matcher(string);
                if (m.find()) {
                    lamp_intensity.setText(
                            String.format(getString(R.string.lamp_intensity), lamp_color_intensity.getProgress()));
                    if (!"0".equals(m.group(2)))
                        lamp_humidity.setText(String.format(getString(R.string.lamp_humidity), m.group(2)));
                    if (!"0".equals(m.group(3)))
                        lamp_lightness.setText(String.format(getString(R.string.lamp_lightness), m.group(3)));
                    if (!"0".equals(m.group(3)) && !"0".equals(m.group(1)))
                        lamp_temperature.setText(String.format(getString(R.string.lamp_temperature), m.group(1)));
                }
            }
        });
    }

    /**
     * Binds UI controls to {@link #onClickListener}
     * */
    private void setupUIControls() {
        lamp_backlight_select = (Button) findViewById(R.id.LAMP_BACKLIGHT_CHOOSER);
        colorPicker = (ColorPickerView) findViewById(R.id.LAMP_COLOR_PICKER);
        lamp_turn_off = (Button) findViewById(R.id.LAMP_TURN_OFF);
        lamp_set_with_effect = (Button) findViewById(R.id.LAMP_SET_COLOR_EFFECT_CHOOSER);
        lamp_effects_select = (Button) findViewById(R.id.LAMP_EFFECT_CHOOSER);
        lamp_connect_disconnect = (Button) findViewById(R.id.LAMP_CONNECT_DISCONNECT);
        lamp_select = (Button) findViewById(R.id.LAMP_CHOOSER);
        lamp_pallet = (Button) findViewById(R.id.LAMP_PALLET_CHOOSER);

        lamp_name = (TextView) findViewById(R.id.LAMP_NAME);
        lamp_humidity = (TextView) findViewById(R.id.LAMP_HUMIDITY);
        lamp_lightness = (TextView) findViewById(R.id.LAMP_LIGHTNESS);
        lamp_temperature = (TextView) findViewById(R.id.LAMP_TEMPERATURE);
        lamp_intensity = (TextView) findViewById(R.id.LAMP_INTENSITY);

        lamp_color_intensity = (CustomSeekBar) findViewById(R.id.LAMP_COLOR_INTENSITY);

        for (Button b : getAllButtons()) {
            b.setOnClickListener(onClickListener);
        }

        colorPicker.setOnColorChangedListener(onColorChangedListener);
        colorPicker.setChangeColorContinually(true);
        colorPicker.setColor(Color.WHITE);

        lamp_effects_select.setText(currentEffect.getName());
        lamp_backlight_select.setText(currentBacklightEffect.getName());
        lamp_set_with_effect.setText(currentColorSetMode.getName());

        lamp_color_intensity.setOnSeekBarChangeListener(colorIntensityListener);

        setUIControlsState(false);
    }

    /**
     * Helper method, sums all buttons for UI controls and OnClickListener sets
     * */
    private Button[] getAllButtons() {
        return new Button[] { lamp_connect_disconnect, lamp_effects_select, lamp_backlight_select, lamp_pallet,
                lamp_turn_off, lamp_set_with_effect, lamp_select };
    }

    /**
     * Sets enabled/disabled state to all UI controls, according to connection
     * state
     * */
    private void setUIControlsState(final boolean enabled) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                for (Button b : getAllButtons()) {
                    b.setEnabled(enabled);
                }
                lamp_select.setEnabled(!enabled);
                colorPicker.setEnabled(enabled);
                lamp_color_intensity.setEnabled(enabled);
                if (!enabled) {
                    if (device != null)
                        lamp_name.setText(device.getName());
                    else
                        lamp_name.setText(R.string.lamp_disconnected);
                    lamp_humidity.setText(null);
                    lamp_lightness.setText(null);
                    lamp_temperature.setText(null);
                    lamp_intensity.setText(null);
                }
            }
        });
    }
}