/*
 * Bluegiga’s Bluetooth Smart Android SW for Bluegiga BLE modules
 * Contact: [email protected]
 *
 * This is free software distributed under the terms of the MIT license reproduced below.
 *
 * Copyright (c) 2013, Bluegiga Technologies
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files ("Software")
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
 * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A  PARTICULAR PURPOSE.
 */
package com.siliconlabs.bledemo.activity;

import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;

import androidx.coordinatorlayout.widget.CoordinatorLayout;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.Toolbar;

import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.appindexing.Thing;
import com.google.android.gms.common.api.GoogleApiClient;
import com.siliconlabs.bledemo.R;
import com.siliconlabs.bledemo.adapters.ConnectionsAdapter;
import com.siliconlabs.bledemo.adapters.DebugModeDeviceAdapter;
import com.siliconlabs.bledemo.adapters.DeviceInfoViewHolder;
import com.siliconlabs.bledemo.adapters.LogAdapter;
import com.siliconlabs.bledemo.ble.BlueToothService;
import com.siliconlabs.bledemo.ble.BluetoothDeviceInfo;
import com.siliconlabs.bledemo.ble.Discovery;
import com.siliconlabs.bledemo.ble.ErrorCodes;
import com.siliconlabs.bledemo.ble.TimeoutGattCallback;
import com.siliconlabs.bledemo.bluetoothdatamodel.parsing.Common;
import com.siliconlabs.bledemo.bluetoothdatamodel.parsing.Converters;
import com.siliconlabs.bledemo.bluetoothdatamodel.parsing.Device;
import com.siliconlabs.bledemo.bluetoothdatamodel.parsing.Engine;
import com.siliconlabs.bledemo.dialogs.Dialogs;
import com.siliconlabs.bledemo.dialogs.LeaveBrowserDialog;
import com.siliconlabs.bledemo.fragment.LogFragment;
import com.siliconlabs.bledemo.fragment.SearchFragment;
import com.siliconlabs.bledemo.interfaces.DebugModeCallback;
import com.siliconlabs.bledemo.interfaces.ServicesConnectionsCallback;
import com.siliconlabs.bledemo.log.TimeoutLog;
import com.siliconlabs.bledemo.toolbars.ConnectionsFragment;
import com.siliconlabs.bledemo.toolbars.FilterFragment;
import com.siliconlabs.bledemo.toolbars.LoggerFragment;
import com.siliconlabs.bledemo.toolbars.ToolbarCallback;
import com.siliconlabs.bledemo.utils.Constants;
import com.siliconlabs.bledemo.utils.FilterDeviceParams;
import com.siliconlabs.bledemo.utils.SharedPrefUtils;
import com.siliconlabs.bledemo.utils.ToolbarName;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;

public class BrowserActivity extends BaseActivity implements DebugModeCallback, SwipeRefreshLayout.OnRefreshListener, Discovery.BluetoothDiscoveryHost, Discovery.DeviceContainer, ServicesConnectionsCallback {
    // static vars
    public static final String ABOUT_DIALOG_HTML_ASSET_FILE_PATH = "file:///android_asset/about.html";
    private static final int BlUETOOTH_SETTINGS_REQUEST_CODE = 100;
    // member vars
    private boolean bleIsSupported = true;
    private boolean isBluetoothAdapterEnabled = false;
    private float lastScale = 0.0f;
    // bluetooth service related objects
    private final Discovery discovery = new Discovery(this, this);
    private BlueToothService.Binding bluetoothBinding;
    // used with  swipeRefreshLayout and devicesRecyclerView
    private DebugModeDeviceAdapter devicesAdapter;
    // dialogs
    private Dialog dialogDeviceExtraAdvertisement;
    private Dialog bluetoothEnableDialog;
    private Dialog dialogLicense;
    private SharedPrefUtils sharedPrefUtils;
    private ConnectionsFragment connectionsFragment;
    private FilterFragment filterFragment;
    private ConnectionsAdapter connectionsAdapter;
    private LoggerFragment loggerFragment;
    private boolean scanning = false;
    private boolean allowUpdating = true;
    private volatile boolean running = false;

    private static final int TOOLBAR_OPEN_PERCENTAGE = 95;
    private static final int TOOLBAR_CLOSE_PERCENTACE = 95;

    private String connectToDeviceAddress = "";
    //log
    private Thread logUpdate;

    private final BroadcastReceiver bluetoothAdapterStateChangeListener = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            BluetoothAdapter defaultBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_OFF:
                        finish();
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                    case BluetoothAdapter.STATE_TURNING_ON:
                        isBluetoothAdapterEnabled = false;
                        break;
                    case BluetoothAdapter.STATE_ON:
                        if (defaultBluetoothAdapter != null &&
                                defaultBluetoothAdapter.isEnabled()) {
                            if (!isBluetoothAdapterEnabled) {
                                toast = Toast.makeText(BrowserActivity.this,
                                        R.string.toast_bluetooth_enabled,
                                        Toast.LENGTH_SHORT);
                                toast.show();
                            }

                            updateListWhenAdapterIsReady = false;
                            bluetoothEnableBar.setVisibility(View.GONE);

                            discovery.disconnect();
                            discovery.connect(BrowserActivity.this);
                            startScanning();
                        }
                        isBluetoothAdapterEnabled = true;
                        break;
                }
            }
        }
    };

    @InjectView(R.id.bluetooth_enable)
    RelativeLayout bluetoothEnableBar;
    @InjectView(R.id.bluetooth_enable_msg)
    TextView bluetoothEnableMsg;
    @InjectView(R.id.bluetooth_enable_btn)
    TextView bluetoothEnableBtn;
    @InjectView(R.id.coordinator_layout)
    CoordinatorLayout coordinatorLayout;
    @InjectView(R.id.toolbar)
    Toolbar toolbar;
    @InjectView(R.id.no_devices_found)
    LinearLayout noDevicesFound;
    @InjectView(R.id.looking_for_devices_background)
    LinearLayout lookingForDevicesBackgroundMessage;
    @InjectView(R.id.scanning_gradient_container)
    RelativeLayout scanningGradientContainer;
    @InjectView(R.id.swipe_refresh_container)
    public SwipeRefreshLayout swipeRefreshLayout;
    StringBuilder subtraction = new StringBuilder();
    @InjectView(R.id.connecting_container)
    public RelativeLayout connectingContainer;
    @InjectView(R.id.connecting_anim_gradient_right_container)
    public LinearLayout connectingGradientContainer;
    @InjectView(R.id.connecting_bar_container)
    public RelativeLayout connectingBarContainer;

    @InjectView(R.id.recyclerview_debug_devices)
    public RecyclerView devicesRecyclerView;


    // BluetoothBrowserToolbar view elements
    @InjectView(R.id.linearlayout_log)
    LinearLayout logLL;
    @InjectView(R.id.linearlayout_connections)
    LinearLayout connectionsLL;
    @InjectView(R.id.linearlayout_filter)
    LinearLayout filterLL;
    @InjectView(R.id.frame_layout)
    FrameLayout frameLayout;
    @InjectView(R.id.framelayout_container)
    RelativeLayout frameLayoutContainerRL;
    @InjectView(R.id.imageview_log)
    ImageView logIV;
    @InjectView(R.id.imageview_connections)
    ImageView connectionsIV;
    @InjectView(R.id.imageview_filter)
    ImageView filterIV;
    @InjectView(R.id.textview_log)
    TextView logTV;
    @InjectView(R.id.textview_connections)
    TextView connectionsTV;
    @InjectView(R.id.textview_filter)
    TextView filterTV;
    @InjectView(R.id.bluetooth_browser_background)
    RelativeLayout bluetoothBrowserBackgroundRL;
    @InjectView(R.id.button_scanning)
    Button scanningButton;

    private boolean btToolbarOpened = false;
    private ToolbarName btToolbarOpenedName = null;
    private TextView tv;

    private static final int RESTART_SCAN_TIMEOUT = 1000;
    private Handler handler;

    private final LinkedList<String> errorMessageQueue = new LinkedList<>();

    private Toast toast;

    private final Runnable displayQueuedMessages = new Runnable() {
        @Override
        public void run() {
            handler.removeCallbacks(displayQueuedMessages);

            synchronized (displayQueuedMessages) {
                if (errorMessageQueue.size() > 0 && toast != null && toast.getView() != null && toast.getView().isShown()) {
                    handler.postDelayed(displayQueuedMessages, 1000);
                } else if (errorMessageQueue.size() > 0) {
                    toast = Toast.makeText(BrowserActivity.this, errorMessageQueue.removeFirst(), Toast.LENGTH_LONG);
                    toast.show();
                    handler.postDelayed(displayQueuedMessages, 1000);
                }
            }
        }
    };

    private final Runnable restartScanTimeout = new Runnable() {
        @Override
        public void run() {

            discovery.clearDevicesCache();
            flushContainer();
            sharedPrefUtils.mergeTmpDevicesToFavorites();
            allowUpdating = true;

            if(!scanning) {
                // If wasnt scanning before - start scanning
                startScanning();
            }
        }
    };

    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sharedPrefUtils = new SharedPrefUtils(getApplicationContext());
        // prepare ui
        setContentView(R.layout.activity_browser);
        ButterKnife.inject(this);

        findViewById(R.id.go_back_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });

        handler = new Handler();

        // init bluetoooth discovery engine, matches to accepted bluetooth gatt profiles
        Engine.getInstance().init(this.getApplicationContext());

        // init/config ui
        setSupportActionBar(toolbar);

        // clear logs
        Constants.clearLogs();

        setShowSpinnerDialogVisibility(false);

        initLicenseDialog();
        initExtraAdvertisementDialog();
        initDevicesRecyclerView();
        initSwipeRefreshLayout();

        if (savedInstanceState == null) {
            LogFragment logFragment = new LogFragment();
            FragmentTransaction logtransaction = getSupportFragmentManager().beginTransaction();
            logtransaction.add(R.id.log_body, logFragment);
            logtransaction.commit();
        }
        if (savedInstanceState == null) {
            SearchFragment searchfragment = new SearchFragment();
            FragmentTransaction filtertransaction = getSupportFragmentManager().beginTransaction();
            filtertransaction.add(R.id.filter_body, searchfragment);
            filtertransaction.commit();
        }

        // handle bluetooth adapter on/off state
        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(bluetoothAdapterStateChangeListener, filter);
        bluetoothEnableBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BluetoothAdapter.getDefaultAdapter().enable();
                changeEnableBluetoothAdapterToConnecing();
            }
        });

        // attempt connection for discovery
        discovery.connect(this);
        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();

        startScanning();

        fragmentsInit();
        handleToolbarClickEvents();
        bluetoothBrowserBackgroundRL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (btToolbarOpened) {
                    closeToolbar();
                    btToolbarOpened = !btToolbarOpened;
                }
            }
        });

    }

    private void fragmentsInit() {
        loggerFragment = new LoggerFragment().setCallback(new ToolbarCallback() {
            @Override
            public void close() {
                closeToolbar();
                btToolbarOpened = !btToolbarOpened;
            }

            @Override
            public void submit(FilterDeviceParams filterDeviceParams, boolean close) {

            }
        });
        loggerFragment.setAdapter(new LogAdapter(Constants.LOGS, getApplicationContext()));


        connectionsFragment = new ConnectionsFragment().setCallback(new ToolbarCallback() {
            @Override
            public void close() {
                closeToolbar();
                btToolbarOpened = !btToolbarOpened;
                devicesAdapter.notifyDataSetChanged();
                updateCountOfConnectedDevices();
            }

            @Override
            public void submit(FilterDeviceParams filterDeviceParams, boolean close) {

            }
        });
        connectionsAdapter = new ConnectionsAdapter(getConnectedBluetoothDevices(), getApplicationContext());
        connectionsFragment.setAdapter(connectionsAdapter);
        connectionsFragment.getAdapter().setServicesConnectionsCallback(this);

        filterFragment = new FilterFragment();

    }

    @Override
    public void onDisconnectClicked(final BluetoothDeviceInfo deviceInfo) {
        bluetoothBinding = new BlueToothService.Binding(getApplicationContext()) {
            @Override
            protected void onBound(BlueToothService service) {
                boolean successDisconnected = service.disconnectGatt(deviceInfo.getAddress());
                if (!successDisconnected) {
                    toast = Toast.makeText(getApplicationContext(), R.string.device_not_from_EFR, Toast.LENGTH_LONG);
                    toast.show();
                }
                updateCountOfConnectedDevices();
                devicesAdapter.notifyDataSetChanged();
            }
        };
        BlueToothService.bind(bluetoothBinding);
    }

    @Override
    public void onDeviceClicked(BluetoothDeviceInfo device) {
        connectToDevice(device);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == BlUETOOTH_SETTINGS_REQUEST_CODE) {
            BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            if (!bluetoothAdapter.isEnabled() && bluetoothEnableDialog != null) {
                bluetoothEnableDialog.show();
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        configureFontScale();

        scanningGradientContainer.setVisibility(View.VISIBLE);

        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(bluetoothAdapterStateChangeListener, filter);

        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        if(!scanning){
            setScanningButtonStart();
        }

        if (!bluetoothAdapter.isEnabled()) {
            finish();
        }

        if (bluetoothAdapter != null) {
            isBluetoothAdapterEnabled = bluetoothAdapter.isEnabled();
            if (boundService != null) {
                Log.d("OnResume", "Called");
//                boundService.clearGatt();
                boundService = null;

            }
            if (bluetoothBinding != null) {
                bluetoothBinding.unbind();
            }
        } else {
            isBluetoothAdapterEnabled = false;
        }
        updateCountOfConnectedDevices();
        devicesAdapter.notifyDataSetChanged();
    }

    private void handleToolbarClickEvents() {

        setToolbarItemsNotClicked();

        logLL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (btToolbarOpened && btToolbarOpenedName == ToolbarName.LOGS) {
                    loggerFragment.stopLogUpdater();
                    closeToolbar();
                    btToolbarOpened = !btToolbarOpened;
                    return;
                }
                if (!btToolbarOpened) {
                    bluetoothBrowserBackgroundRL.setBackgroundColor(Color.parseColor("#99000000"));
                    bluetoothBrowserBackgroundRL.setVisibility(View.VISIBLE);
                    ViewCompat.setTranslationZ(bluetoothBrowserBackgroundRL, 4f);
                    animateToolbarOpen(TOOLBAR_OPEN_PERCENTAGE, 300);
                    btToolbarOpened = !btToolbarOpened;
                }
                setToolbarItemsNotClicked();
                setToolbarItemClicked(logIV, logTV);
                btToolbarOpenedName = ToolbarName.LOGS;
                setToolbarFragment(loggerFragment);
                loggerFragment.runLogUpdater();
                loggerFragment.scrollToEnd();
            }
        });

        connectionsLL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (btToolbarOpened && btToolbarOpenedName == ToolbarName.CONNECTIONS) {
                    closeToolbar();
                    btToolbarOpened = !btToolbarOpened;
                    return;
                }
                if (!btToolbarOpened) {
                    bluetoothBrowserBackgroundRL.setBackgroundColor(Color.parseColor("#99000000"));
                    bluetoothBrowserBackgroundRL.setVisibility(View.VISIBLE);
                    ViewCompat.setTranslationZ(bluetoothBrowserBackgroundRL, 4f);
                    animateToolbarOpen(TOOLBAR_OPEN_PERCENTAGE, 300);
                    btToolbarOpened = !btToolbarOpened;
                }
                setToolbarItemsNotClicked();
                setToolbarItemClicked(connectionsIV, connectionsTV);
                btToolbarOpenedName = ToolbarName.CONNECTIONS;
                setToolbarFragment(connectionsFragment);
            }
        });

        filterLL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (btToolbarOpened && btToolbarOpenedName == ToolbarName.FILTER) {
                    closeToolbar();
                    btToolbarOpened = !btToolbarOpened;
                    return;
                }
                if (!btToolbarOpened) {
                    bluetoothBrowserBackgroundRL.setBackgroundColor(Color.parseColor("#99000000"));
                    bluetoothBrowserBackgroundRL.setVisibility(View.VISIBLE);
                    ViewCompat.setTranslationZ(bluetoothBrowserBackgroundRL, 4f);
                    animateToolbarOpen(TOOLBAR_OPEN_PERCENTAGE, 300);
                    btToolbarOpened = !btToolbarOpened;
                }
                setToolbarItemsNotClicked();
                setToolbarItemClicked(filterIV, filterTV);
                btToolbarOpenedName = ToolbarName.FILTER;
                setToolbarFragment(filterFragment.setCallback(new ToolbarCallback() {
                    @Override
                    public void close() {
                        closeToolbar();
                        btToolbarOpened = !btToolbarOpened;
                    }

                    @Override
                    public void submit(FilterDeviceParams filterDeviceParams, boolean close) {
                        if (close) {
                            closeToolbar();
                            btToolbarOpened = !btToolbarOpened;
                        }
                        if(filterDeviceParams.isEmptyFilter()) {
                            filterIV.setImageDrawable(ContextCompat.getDrawable(BrowserActivity.this,R.drawable.ic_icon_filter));
                        } else {
                            filterIV.setImageDrawable(ContextCompat.getDrawable(BrowserActivity.this,R.drawable.ic_icon_filter_active));
                        }


                        filterDevices(filterDeviceParams);
                    }
                }));
            }
        });

    }

    public void setScanningButtonStart() {
        scanningButton.setText(getResources().getString(R.string.Start_Scanning));
        scanningButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(BrowserActivity.this, R.color.silabs_blue)));
    }

    public void setScanningButtonStop() {
        scanningButton.setText(getResources().getString(R.string.Stop_Scanning));
        scanningButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(BrowserActivity.this, R.color.silabs_red)));
    }

    private void hideKeyboard() {
        View view = this.getCurrentFocus();
        if (view != null) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }

    private void closeToolbar() {
        animateToolbarClose(TOOLBAR_CLOSE_PERCENTACE, 300);
        setToolbarItemsNotClicked();
        bluetoothBrowserBackgroundRL.setVisibility(View.GONE);
        hideKeyboard();
    }

    private void setToolbarItemClicked(ImageView imageView, TextView textView) {
        textView.setTextColor(ContextCompat.getColor(this, R.color.silabs_blue));
        DrawableCompat.setTint(imageView.getDrawable(), ContextCompat.getColor(this, R.color.silabs_blue));
    }

    private void setToolbarItemsNotClicked() {
        logTV.setTextColor(ContextCompat.getColor(this, R.color.silabs_primary_text));
        DrawableCompat.setTint(logIV.getDrawable(), ContextCompat.getColor(this, R.color.silabs_primary_text));

        filterTV.setTextColor(ContextCompat.getColor(this, R.color.silabs_primary_text));
        DrawableCompat.setTint(filterIV.getDrawable(), ContextCompat.getColor(this, R.color.silabs_primary_text));

        connectionsTV.setTextColor(ContextCompat.getColor(this, R.color.silabs_primary_text));
        DrawableCompat.setTint(connectionsIV.getDrawable(), ContextCompat.getColor(this, R.color.silabs_primary_text));
    }

    private void animateToolbarOpen(int openPercentHeight, int duration) {
        ValueAnimator animator = ValueAnimator.ofInt(0, percentHeightToPx(openPercentHeight)).setDuration(duration);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                frameLayout.getLayoutParams().height = value.intValue();
                frameLayout.requestLayout();
            }
        });

        frameLayout.setVisibility(View.VISIBLE);
        ViewCompat.setTranslationZ(frameLayoutContainerRL, 5f);
        AnimatorSet set = new AnimatorSet();
        set.play(animator);
        set.setInterpolator(new AccelerateDecelerateInterpolator());
        set.start();
    }

    private void animateToolbarClose(int openPercentHeight, int duration) {
        ValueAnimator animator = ValueAnimator.ofInt(percentHeightToPx(openPercentHeight), 0).setDuration(duration);


        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                frameLayout.getLayoutParams().height = value.intValue();
                frameLayout.requestLayout();
            }
        });

        AnimatorSet set = new AnimatorSet();
        set.play(animator);
        set.setInterpolator(new AccelerateDecelerateInterpolator());
        set.start();
    }

    private int percentHeightToPx(int percent) {
        if (percent < 0 || percent > 100) throw new IllegalArgumentException();
        int height = devicesRecyclerView.getHeight() + scanningGradientContainer.getHeight();
        return (int) (((float) percent / 100.0) * height);
    }

    private void setToolbarFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.frame_layout, fragment);
        fragmentTransaction.commit();
    }

    @Override
    protected void onPause() {
        super.onPause();

        Log.d("onPause", "Called");
        onScanningStopped();

        unregisterReceiver(bluetoothAdapterStateChangeListener);

    }

    @Override
    public void onBackPressed() {
        if (connectingContainer.getVisibility() == View.VISIBLE) {
            if (boundService != null) {
                Log.d("onBackPressed", "Called");
                boundService.clearGatt();
            }

            hideConnectingAnimation();
        } else {
            if(sharedPrefUtils.shouldDisplayLeaveBrowserDialog() && getConnectedBluetoothDevices().size() > 0) {
                LeaveBrowserDialog dialog = new LeaveBrowserDialog(new LeaveBrowserDialog.LeaveBrowserCallback() {
                    @Override
                    public void onOkClicked() {
                        BrowserActivity.super.onBackPressed();
                    }
                },BrowserActivity.this);
                dialog.show(getSupportFragmentManager(),"leave_browser_dialog");
            } else {
                super.onBackPressed();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        bluetoothBinding = new BlueToothService.Binding(getApplicationContext()) {
            @Override
            protected void onBound(BlueToothService service) {
                for (BluetoothDevice bd : getConnectedBluetoothDevices()) {
                    service.disconnectGatt(bd.getAddress());
                }
                service.clearGatt();
                bluetoothBinding.unbind();
            }
        };
        BlueToothService.bind(bluetoothBinding);

//        Log.d("onDestroy", "Called");
//        if (bluetoothBinding != null) {
//            bluetoothBinding.unbind();
//        }
        discovery.disconnect();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main_debug_mode, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_license:
                showAbout();
                break;

            case R.id.menu_log:
                adjustLayout();
                break;

            case android.R.id.home:
                finish();
                return true;

            case R.id.menu_mappings:
                Intent intent = new Intent(BrowserActivity.this, MappingDictionaryActivity.class);
                startActivity(intent);

            default:
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    void filterDevices(FilterDeviceParams filterDeviceParams) {
        devicesAdapter.filterDevices(filterDeviceParams, true);
        devicesAdapter.setDebugMode();
        devicesRecyclerView.setAdapter(devicesAdapter);
        devicesRecyclerView.setHasFixedSize(true);
    }

    @OnClick(R.id.button_scanning)
    public void onScanningButtonClicked() {

        if (scanning || scanningButton.getText().equals(getString(R.string.Stop_Scanning))) {
            handler.removeCallbacks(restartScanTimeout);
            devicesRecyclerView.setVisibility(View.VISIBLE);
            onScanningStopped();
        } else {
            startScanning();
        }


    }

    private void adjustLayout() { //TODO Fix animation (Almost ok)

        RelativeLayout logbody = findViewById(R.id.log_body);

        if (logbody.getVisibility() == View.GONE) {
            logbody.setVisibility(View.VISIBLE);
            Log.i("adjustLayout", "Creating View");
            running = true;
            startlog();
        } else {
            Log.i("adjustLayout", "Hiding View");

            running = false;
            logbody.setVisibility(View.GONE);
        }
    }

    private void startlog() {
        if (logUpdate == null) {
            logUpdate = new Thread(new Runnable() {
                public void run() {
                    synchronized (this) {
                        try {
                            while (running) {
                                log();
                                Thread.sleep(250);
                            }
                        } catch (Exception e) {
                            Log.e("logUpdate()", "error: " + e);
                        }
                    }
                }
            });
            logUpdate.start();
        }
    }

    public Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //Log.i("handler","receiving message");
            String sentString = msg.getData().getString("what");
            tv = findViewById(R.id.log_view);
            tv.setMovementMethod(new ScrollingMovementMethod());
            tv.append(sentString);
            tv.scrollTo(0, tv.getScrollY());
        }
    };

    private void log() {
        StringBuilder stringBuilder = new StringBuilder();

        try {
            Process process = Runtime.getRuntime().exec("logcat -d");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            //Log.i("log()","creating log");
            while ((line = bufferedReader.readLine()) != null) {
                if (line.contains(" I ") || line.contains(" E ") || line.contains(" D ")) { //TODO Log filter options
                    if (!line.contains("ViewRoot")) {
                        stringBuilder.append(line);
                        stringBuilder.append("\n");
                    }
                }
            }

            if (subtraction.toString().length() != stringBuilder.toString().length()) {
                //Log.i("log()","sending to handler");
                String result;
                if (subtraction.toString().length() > stringBuilder.toString().length()) {
                    result = stringBuilder.toString();
                } else {
                    result = stringBuilder.substring(subtraction.length(), stringBuilder.length());
                }
                subtraction = stringBuilder;
                Message m = new Message();
                Bundle b = new Bundle();
                b.putString("what", result);
                m.setData(b);
                mHandler.sendMessage(m);
            }
        } catch (IOException e) {
            Log.e("log()", "couldn't create log: " + e);
        }
    }

    public void sortlist(int comparator) {
        //Log.i("MADM","sortlist");
        devicesAdapter.sort(comparator, true);
        devicesAdapter.setDebugMode();
        devicesRecyclerView.setAdapter(devicesAdapter);
        devicesRecyclerView.setHasFixedSize(true);
    }

    public void filterbond() {
        devicesAdapter.updateWithBonded(devicesAdapter.getDevicesInfo());
        devicesRecyclerView.setAdapter(devicesAdapter);
        devicesRecyclerView.setHasFixedSize(true);
    }

    public String getdate(String string) {
        String currentDateTimeString = null;
        switch (string) {
            case "normal":
                currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
                break;
            case "compact":
                SimpleDateFormat LOG_FILE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ssZ");
                currentDateTimeString = LOG_FILE_FORMAT.format(new Date());
                break;
        }
        return currentDateTimeString;
    }

    public File save_log() {
        final File path = new File(Environment.getExternalStorageDirectory(), "SiliconLabs_EFRConnect");
        final File file = new File(path + File.separator + "SiliconLabs." + getdate("compact") + ".txt");
        Thread save_log = new Thread(new Runnable() {
            public void run() {
                String savedata = tv.getText().toString();
                if (!path.exists()) {
                    path.mkdir();
                }
                BufferedWriter bw = null;
                try {
                    file.createNewFile();
                    bw = new BufferedWriter(new FileWriter(file), 1024);
                    bw.write(savedata);
                } catch (IOException e) {
                    Log.e("save_log()", "error saving log: " + e);
                } finally {
                    if (bw != null) {
                        try {
                            bw.close();
                        } catch (IOException e) {
                            Log.e("save_log()", "error closing save_log(): " + e);
                        }
                    }
                }
            }
        });
        save_log.start();
        return file;
    }

    public void share_log() {
        Thread share_log = new Thread(new Runnable() {
            public void run() {
                String logdata = tv.getText().toString();
                Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
                shareIntent.setType("text/plain");
                shareIntent.putExtra(Intent.EXTRA_SUBJECT, "SiliconLabs BGApp Log: " + getdate("normal"));
                shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, logdata);
                startActivity(Intent.createChooser(shareIntent, "Share SiliconLabs BGApp Log ..."));
            }
        });
        share_log.start();
    }

    public void clear_log() {
        tv = findViewById(R.id.log_view);
        tv.setText("");
        try {
            Runtime.getRuntime().exec(new String[]{"logcat", "-c"});
            Log.i("clear_log()", "log cleaned");
            //restartlog();
        } catch (IOException e) {
            Log.e("clear_log()", "error clearing log: " + e);
        }
    }

    public void performSearch(String string) {
        toast = Toast.makeText(getBaseContext(), getResources().getString(R.string.Search_for_s, string), Toast.LENGTH_SHORT);
        toast.show();
        if (string.equals("")) {
            updateWithDevices(devicesAdapter.getDevicesInfo());
        } else {
            updateWithDevices(devicesAdapter.getDevicesInfo(), string);
        }
        devicesRecyclerView.setAdapter(devicesAdapter);
        devicesRecyclerView.setHasFixedSize(true);
    }

    @Override
    public void onRefresh() {
        // callback for swiperefreshlayout
        handler.removeCallbacks(restartScanTimeout);

        allowUpdating = false;
        devicesRecyclerView.setVisibility(View.GONE);
        noDevicesFound.setVisibility(View.GONE);
        lookingForDevicesBackgroundMessage.setVisibility(View.VISIBLE);
        setScanningButtonStop();

        handler.postDelayed(restartScanTimeout,RESTART_SCAN_TIMEOUT);

        swipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(false);
            }
        });
    }


    private void initDevicesRecyclerView() {
        sharedPrefUtils.mergeTmpDevicesToFavorites();
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        devicesRecyclerView.setLayoutManager(layoutManager);
        devicesAdapter = new DebugModeDeviceAdapter(this,
                this,
                new DeviceInfoViewHolder.Generator(R.layout.bluetooth_browser_device_item) {
                    @Override
                    public DeviceInfoViewHolder generate(View itemView) {
                        return new DebugModeDeviceAdapter.ViewHolder(
                                BrowserActivity.this,
                                itemView,
                                BrowserActivity.this);
                    }
                });
        devicesAdapter.setDebugMode();
        devicesRecyclerView.setAdapter(devicesAdapter);
        devicesRecyclerView.setHasFixedSize(true);
    }

    private void initSwipeRefreshLayout() {
        swipeRefreshLayout.setOnRefreshListener(this);
        swipeRefreshLayout.setColorSchemeColors(ContextCompat.getColor(BrowserActivity.this, android.R.color.holo_red_dark),
                ContextCompat.getColor(BrowserActivity.this, android.R.color.holo_orange_dark),
                ContextCompat.getColor(BrowserActivity.this, android.R.color.holo_orange_light),
                ContextCompat.getColor(BrowserActivity.this, android.R.color.holo_red_light));
    }

    private void initExtraAdvertisementDialog() {
        dialogDeviceExtraAdvertisement = new Dialog(this);
        dialogDeviceExtraAdvertisement.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialogDeviceExtraAdvertisement.setContentView(R.layout.dialog_advertisement_details);
        dialogDeviceExtraAdvertisement.findViewById(R.id.close_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialogDeviceExtraAdvertisement.dismiss();
            }
        });
    }

    //Webview -------------------------
    private void initLicenseDialog() {
        dialogLicense = new Dialog(this);
        dialogLicense.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialogLicense.setContentView(R.layout.dialog_about_silicon_labs_blue_gecko);
        WebView view = dialogLicense.findViewById(R.id.menu_item_license);
        Button closeButton = dialogLicense.findViewById(R.id.close_about_btn);

        closeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialogLicense.dismiss();
            }
        });

        view.loadUrl(ABOUT_DIALOG_HTML_ASSET_FILE_PATH);
    }

    // Displays dialog and request user to enable Bluetooth
    private void bluetoothEnable() {
        if (bluetoothEnableDialog == null || !bluetoothEnableDialog.isShowing()) {
            bluetoothEnableDialog = Dialogs.showAlert(this.getText(R.string.no_bluetooth_dialog_title_text), this
                            .getText(R.string.no_bluetooth_dialog_text), this, getText(android.R.string.ok),
                    getText(android.R.string.cancel), new OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                            Intent intentBluetooth = new Intent();
                            intentBluetooth.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
                            BrowserActivity.this.startActivityForResult(intentBluetooth,
                                    BlUETOOTH_SETTINGS_REQUEST_CODE);
                        }
                    }, new OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                            BrowserActivity.this.finish();
                        }
                    });
        }
    }

    // Displays about dialog
    private void showAbout() {
        dialogLicense.show();
    }

    // Configures number of shown advertisement types
    private void configureFontScale() {
        float scale = getResources().getConfiguration().fontScale;
        if (lastScale != scale) {
            lastScale = scale;
            if (lastScale == Common.FONT_SCALE_LARGE) {
                Device.MAX_EXTRA_DATA = 2;
            } else if (lastScale == Common.FONT_SCALE_XLARGE) {
                Device.MAX_EXTRA_DATA = 1;
            } else {
                Device.MAX_EXTRA_DATA = 3;
            }
        }
    }

    private String advertData(BluetoothDeviceInfo device, String advertisementData) {
        if (device.getName() != null && !device.getName().equals("null")) {
            advertisementData += (device.getName());
            advertisementData += ("<br><small>LOCAL NAME</small><br><br>");
        }
        advertisementData += (device.getAddress());
        advertisementData += ("<br><small>MAC ADDRESS</small><br><br>");
        if (device.getBleFormat() != null && !device.getBleFormat().toString().equals("UNSPECIFIED")) {
            advertisementData += (device.getBleFormat());
            advertisementData += ("<br><small>BEACON FORMAT</small><br><br>");
        }
        advertisementData += Integer.toHexString(device.scanInfo.getScanRecord().getAdvertiseFlags());//todo
        advertisementData += ("<br><small>FLAGS</small><br><br>");
        advertisementData += Integer.toString(device.scanInfo.getRssi());
        advertisementData += ("<br><small>RSSI</small><br><br>");
        if (device.scanInfo.getScanRecord().getTxPowerLevel() > (-100)) {
            advertisementData += Integer.toString(device.scanInfo.getScanRecord().getTxPowerLevel());
            advertisementData += ("<br><small>TX POWER</small><br><br>");
        }
        if (device.scanInfo.getAdvertData() != null && !device.scanInfo.getAdvertData().toString().equals("{}")) {
            String advertdata = device.scanInfo.getAdvertData().toString();
            advertdata = advertdata.substring(1, advertdata.length() - 1);
            advertdata = advertdata.replaceAll(",", "\n");
            advertisementData += (advertdata);
            advertisementData += ("<br><small>ADVERT DATA</small><br><br>");
        }
        if (device.scanInfo.getScanRecord().getManufacturerSpecificData() != null &&
                !device.scanInfo.getScanRecord().getManufacturerSpecificData().toString().equals("{}")) {
            SparseArray<byte[]> manData = device.scanInfo.getScanRecord().getManufacturerSpecificData();
            String advertdata;
            StringBuilder advertisementDataBuilder = new StringBuilder(advertisementData);
            for (int i = 0; i < manData.size(); ++i) {
                int key = manData.keyAt(i);
                byte[] value = manData.get(key);
                advertdata = ("<br><small>Key: </small>" + Converters.getHexValue((byte) key) +
                        " <br><small>Value: </small>" + Converters.getHexValue(value));
                advertisementDataBuilder.append(advertdata);
            }
            advertisementData = advertisementDataBuilder.toString();
            advertisementData += ("<br><small>MANUFACTURER SPECIFIC DATA</small><br><br>");
        }
        if (device.scanInfo.getScanRecord().getServiceData() != null &&
                !device.scanInfo.getScanRecord().getServiceData().toString().equals("{}")) {
            String advertdata = device.scanInfo.getScanRecord().getServiceData().toString();
            advertdata = advertdata.substring(1, advertdata.length() - 1);
            advertdata = advertdata.replaceAll(",", "\n");
            advertisementData += (advertdata);
            advertisementData += ("<br><small>SERVICE DATA</small><br><br>");
        }
        if (device.scanInfo.getScanRecord().getServiceUuids() != null) {
            String advertdata = device.scanInfo.getScanRecord().getServiceUuids().toString();
            advertdata = advertdata.substring(1, advertdata.length() - 1);
            advertdata = advertdata.replaceAll(",", "\n");
            advertisementData += (advertdata);
            advertisementData += ("<br><small>SERVICES UUIDs</small><br><br>");
        }
        return advertisementData;
    }

    private String prepareAdvertisementTextWithBoldedPropertyNames(BluetoothDeviceInfo device) {
        StringBuilder advertisementData = new StringBuilder();

        for (int i = 0; i < device.getAdvertData().size(); i++) {
            String data = device.getAdvertData().get(i);
            String[] advertiseData = data.split(":");
            StringBuilder deviceProperty = new StringBuilder("<b>" + advertiseData[1] + "</b><br><small>" + advertiseData[0] + "</small>");
            deviceProperty.append("<br>");

            if (advertiseData.length > 2) {
                deviceProperty.append(" <small>");

                for (int j = 2; j < advertiseData.length; j++) {
                    deviceProperty.append(advertiseData[j]);
                    if (j != advertiseData.length - 1) {
                        advertisementData.append(":");
                    }
                }

                deviceProperty.append("</small>");
                if (i != device.getAdvertData().size() - 1) {
                    deviceProperty.append("<br>");
                }
            }

            advertisementData.append(deviceProperty).append("<br>");

        }
        // Info in advertisement data ----------------------------------------------
        advertisementData = new StringBuilder(advertData(device, advertisementData.toString()));
        //-------------------------------------------------------------------------

        advertisementData.append("<br>Scan Record:<br>");
        advertisementData.append("");

        byte[] rawBytes = device.scanInfo.getScanRecord().getBytes();
        for (int i = 0; i < rawBytes.length; i++) {
            advertisementData.append(Converters.getHexValue(rawBytes[i]));

            if (i != rawBytes.length - 1) {
                advertisementData.append("&nbsp;");
            }

            if ((i + 1) % 8 == 0) {
                advertisementData.append("<br>&nbsp;");
            }
        }
        advertisementData.append("");

        return advertisementData.toString();
    }

    private String prepareAdvertisementRawData(BluetoothDeviceInfo device) {
        StringBuilder advertisementData = new StringBuilder();
        // Info in advertisement data ----------------------------------------------
        advertisementData = new StringBuilder(advertData(device, advertisementData.toString()));
        // -------------------------------------------------------------------------
        advertisementData.append("Scan Record:<br>");
        advertisementData.append("[");

        byte[] rawBytes = device.scanInfo.getScanRecord().getBytes();
        for (int i = 0; i < rawBytes.length; i++) {
            if (rawBytes[i] == 0x00 && rawBytes[i + 1] == 0x00) {
                break;
            }
            advertisementData.append(Converters.getHexValue(rawBytes[i]));

            if (i != rawBytes.length - 1) {
                advertisementData.append("&nbsp;");
            }

            if ((i + 1) % 8 == 0) {
                advertisementData.append("<br>&nbsp;");
            }
        }
        advertisementData.append("]");

        return advertisementData.toString();
    }

    // Displays scanning status in UI and starts scanning for new BLE devices
    private void startScanning() {
        setScanningButtonStop();
        scanning = true;
        setScanningProgress(true);
        setScanningStatus(true);
        devicesAdapter.setRunUpdater(true);
        // Connected devices are not deleted from list
        reDiscover(false);
    }

    private void onScanningStopped() {
        setScanningButtonStart();
        scanning = false;
        discovery.stopDiscovery(false);
        setScanningStatus(devicesAdapter.getItemCount() > 0);
        setScanningProgress(false);
        devicesAdapter.setRunUpdater(false);
        int numbDevicesCurrentlyDisplaying = devicesAdapter.getItemCount();

        if (numbDevicesCurrentlyDisplaying > 0) {
            noDevicesFound.setVisibility(View.GONE);
            lookingForDevicesBackgroundMessage.setVisibility(View.GONE);
        }
    }

    private void setScanningStatus(boolean foundDevices) {
        if (!foundDevices) {
            noDevicesFound.setVisibility(View.VISIBLE);
            lookingForDevicesBackgroundMessage.setVisibility(View.GONE);
        }
    }

    private void setScanningProgress(boolean isScanning) {
        if (devicesAdapter.getItemCount() == 0) {
            if (isScanning) {
                lookingForDevicesBackgroundMessage.setVisibility(View.VISIBLE);
                noDevicesFound.setVisibility(View.GONE);
            } else {
                lookingForDevicesBackgroundMessage.setVisibility(View.GONE);
                noDevicesFound.setVisibility(View.VISIBLE);
            }
        }
    }

    private boolean refreshDeviceCache(BluetoothGatt gatt) {
        try {
            Log.d("refreshDevice", "Called");
            BluetoothGatt localBluetoothGatt = gatt;
            Method localMethod = localBluetoothGatt.getClass().getMethod("refresh");
            if (localMethod != null) {
                boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue();
                Log.d("refreshDevice", "bool: " + bool);
                return bool;
            }
        } catch (Exception localException) {
            Log.e("refreshDevice", "An exception occured while refreshing device");
        }
        return false;
    }

    public void showConnectingAnimation() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                scanningGradientContainer.setVisibility(View.GONE);

                Animation connectingGradientAnimation = AnimationUtils.loadAnimation(BrowserActivity.this,
                        R.anim.connection_translate_right);
                connectingContainer.setVisibility(View.VISIBLE);
                connectingGradientContainer.startAnimation(connectingGradientAnimation);
                Animation connectingBarFlyIn = AnimationUtils.loadAnimation(BrowserActivity.this,
                        R.anim.scanning_bar_fly_in);
                connectingBarContainer.startAnimation(connectingBarFlyIn);
            }
        });
    }

    public void hideConnectingAnimation() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                devicesAdapter.debugModeConnectingDevice = null;
                devicesAdapter.notifyDataSetChanged();

                connectingContainer.setVisibility(View.GONE);
                connectingGradientContainer.clearAnimation();

                scanningGradientContainer.setVisibility(View.VISIBLE);
            }
        });
    }

    private BlueToothService boundService;

    @Override
    public void connectToDevice(final BluetoothDeviceInfo deviceInfo) {
        connectToDeviceAddress = deviceInfo.getAddress();

        if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
            return;
        }

        if (scanning) {
            onScanningStopped();
        }

        if (deviceInfo == null) {
            Log.e("deviceInfo", "null");
            return;
        }

        if (bluetoothBinding != null) {
            bluetoothBinding.unbind();
        }

        final BluetoothDeviceInfo bluetoothDeviceInfo = deviceInfo;
//        runOnUiThread(new Runnable() {
//            @Override
//            public void run() {
////                devicesAdapter.debugModeConnectingDevice = bluetoothDeviceInfo;
////                devicesAdapter.notifyDataSetChanged();
//            }
//        });

        showConnectingAnimation();

        bluetoothBinding = new BlueToothService.Binding(this) {
            @Override
            protected void onBound(final BlueToothService service) {
                boundService = service;

                //fixme

                if (service.isGattConnected(deviceInfo.getAddress())) {
                    connectToDeviceAddress = "";
                    hideConnectingAnimation();
                    if (btToolbarOpened) {
                        closeToolbar();
                        btToolbarOpened = !btToolbarOpened;
                    }
                    Intent intent = new Intent(BrowserActivity.this, DeviceServicesActivity.class);
                    intent.putExtra("DEVICE_SELECTED_ADDRESS", deviceInfo.getAddress());
                    startActivity(intent);
                    return;
                }

                service.connectGatt(bluetoothDeviceInfo.device, false, new TimeoutGattCallback() {
                    @Override
                    public void onTimeout() {
                        Constants.LOGS.add(new TimeoutLog(bluetoothDeviceInfo.device));
                        toast = Toast.makeText(BrowserActivity.this,
                                R.string.toast_connection_timed_out,
                                Toast.LENGTH_SHORT);
                        toast.show();
                        hideConnectingAnimation();
                        connectToDeviceAddress = "";
                    }

                    @Override
                    public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
                        super.onConnectionStateChange(gatt, status, newState);
                        updateCountOfConnectedDevices();
                        service.gattMap.put(deviceInfo.getAddress(), gatt);
                        hideConnectingAnimation();

                        if (newState == BluetoothGatt.STATE_DISCONNECTED && status != BluetoothGatt.GATT_SUCCESS) {
                            final String deviceName = TextUtils.isEmpty(bluetoothDeviceInfo.getName()) ? getString(R.string.not_advertising_shortcut) : bluetoothDeviceInfo.getName();
                            if (gatt.getDevice().getAddress().equals(connectToDeviceAddress)) {
                                connectToDeviceAddress = "";
                                synchronized (errorMessageQueue) {
                                    errorMessageQueue.add(ErrorCodes.getFailedConnectingToDeviceMessage(deviceName, status));
                                }
                            } else {
                                synchronized (errorMessageQueue) {
                                    errorMessageQueue.add(ErrorCodes.getDeviceDisconnectedMessage(deviceName, status));
                                }
                            }

                            handler.removeCallbacks(displayQueuedMessages);
                            handler.postDelayed(displayQueuedMessages, 500);

                        } else if (newState == BluetoothGatt.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
                            //refreshDeviceCache(gatt);
                            if (service.isGattConnected()) {
                                connectToDeviceAddress = "";
                                if (btToolbarOpened) {
                                    closeToolbar();
                                    btToolbarOpened = !btToolbarOpened;
                                }

                                Intent intent = new Intent(BrowserActivity.this, DeviceServicesActivity.class);
                                intent.putExtra("DEVICE_SELECTED_ADDRESS", deviceInfo.getAddress());
                                startActivity(intent);
                            }
                        } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                            Log.d("STATE_DISCONNECTED", "Called");
                            gatt.close();
                            service.clearGatt();
                            bluetoothBinding.unbind();
                        }
                    }
                });
            }
        };
        BlueToothService.bind(bluetoothBinding);
    }

    @Override
    public void addToFavorite(String deviceAddress) {
        sharedPrefUtils.addDeviceToFavorites(deviceAddress);
    }

    @Override
    public void removeFromFavorite(String deviceAddress) {
        sharedPrefUtils.removeDeviceFromFavorites(deviceAddress);
        sharedPrefUtils.removeDeviceFromTemporaryFavorites(deviceAddress);
    }

    @Override
    public void addToTemporaryFavorites(String deviceAddress) {
        sharedPrefUtils.addDeviceToTemporaryFavorites(deviceAddress);
    }

    @Override
    public void updateCountOfConnectedDevices() {
        final List<BluetoothDevice> connectedBluetoothDevices = getConnectedBluetoothDevices();
        final int size = connectedBluetoothDevices.size();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                connectionsTV.setText(size + " Connections");
                connectionsFragment.getAdapter().setConnectionsList(connectedBluetoothDevices);
                connectionsFragment.getAdapter().notifyDataSetChanged();
            }
        });
    }

    private List<BluetoothDevice> getConnectedBluetoothDevices() {
        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
    }

    @Override
    public boolean isReady() {
        return !isFinishing();
    }

    @Override
    public void reDiscover() {
        reDiscover(false);
    }

    private boolean updateListWhenAdapterIsReady = false;

    @Override
    public void onAdapterDisabled() {

    }

    @Override
    public void onAdapterEnabled() {

    }

    private void showEnableBluetoothAdapterBar() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                bluetoothEnableMsg.setText(R.string.bluetooth_adapter_bar_disabled);
                bluetoothEnableBar.setBackgroundColor(ContextCompat.getColor(BrowserActivity.this, R.color.silabs_red_dark));
                bluetoothEnableBtn.setVisibility(View.VISIBLE);
                bluetoothEnableBar.setVisibility(View.VISIBLE);
                bluetoothEnableBar.postInvalidate();
            }
        });
    }

    private void changeEnableBluetoothAdapterToConnecing() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                BluetoothAdapter.getDefaultAdapter().enable();
                updateListWhenAdapterIsReady = true;
                bluetoothEnableBtn.setVisibility(View.GONE);
                bluetoothEnableMsg.setText(R.string.bluetooth_adapter_bar_turning_on);
                bluetoothEnableBar.setBackgroundColor(ContextCompat.getColor(BrowserActivity.this, R.color.silabs_blue)); //TODO bluetooth bar color
            }
        });
    }

    @Override
    public void flushContainer() {
        devicesAdapter.flushContainer();
    }

    @Override
    public void updateWithDevices(List devices) {
        if(allowUpdating) devicesAdapter.updateWith(devices);
        else return;

        if (devicesAdapter.getItemCount() > 0) {
            lookingForDevicesBackgroundMessage.setVisibility(View.GONE);
            noDevicesFound.setVisibility(View.GONE);
            devicesRecyclerView.setVisibility(View.VISIBLE);
        } else {
            lookingForDevicesBackgroundMessage.setVisibility(View.VISIBLE);
        }
    }

    public void updateWithDevices(List devices, String string) {
        if(allowUpdating) devicesAdapter.updateWith(devices, string);
        else return;

        if (devicesAdapter.getItemCount() > 0) {
            lookingForDevicesBackgroundMessage.setVisibility(View.GONE);
            noDevicesFound.setVisibility(View.GONE);
            devicesRecyclerView.setVisibility(View.VISIBLE);
        } else {
            lookingForDevicesBackgroundMessage.setVisibility(View.VISIBLE);
        }
    }
    //}

    public void reDiscover(boolean clearCachedDiscoveries) {
        discovery.startDiscovery(clearCachedDiscoveries);
    }

    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    public Action getIndexApiAction() {
        Thing object = new Thing.Builder()
                .setName("BrowserActivity Page") // TODO: Define a title for the content shown.
                // TODO: Make sure this auto-generated URL is correct.
                .setUrl(Uri.parse("http://[ENTER-YOUR-URL-HERE]"))
                .build();
        return new Action.Builder(Action.TYPE_VIEW)
                .setObject(object)
                .setActionStatus(Action.STATUS_TYPE_COMPLETED)
                .build();
    }

    @Override
    public void onStart() {
        super.onStart();
        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        client.connect();
        AppIndex.AppIndexApi.start(client, getIndexApiAction());
    }

    @Override
    public void onStop() {
        super.onStop();

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        //Log.d("onStop","Called");
        AppIndex.AppIndexApi.end(client, getIndexApiAction());
        client.disconnect();
    }

}