/** Notifies the listener about possible region-based events */
  private void notifyListener() {

    IBeacon newNearestBeacon = null;

    if (_arrOrderedIBeacons.size() > 0) newNearestBeacon = _arrOrderedIBeacons.get(0);

    // Case 1: enter iBeacon region from nowhere
    if (_previousNearestIBeacon == null && newNearestBeacon != null) {
      _listener.enterRegion(newNearestBeacon);
      _previousNearestIBeacon = newNearestBeacon;
    }
    // Case 2: keep in the same iBeacon region, update proximity
    else if (_previousNearestIBeacon != null
        && newNearestBeacon != null
        && _previousNearestIBeacon.equals(newNearestBeacon)) {
      _previousNearestIBeacon = newNearestBeacon;
    }
    // Case 3: enter a different iBeacon region (roaming)
    else if (_previousNearestIBeacon != null
        && newNearestBeacon != null
        && !_previousNearestIBeacon.equals(newNearestBeacon)) {
      _listener.exitRegion(_previousNearestIBeacon);
      _listener.enterRegion(newNearestBeacon);
      _previousNearestIBeacon = newNearestBeacon;
    }
    // Case 4: leave iBeacon region
    else if (_previousNearestIBeacon != null && newNearestBeacon == null) {
      _listener.exitRegion(_previousNearestIBeacon);
      _previousNearestIBeacon = null;
    }
  }
 @Override
 public void run() {
   _scanning = false;
   _bluetoothAdapter.stopLeScan(mLeScanCallback);
   if (_arrOrderedIBeacons.size() == 0) _listener.searchState(SEARCH_END_EMPTY);
   else _listener.searchState(SEARCH_END_SUCCESS);
   notifyListener();
 }
  /**
   * Starts or stops the scanning process looking for iBeacons
   *
   * @param enable <code>true</code> to start scanning, <code>false</code> to stop the scanning
   *     process
   */
  public void scanIBeacons(final boolean enable) {
    if (enable) {
      // Stops scanning after a pre-defined scan period.
      _timeoutHandler = new Handler();
      _timeoutHandler.postDelayed(timeoutTask, IBeaconProtocol.SCANNING_PERIOD);

      _scanning = true;
      _arrOrderedIBeacons.clear();
      _bluetoothAdapter.startLeScan(mLeScanCallback);
      _listener.searchState(SEARCH_STARTED);
    } else {
      _scanning = false;
      _bluetoothAdapter.stopLeScan(mLeScanCallback);
      _listener.searchState(SEARCH_END_SUCCESS);
    }
    // Cannot obtain error status=133 this way
    Log.i(
        Utils.LOG_TAG,
        "The status:" + _bluetoothAdapter.getProfileConnectionState(BluetoothProfile.GATT));
  }
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {

          Log.i(Utils.LOG_TAG, "BLE packet received");

          IBeacon newBeacon = parseAdvertisementData(scanRecord);
          if (newBeacon == null) return;

          newBeacon.setMacAddress(device.getAddress());

          // If already discovered, then just refresh the RSSI of the existing instance and return
          if (_arrOrderedIBeacons.contains(newBeacon)) {

            double newDistance = calculateDistance(newBeacon.getPowerValue(), rssi);
            IBeacon previousIBeaconInfo = findIfExists(newBeacon);
            double oldDistance = previousIBeaconInfo.getProximity();

            if (newDistance < oldDistance) {
              Log.i(Utils.LOG_TAG, "Updating distance");
              previousIBeaconInfo.setProximity(newDistance);
              // Sort again
              Collections.sort(_arrOrderedIBeacons, new IBeaconProximityComparator());
            }
            return;
          }

          newBeacon.setEasiBeacon(false);

          if (device.getName() != null) {

            Log.e("device.getName()", String.valueOf(device.getName()));
            if (device.getName().startsWith(EASIBEACON_IDPREFIX)) {

              newBeacon.setEasiBeacon(true);
              String version = device.getName().substring(EASIBEACON_IDPREFIX.length());
              newBeacon.setVersionModel(version);

              if (newBeacon.getVersion() == 1) {
                // Version 1 is always connectable
                newBeacon.setConnectable(true);

              } else if (newBeacon.getVersion() == 2) {

                newBeacon.setConnectable(getConnectable(scanRecord));
                if (!newBeacon.isConnectable())
                  newBeacon.setEasiBeacon(false); // If not connectable we will report it as unknown
              }
            }
          }
          // Review this
          Log.i(
              Utils.LOG_TAG,
              device.getName()
                  + " "
                  + device.getAddress()
                  + " "
                  + newBeacon.getPowerValue()
                  + " "
                  + rssi
                  + " Connectable: "
                  + newBeacon.isConnectable());
          newBeacon.setProximity(calculateDistance(newBeacon.getPowerValue(), rssi));

          if (!_arrOrderedIBeacons.contains(newBeacon)) {

            _arrOrderedIBeacons.add(newBeacon);
            Collections.sort(_arrOrderedIBeacons, new IBeaconProximityComparator());
            _listener.beaconFound(newBeacon);

            // Every time a new beacon is found, reset the timeout
            _timeoutHandler.removeCallbacks(timeoutTask);
            _timeoutHandler.postDelayed(timeoutTask, IBeaconProtocol.SCANNING_PERIOD);
          }
        }