public void removeNotify(
      UUID serviceUUID, UUID characteristicUUID, Callback success, Callback fail) {

    Log.d(LOG_TAG, "removeNotify");

    if (gatt == null) {
      fail.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findNotifyCharacteristic(service, characteristicUUID);
    // String key = generateHashKey(serviceUUID, characteristic);

    if (characteristic != null) {

      if (gatt.setCharacteristicNotification(characteristic, false)) {
        success.invoke();
      } else {
        // TODO we can probably ignore and return success anyway since we removed the notification
        // callback
        fail.invoke("Failed to stop notification for " + characteristicUUID);
      }

    } else {
      fail.invoke("Characteristic " + characteristicUUID + " not found");
    }
  }
  // NOTE: Currently not reentrant / doesn't support concurrent requests
  @ReactMethod
  public void launchCamera(ReadableMap options, Callback callback) {
    if (options.hasKey("noData")) {
      noData = options.getBoolean("noData");
    }

    Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (cameraIntent.resolveActivity(mMainActivity.getPackageManager()) == null) {
      callback.invoke(true, "error resolving activity");
      return;
    }

    // we create a tmp file to save the result
    File imageFile;
    try {
      imageFile =
          File.createTempFile(
              "exponent_capture_",
              ".jpg",
              Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }
    if (imageFile == null) {
      callback.invoke(true, "error file not created");
      return;
    }
    cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
    mCameraCaptureURI = Uri.fromFile(imageFile);
    mCallback = callback;
    mMainActivity.startActivityForResult(cameraIntent, REQUEST_LAUNCH_CAMERA);
  }
  @Override
  public void onCharacteristicWrite(
      BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    // Log.d(LOG_TAG, "onCharacteristicWrite " + characteristic);

    if (writeCallback != null) {

      if (writeQueue.size() > 0) {
        byte[] data = writeQueue.get(0);
        writeQueue.remove(0);
        // Log.d(LOG_TAG, "rimangono in coda: " + writeQueue.size());
        doWrite(characteristic, data);
      } else {

        if (status == BluetoothGatt.GATT_SUCCESS) {
          writeCallback.invoke();
          // Log.e(LOG_TAG, "writeCallback invocato");
        } else {
          // writeCallback.error(status);
          Log.e(LOG_TAG, "errore onCharacteristicWrite:" + status);
          writeFailCallback.invoke("Error writing status: " + status);
        }

        writeCallback = null;
        writeFailCallback = null;
      }
    } else Log.e(LOG_TAG, "Nessun callback su write");
  }
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // robustness code
    if (mCallback == null
        || (mCameraCaptureURI == null && requestCode == REQUEST_LAUNCH_CAMERA)
        || (requestCode != REQUEST_LAUNCH_CAMERA && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY)) {
      return;
    }

    // user cancel
    if (resultCode != Activity.RESULT_OK) {
      mCallback.invoke(true, Arguments.createMap());
      return;
    }

    WritableMap response = Arguments.createMap();
    Uri uri = (requestCode == REQUEST_LAUNCH_CAMERA) ? mCameraCaptureURI : data.getData();

    // let's set data
    String realPath = getRealPathFromURI(uri);

    response.putString("path", uri.toString());
    response.putString("uri", realPath);
    if (!noData) {
      response.putString("data", getBase64StringFromFile(realPath));
    }

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(realPath, options);
    response.putInt("width", options.outWidth);
    response.putInt("height", options.outHeight);

    try {
      ExifInterface exif = new ExifInterface(realPath);
      int orientation =
          exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
      boolean isVertical = true;
      switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_270:
          isVertical = false;
          break;
        case ExifInterface.ORIENTATION_ROTATE_90:
          isVertical = false;
          break;
      }
      response.putBoolean("isVertical", isVertical);
    } catch (IOException e) {
      e.printStackTrace();
    }

    mCallback.invoke(false, response);
  }
 /**
  * Like {@link #measure} and {@link #measureLayout} but measures relative to the immediate parent.
  */
 public void measureLayoutRelativeToParent(
     int tag, Callback errorCallback, Callback successCallback) {
   try {
     measureLayoutRelativeToParent(tag, mMeasureBuffer);
     float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]);
     float relativeY = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]);
     float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]);
     float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]);
     successCallback.invoke(relativeX, relativeY, width, height);
   } catch (IllegalViewOperationException e) {
     errorCallback.invoke(e.getMessage());
   }
 }
  @ReactMethod
  public void getAuthCredentials(
      ReadableMap args, Callback successCallback, Callback errorCallback) {
    ClientManager clientManager =
        new ClientManager(
            getReactApplicationContext(),
            SalesforceSDKManager.getInstance().getAccountType(),
            SalesforceSDKManager.getInstance().getLoginOptions(),
            SalesforceSDKManager.getInstance().shouldLogoutWhenTokenRevoked());
    RestClient client = clientManager.peekRestClient();

    RestClient.ClientInfo clientInfo = client.getClientInfo();
    try {
      JSONObject data = new JSONObject();
      data.put(ACCESS_TOKEN, client.getAuthToken());
      data.put(REFRESH_TOKEN, client.getRefreshToken());
      data.put(USER_ID, clientInfo.userId);
      data.put(ORG_ID, clientInfo.orgId);
      data.put(CLIENT_ID, clientInfo.clientId);
      data.put(LOGIN_URL, clientInfo.loginUrl.toString());
      data.put(IDENTITY_URL, clientInfo.identityUrl.toString());
      data.put(INSTANCE_URL, clientInfo.instanceUrl.toString());
      data.put(COMMUNITY_ID, clientInfo.communityId);
      data.put(COMMUNITY_URL, clientInfo.communityUrl);

      ReactBridgeHelper.invokeSuccess(successCallback, data);
    } catch (JSONException e) {
      Log.e("OauthReactBridge", "getAuthCredentials", e);
      errorCallback.invoke(e.toString());
    }
  }
 @Override
 public void onDismiss(PopupMenu menu) {
   if (!mConsumed) {
     mSuccess.invoke(UIManagerModuleConstants.ACTION_DISMISSED);
     mConsumed = true;
   }
 }
 @Override
 public void onServicesDiscovered(BluetoothGatt gatt, int status) {
   super.onServicesDiscovered(gatt, status);
   connectCallback.invoke();
   connectCallback = null;
   connectFailCallback = null;
 }
 @ReactMethod
 public String getCurrentToken(final Callback callback) {
   TwitterSession session = Twitter.getSessionManager().getActiveSession();
   if (session != null) {
     TwitterAuthToken authToken = session.getAuthToken();
     String token = authToken.token;
     String secret = authToken.secret;
     if (callback != null) {
       callback.invoke(token);
     }
     return token;
   }
   if (callback != null) {
     callback.invoke(null);
   }
   return null;
 }
  @ReactMethod
  public void showDialog(
      String title,
      String message,
      String button1Text,
      String button2Text,
      final Callback successCallback,
      Callback errorCallback) {

    FragmentActivity fragmentActivity = null;
    try {
      fragmentActivity = (FragmentActivity) mainActivity;
    } catch (ClassCastException e) {
      errorCallback.invoke("Unable to show dialog, main activity is not a FragmentActivity");
      return;
    }

    AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity);

    builder.setCancelable(false);

    DialogInterface.OnClickListener clickListener =
        new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
            switch (which) {
              case DialogInterface.BUTTON_POSITIVE:
                successCallback.invoke(0);
                break;
              case DialogInterface.BUTTON_NEGATIVE:
                successCallback.invoke(1);
                break;
              default:
                throw new CodePushUnknownException("Unknown button ID pressed.");
            }
          }
        };

    if (title != null) {
      builder.setTitle(title);
    }

    if (message != null) {
      builder.setMessage(message);
    }

    if (button1Text != null) {
      builder.setPositiveButton(button1Text, clickListener);
    }

    if (button2Text != null) {
      builder.setNegativeButton(button2Text, clickListener);
    }

    AlertDialog dialog = builder.create();
    dialog.show();
  }
 @Override
 public boolean onMenuItemClick(MenuItem item) {
   if (!mConsumed) {
     mSuccess.invoke(UIManagerModuleConstants.ACTION_ITEM_SELECTED, item.getOrder());
     mConsumed = true;
     return true;
   }
   return false;
 }
  @ReactMethod
  public void logoutCurrentUser(
      ReadableMap args, Callback successCallback, Callback errorCallback) {
    UserAccount account =
        SalesforceSDKManager.getInstance().getUserAccountManager().getCurrentUser();
    SalesforceSDKManager.getInstance().getUserAccountManager().signoutUser(account, null);

    successCallback.invoke();
  }
  // NOTE: Currently not reentrant / doesn't support concurrent requests
  @ReactMethod
  public void launchImageLibrary(final ReadableMap options, final Callback callback) {
    response = Arguments.createMap();

    if (options.hasKey("noData")) {
      noData = options.getBoolean("noData");
    }
    if (options.hasKey("maxWidth")) {
      maxWidth = options.getInt("maxWidth");
    }
    if (options.hasKey("maxHeight")) {
      maxHeight = options.getInt("maxHeight");
    }
    if (options.hasKey("aspectX")) {
      aspectX = options.getInt("aspectX");
    }
    if (options.hasKey("aspectY")) {
      aspectY = options.getInt("aspectY");
    }
    if (options.hasKey("quality")) {
      quality = (int) (options.getDouble("quality") * 100);
    }
    tmpImage = true;
    if (options.hasKey("storageOptions")) {
      tmpImage = false;
    }
    if (options.hasKey("allowsEditing")) {
      allowEditing = options.getBoolean("allowsEditing");
    }
    forceAngle = false;
    if (options.hasKey("angle")) {
      forceAngle = true;
      angle = options.getInt("angle");
    }
    if (options.hasKey("assetProperties")) {
      assetProperties = options.getBoolean("assetProperties");
    }

    Intent libraryIntent =
        new Intent(
            Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

    if (libraryIntent.resolveActivity(mMainActivity.getPackageManager()) == null) {
      response.putString("error", "Cannot launch photo library");
      callback.invoke(response);
      return;
    }

    mCallback = callback;

    try {
      mMainActivity.startActivityForResult(libraryIntent, REQUEST_LAUNCH_IMAGE_LIBRARY);
    } catch (ActivityNotFoundException e) {
      e.printStackTrace();
    }
  }
 public void connect(Callback connectCallback, Callback failCallback, Activity activity) {
   if (!connected) {
     BluetoothDevice device = getDevice();
     this.connectCallback = connectCallback;
     this.connectFailCallback = failCallback;
     gatt = device.connectGatt(activity, false, this);
   } else {
     connectCallback.invoke();
   }
 }
 /**
  * Method called from bridge to get auth credentials
  *
  * @param successCallback
  * @param errorCallback
  */
 public void getAuthCredentials(Callback successCallback, Callback errorCallback) {
   Log.i(TAG, "getAuthCredentials called");
   if (client != null) {
     if (successCallback != null) {
       ReactBridgeHelper.invokeSuccess(successCallback, client.getJSONCredentials());
     }
   } else {
     if (errorCallback != null) {
       errorCallback.invoke("Not authenticated");
     }
   }
 }
  public void read(
      UUID serviceUUID, UUID characteristicUUID, Callback successCallback, Callback failCallback) {

    if (gatt == null) {
      failCallback.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findReadableCharacteristic(service, characteristicUUID);

    if (characteristic == null) {
      failCallback.invoke("Characteristic " + characteristicUUID + " not found.");
    } else {
      readCallback = successCallback;
      readFailCallback = failCallback;
      if (!gatt.readCharacteristic(characteristic)) {
        readCallback = null;
        failCallback.invoke("Read failed");
      }
    }
  }
  @Override
  public void onCharacteristicRead(
      BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicRead(gatt, characteristic, status);
    Log.d(LOG_TAG, "onCharacteristicRead " + characteristic);

    if (readCallback != null) {

      if (status == BluetoothGatt.GATT_SUCCESS) {
        byte[] dataValue = characteristic.getValue();
        String value = BleManager.bytesToHex(dataValue);

        if (readCallback != null) {
          readCallback.invoke(value);
          readCallback = null;
        }
      } else {
        readFailCallback.invoke("Error reading " + characteristic.getUuid() + " status=" + status);
        readFailCallback = null;
      }

      readCallback = null;
    }
  }
  @Override
  public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

    Log.d(
        LOG_TAG,
        "onConnectionStateChange da "
            + status
            + " a "
            + newState
            + " peripheral:"
            + device.getAddress());

    this.gatt = gatt;

    if (newState == BluetoothGatt.STATE_CONNECTED) {

      connected = true;
      gatt.discoverServices();

    } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {

      if (connected) {
        connected = false;

        if (gatt != null) {
          gatt.close();
          gatt = null;
        }

        WritableMap map = Arguments.createMap();
        map.putString("peripheral", device.getAddress());
        sendEvent("BleManagerDisconnectPeripheral", map);
        Log.d(LOG_TAG, "BleManagerDisconnectPeripheral peripheral:" + device.getAddress());
      }
      if (connectFailCallback != null) {
        connectFailCallback.invoke();
        connectFailCallback = null;
        connectCallback = null;
      }
    }
  }
  // This seems way too complicated
  public void registerNotify(
      UUID serviceUUID, UUID characteristicUUID, Callback success, Callback fail) {

    Log.d(LOG_TAG, "registerNotify");

    if (gatt == null) {
      fail.invoke("BluetoothGatt is null");
      return;
    }

    BluetoothGattService service = gatt.getService(serviceUUID);
    BluetoothGattCharacteristic characteristic =
        findNotifyCharacteristic(service, characteristicUUID);
    // String key = generateHashKey(serviceUUID, characteristic);

    if (characteristic != null) {
      Log.d(LOG_TAG, "characteristic ok");

      if (gatt.setCharacteristicNotification(characteristic, true)) {

        BluetoothGattDescriptor descriptor =
            characteristic.getDescriptor(UUID.fromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
        if (descriptor != null) {
          Log.d(LOG_TAG, "trovato descriptor");

          // prefer notify over indicate
          if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
            Log.d(LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
          } else if ((characteristic.getProperties()
                  & BluetoothGattCharacteristic.PROPERTY_INDICATE)
              != 0) {
            Log.d(LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
          } else {
            Log.d(
                LOG_TAG,
                "Characteristic "
                    + characteristicUUID
                    + " does not have NOTIFY or INDICATE property set");
          }

          if (gatt.writeDescriptor(descriptor)) {
            // Tutto ok
            Log.d(LOG_TAG, "registerNotify completato");
            success.invoke();
          } else {
            fail.invoke(
                "Failed to set client characteristic notification for " + characteristicUUID);
          }

        } else {
          fail.invoke("Set notification failed for " + characteristicUUID);
        }

      } else {
        fail.invoke("Failed to register notification for " + characteristicUUID);
      }

    } else {
      fail.invoke("Characteristic " + characteristicUUID + " not found");
    }
  }
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // robustness code
    if (mCallback == null
        || (mCameraCaptureURI == null && requestCode == REQUEST_LAUNCH_CAMERA)
        || (requestCode != REQUEST_LAUNCH_CAMERA
            && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY
            && requestCode != REQUEST_IMAGE_CROPPING)) {
      return;
    }

    // user cancel
    if (resultCode != Activity.RESULT_OK) {
      response.putBoolean("didCancel", true);
      mCallback.invoke(response);
      return;
    }

    Uri uri;
    switch (requestCode) {
      case REQUEST_LAUNCH_CAMERA:
        uri = mCameraCaptureURI;
        break;
      case REQUEST_IMAGE_CROPPING:
        uri = mCropImagedUri;
        break;
      default:
        uri = data.getData();
    }

    if (requestCode != REQUEST_IMAGE_CROPPING && allowEditing == true) {
      Intent cropIntent = new Intent("com.android.camera.action.CROP");
      cropIntent.setDataAndType(uri, "image/*");
      cropIntent.putExtra("crop", "true");

      if (aspectX > 0 && aspectY > 0) {
        // aspectX:aspectY, the ratio of width to height
        cropIntent.putExtra("aspectX", aspectX);
        cropIntent.putExtra("aspectY", aspectY);
        cropIntent.putExtra("scale", true);
      }

      // we create a file to save the result
      File imageFile = createNewFile();
      mCropImagedUri = Uri.fromFile(imageFile);
      cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCropImagedUri);

      try {
        mMainActivity.startActivityForResult(cropIntent, REQUEST_IMAGE_CROPPING);
      } catch (ActivityNotFoundException e) {
        e.printStackTrace();
      }
      return;
    }

    String realPath = getRealPathFromURI(uri);
    boolean isUrl = false;

    if (realPath != null) {
      try {
        URL url = new URL(realPath);
        isUrl = true;
      } catch (MalformedURLException e) {
        // not a url
      }
    }

    if (realPath == null || isUrl) {
      try {
        File file = createFileFromURI(uri);
        realPath = file.getAbsolutePath();
        uri = Uri.fromFile(file);
      } catch (Exception e) {
        response.putString("error", "Could not read photo");
        response.putString("uri", uri.toString());
        mCallback.invoke(response);
        return;
      }
    }

    ExifInterface exif = null;
    int CurrentAngle = 0;
    try {
      exif = new ExifInterface(realPath);
      int orientation =
          exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
      boolean isVertical = true;
      switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_270:
          isVertical = false;
          CurrentAngle = 270;
          break;
        case ExifInterface.ORIENTATION_ROTATE_90:
          isVertical = false;
          CurrentAngle = 90;
          break;
        case ExifInterface.ORIENTATION_ROTATE_180:
          CurrentAngle = 180;
          break;
      }
      response.putBoolean("isVertical", isVertical);
    } catch (IOException e) {
      e.printStackTrace();
      response.putString("error", e.getMessage());
      mCallback.invoke(response);
      return;
    }

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    Bitmap photo = BitmapFactory.decodeFile(realPath, options);
    int initialWidth = options.outWidth;
    int initialHeight = options.outHeight;

    // don't create a new file if contraint are respected
    if (((initialWidth < maxWidth && maxWidth > 0) || maxWidth == 0)
        && ((initialHeight < maxHeight && maxHeight > 0) || maxHeight == 0)
        && quality == 100
        && (!forceAngle || (forceAngle && CurrentAngle == angle))) {
      response.putInt("width", initialWidth);
      response.putInt("height", initialHeight);
    } else {
      File resized = getResizedImage(getRealPathFromURI(uri), initialWidth, initialHeight);
      realPath = resized.getAbsolutePath();
      uri = Uri.fromFile(resized);
      photo = BitmapFactory.decodeFile(realPath, options);
      response.putInt("width", options.outWidth);
      response.putInt("height", options.outHeight);
    }

    response.putString("uri", uri.toString());
    response.putString("path", realPath);

    if (!noData) {
      response.putString("data", getBase64StringFromFile(realPath));
    }

    if (assetProperties) {
      WritableNativeMap assetPropertiesMap = new WritableNativeMap();

      if (options.outMimeType != null) {
        assetPropertiesMap.putString("mimeType", options.outMimeType);
      }

      String[] splitPath = realPath.split("/");
      if (splitPath.length > 0) {
        assetPropertiesMap.putString("fileName", splitPath[splitPath.length - 1]);
      }

      File file = new File(realPath);
      if (file != null) {
        assetPropertiesMap.putDouble("fileSize", file.length());
      }

      response.putMap("assetProperties", assetPropertiesMap);
      mCallback.invoke(response);
    } else {
      mCallback.invoke(response);
    }
  }
  // NOTE: Currently not reentrant / doesn't support concurrent requests
  @ReactMethod
  public void launchCamera(final ReadableMap options, final Callback callback) {
    response = Arguments.createMap();

    if (options.hasKey("noData")) {
      noData = options.getBoolean("noData");
    }
    if (options.hasKey("maxWidth")) {
      maxWidth = options.getInt("maxWidth");
    }
    if (options.hasKey("maxHeight")) {
      maxHeight = options.getInt("maxHeight");
    }
    if (options.hasKey("aspectX")) {
      aspectX = options.getInt("aspectX");
    }
    if (options.hasKey("aspectY")) {
      aspectY = options.getInt("aspectY");
    }
    if (options.hasKey("quality")) {
      quality = (int) (options.getDouble("quality") * 100);
    }
    tmpImage = true;
    if (options.hasKey("storageOptions")) {
      tmpImage = false;
    }
    if (options.hasKey("allowsEditing")) {
      allowEditing = options.getBoolean("allowsEditing");
    }
    forceAngle = false;
    if (options.hasKey("angle")) {
      forceAngle = true;
      angle = options.getInt("angle");
    }
    if (options.hasKey("assetProperties")) {
      assetProperties = options.getBoolean("assetProperties");
    }

    Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (cameraIntent.resolveActivity(mMainActivity.getPackageManager()) == null) {
      response.putString("error", "Cannot launch camera");
      callback.invoke(response);
      return;
    }

    // we create a tmp file to save the result
    File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    File imageFile;
    try {
      // Make sure the Pictures directory exists.
      path.mkdirs();
      imageFile = File.createTempFile("capture", ".jpg", path);
    } catch (IOException e) {
      e.printStackTrace();
      response.putString("error", e.getMessage());
      callback.invoke(response);
      return;
    }
    cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
    mCameraCaptureURI = Uri.fromFile(imageFile);
    mCallback = callback;

    try {
      mMainActivity.startActivityForResult(cameraIntent, REQUEST_LAUNCH_CAMERA);
    } catch (ActivityNotFoundException e) {
      e.printStackTrace();
    }
  }
 /**
  * Returns user's Distinct ID that is automatically assigned by Mixpanel.
  *
  * @param successCallback
  */
 @ReactMethod
 public void getDistinctId(Callback successCallback) {
   successCallback.invoke(mixpanel.getDistinctId());
 }
  public void write(
      UUID serviceUUID,
      UUID characteristicUUID,
      byte[] data,
      Callback successCallback,
      Callback failCallback,
      int writeType) {

    Log.d(LOG_TAG, "write interno peripheral");

    if (gatt == null) {
      failCallback.invoke("BluetoothGatt is null");

    } else {

      BluetoothGattService service = gatt.getService(serviceUUID);
      BluetoothGattCharacteristic characteristic =
          findWritableCharacteristic(service, characteristicUUID, writeType);
      characteristic.setWriteType(writeType);

      if (characteristic == null) {
        failCallback.invoke("Characteristic " + characteristicUUID + " not found.");
      } else {

        if (writeQueue.size() > 0) {
          failCallback.invoke("Scrittura con byte ancora in coda");
        }

        if (writeCallback != null) {
          failCallback.invoke("Altra scrittura in corso");
        }

        if (writeQueue.size() == 0 && writeCallback == null) {

          if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
            writeCallback = successCallback;
            writeFailCallback = failCallback;
          } else successCallback.invoke();

          if (data.length > 20) {
            int dataLength = data.length;
            int count = 0;
            byte[] firstMessage = null;
            while (count < dataLength && (dataLength - count > 20)) {
              if (count == 0) {
                firstMessage = Arrays.copyOfRange(data, count, count + 20);
              } else {
                byte[] splitMessage = Arrays.copyOfRange(data, count, count + 20);
                writeQueue.add(splitMessage);
              }
              count += 20;
            }
            if (count < dataLength) {
              // Rimangono byte in coda
              byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
              Log.d(LOG_TAG, "Lunghezza ultimo messaggio: " + splitMessage.length);
              writeQueue.add(splitMessage);
            }

            Log.d(LOG_TAG, "Messaggi in coda: " + writeQueue.size());
            doWrite(characteristic, firstMessage);
          } else {
            characteristic.setValue(data);

            if (gatt.writeCharacteristic(characteristic)) {
              Log.d(LOG_TAG, "write completato");
            } else {
              writeCallback = null;
              failCallback.invoke("Write failed");
            }
          }
        }
      }
    }
  }