// Path: bletia-core/src/main/java/info/izumin/android/bletia/core/BleState.java
// public enum BleState {
//     DISCONNECTED,
//     CONNECTING,
//     CONNECTED,
//     SERVICE_DISCOVERING,
//     SERVICE_DISCOVERED,
//     DISCONNECTING
// }
// 
// Path: rxbletia/src/main/java/info/izumin/android/bletia/rx/RxBletia.java
// public class RxBletia extends AbstractBletia {
//     public static final String TAG = RxBletia.class.getSimpleName();
// 
//     private final Map<UUID, RxEnableNotificationAction> mEnabledNotificationList;
//     private final PublishSubject<BletiaException> mExceptionSubject;
// 
//     public RxBletia(Context context) {
//         super(context);
//         setSubListener(mListener);
//         mEnabledNotificationList = new HashMap<>();
//         mExceptionSubject = PublishSubject.create();
//     }
// 
//     public Observable<Void> connect(BluetoothDevice device) {
//         return execute(new RxConnectAction(device, getStateContainer()));
//     }
// 
//     public Observable<Void> disconnect() {
//         return execute(new RxDisconnectAction(getStateContainer()));
//     }
// 
//     public Observable<Void> discoverServices() {
//         return execute(new RxDiscoverServicesAction(getStateContainer()));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> writeCharacteristic(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxWriteCharacteristicAction(characteristic));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> readCharacteristic(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxReadCharacteristicAction(characteristic));
//     }
// 
//     public Observable<BluetoothGattDescriptor> writeDescriptor(BluetoothGattDescriptor descriptor) {
//         return execute(new RxWriteDescriptorAction(descriptor));
//     }
// 
//     public Observable<BluetoothGattDescriptor> readDescriptor(BluetoothGattDescriptor descriptor) {
//         return execute(new RxReadDescriptorAction(descriptor));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> enableNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
//         return enabled ? enableNotification(characteristic) : disableNotification(characteristic);
//     }
// 
//     public Observable<BluetoothGattCharacteristic> enableNotification(final BluetoothGattCharacteristic characteristic) {
//         final UUID uuid = characteristic.getUuid();
//         if (mEnabledNotificationList.containsKey(uuid)) {
//             return mEnabledNotificationList.get(uuid).getResolver();
//         }
//         final RxEnableNotificationAction action = new RxEnableNotificationAction(characteristic);
//         mEnabledNotificationList.put(characteristic.getUuid(), action);
//         return execute(action).doOnUnsubscribe(new Action0() {
//             @Override
//             public void call() {
//                 mEnabledNotificationList.remove(uuid);
//             }
//         });
//     }
// 
//     public Observable<BluetoothGattCharacteristic> disableNotification(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxDisableNotificationAction(characteristic))
//                 .doOnNext(new Action1<BluetoothGattCharacteristic>() {
//                     @Override
//                     public void call(BluetoothGattCharacteristic characteristic) {
//                         mEnabledNotificationList.remove(characteristic.getUuid());
//                     }
//                 });
//     }
// 
//     public Observable<Integer> readRemoteRssi() {
//         return execute(new RxReadRemoteRssiAction());
//     }
// 
//     public Observable<BletiaException> observeError() {
//         return mExceptionSubject;
//     }
// 
//     private final BleEventListener mListener = new BleEventListener() {
//         @Override
//         public void onCharacteristicChanged(final BluetoothGattCharacteristic characteristic) {
//             final UUID uuid = characteristic.getUuid();
//             if (mEnabledNotificationList.containsKey(uuid)) {
//                 mEnabledNotificationList.get(uuid).resolve(characteristic);
//             }
//         }
// 
//         @Override
//         public void onError(BletiaException exception) {
//             mExceptionSubject.onNext(exception);
//         }
//     };
// }


import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.UUID;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnItemClick;
import info.izumin.android.bletia.core.BleState;
import info.izumin.android.bletia.rx.RxBletia;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subscriptions.CompositeSubscription;
                .flatMap(_void -> mBletia.discoverServices())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(_void -> refresh(true))
                .flatMap(_void -> mBletia.enableNotification(getBatteryStateCharacteristic()))
                .filter(characteristic -> characteristic.getValue() != null)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        characteristic -> mTextBatteryState.setText(String.valueOf(characteristic.getValue()[0])),
                        throwable -> Log.d(TAG, throwable.getMessage())
                );
        mSubscriptions.add(subscription);
    }

    @OnItemClick(R.id.list_devices)
    void onDeviceSelected(int position) {
        BluetoothDevice device = mDeviceAdapter.getItem(position);
        Log.d(TAG, "Connected to " + device.getName() + " !");
        mScanner.stopScan(mScanCallback);
        connect(device);
    }

    @OnClick(R.id.btn_alert_off)
    void onClickBtnAlertOff() {
        mBletia.writeCharacteristic(getAlertLevelCharacteristic(0))
                .subscribe(
                        characteristic -> {},
                        throwable -> Log.d(TAG, throwable.getMessage())
                );
    }

// Path: bletia-core/src/main/java/info/izumin/android/bletia/core/BleState.java
// public enum BleState {
//     DISCONNECTED,
//     CONNECTING,
//     CONNECTED,
//     SERVICE_DISCOVERING,
//     SERVICE_DISCOVERED,
//     DISCONNECTING
// }
// 
// Path: rxbletia/src/main/java/info/izumin/android/bletia/rx/RxBletia.java
// public class RxBletia extends AbstractBletia {
//     public static final String TAG = RxBletia.class.getSimpleName();
// 
//     private final Map<UUID, RxEnableNotificationAction> mEnabledNotificationList;
//     private final PublishSubject<BletiaException> mExceptionSubject;
// 
//     public RxBletia(Context context) {
//         super(context);
//         setSubListener(mListener);
//         mEnabledNotificationList = new HashMap<>();
//         mExceptionSubject = PublishSubject.create();
//     }
// 
//     public Observable<Void> connect(BluetoothDevice device) {
//         return execute(new RxConnectAction(device, getStateContainer()));
//     }
// 
//     public Observable<Void> disconnect() {
//         return execute(new RxDisconnectAction(getStateContainer()));
//     }
// 
//     public Observable<Void> discoverServices() {
//         return execute(new RxDiscoverServicesAction(getStateContainer()));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> writeCharacteristic(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxWriteCharacteristicAction(characteristic));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> readCharacteristic(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxReadCharacteristicAction(characteristic));
//     }
// 
//     public Observable<BluetoothGattDescriptor> writeDescriptor(BluetoothGattDescriptor descriptor) {
//         return execute(new RxWriteDescriptorAction(descriptor));
//     }
// 
//     public Observable<BluetoothGattDescriptor> readDescriptor(BluetoothGattDescriptor descriptor) {
//         return execute(new RxReadDescriptorAction(descriptor));
//     }
// 
//     public Observable<BluetoothGattCharacteristic> enableNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
//         return enabled ? enableNotification(characteristic) : disableNotification(characteristic);
//     }
// 
//     public Observable<BluetoothGattCharacteristic> enableNotification(final BluetoothGattCharacteristic characteristic) {
//         final UUID uuid = characteristic.getUuid();
//         if (mEnabledNotificationList.containsKey(uuid)) {
//             return mEnabledNotificationList.get(uuid).getResolver();
//         }
//         final RxEnableNotificationAction action = new RxEnableNotificationAction(characteristic);
//         mEnabledNotificationList.put(characteristic.getUuid(), action);
//         return execute(action).doOnUnsubscribe(new Action0() {
//             @Override
//             public void call() {
//                 mEnabledNotificationList.remove(uuid);
//             }
//         });
//     }
// 
//     public Observable<BluetoothGattCharacteristic> disableNotification(BluetoothGattCharacteristic characteristic) {
//         return execute(new RxDisableNotificationAction(characteristic))
//                 .doOnNext(new Action1<BluetoothGattCharacteristic>() {
//                     @Override
//                     public void call(BluetoothGattCharacteristic characteristic) {
//                         mEnabledNotificationList.remove(characteristic.getUuid());
//                     }
//                 });
//     }
// 
//     public Observable<Integer> readRemoteRssi() {
//         return execute(new RxReadRemoteRssiAction());
//     }
// 
//     public Observable<BletiaException> observeError() {
//         return mExceptionSubject;
//     }
// 
//     private final BleEventListener mListener = new BleEventListener() {
//         @Override
//         public void onCharacteristicChanged(final BluetoothGattCharacteristic characteristic) {
//             final UUID uuid = characteristic.getUuid();
//             if (mEnabledNotificationList.containsKey(uuid)) {
//                 mEnabledNotificationList.get(uuid).resolve(characteristic);
//             }
//         }
// 
//         @Override
//         public void onError(BletiaException exception) {
//             mExceptionSubject.onNext(exception);
//         }
//     };
// }


// Path: samples/rxbletia-sample/src/main/java/info/izumin/android/rx/sample/MainActivity.java
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.UUID;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnItemClick;
import info.izumin.android.bletia.core.BleState;
import info.izumin.android.bletia.rx.RxBletia;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subscriptions.CompositeSubscription;

                .flatMap(_void -> mBletia.discoverServices())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(_void -> refresh(true))
                .flatMap(_void -> mBletia.enableNotification(getBatteryStateCharacteristic()))
                .filter(characteristic -> characteristic.getValue() != null)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        characteristic -> mTextBatteryState.setText(String.valueOf(characteristic.getValue()[0])),
                        throwable -> Log.d(TAG, throwable.getMessage())
                );
        mSubscriptions.add(subscription);
    }

    @OnItemClick(R.id.list_devices)
    void onDeviceSelected(int position) {
        BluetoothDevice device = mDeviceAdapter.getItem(position);
        Log.d(TAG, "Connected to " + device.getName() + " !");
        mScanner.stopScan(mScanCallback);
        connect(device);
    }

    @OnClick(R.id.btn_alert_off)
    void onClickBtnAlertOff() {
        mBletia.writeCharacteristic(getAlertLevelCharacteristic(0))
                .subscribe(
                        characteristic -> {},
                        throwable -> Log.d(TAG, throwable.getMessage())
                );
    }

    @OnClick(R.id.btn_alert_vibration)