package no.nordicsemi.puckcentral.location; import com.radiusnetworks.ibeacon.IBeacon; import org.droidparts.Injector; import org.droidparts.annotation.inject.InjectDependency; import org.droidparts.bus.EventBus; import org.joda.time.DateTime; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import no.nordicsemi.puckcentral.bluetooth.gatt.CubeConnectionManager; import no.nordicsemi.puckcentral.db.PuckManager; import no.nordicsemi.puckcentral.models.Puck; import no.nordicsemi.puckcentral.triggers.Trigger; public class LocationManager { @InjectDependency PuckManager mPuckManager; @InjectDependency CubeConnectionManager mCubeManager; private Puck mClosestPuck; private DateTime mLastChanged; private final int THROTTLE = 3; private boolean injected = false; public LocationManager() { mLastChanged = new DateTime(); } public void updateLocation(Collection<IBeacon> iBeacons) { if (mLastChanged.plusSeconds(THROTTLE).isAfterNow()) { return; } if (!injected) { Injector.inject(Injector.getApplicationContext(), this); injected = true; } // There are few cases where there are exactly 0 iBeacons present, if (iBeacons.size() == 0) { setLocation(null); return; } IBeacon[] iBeaconsArray = iBeacons.toArray(new IBeacon[iBeacons.size()]); Arrays.sort(iBeaconsArray, new Comparator<IBeacon>() { @Override public int compare(IBeacon a, IBeacon b) { return a.getAccuracy() - b.getAccuracy() < 0 ? 1 : -1; } }); for (IBeacon iBeacon : iBeaconsArray) { Puck puck = mPuckManager.forIBeacon(iBeacon); if (puck != null) { mCubeManager.checkAndConnectToPuck(puck); } } for (IBeacon iBeacon : iBeaconsArray) { if(iBeacon.getProximity() == IBeacon.PROXIMITY_IMMEDIATE) { setLocation(iBeacon); return; } } for (IBeacon iBeacon : iBeaconsArray) { if(iBeacon.getProximity() == IBeacon.PROXIMITY_NEAR) { setLocation(iBeacon); return; } } setLocation(null); } private void setLocation(IBeacon iBeacon) { mLastChanged = new DateTime(); if (iBeacon == null) { leaveCurrentZone(); return; } Puck newClosestPuck = mPuckManager.forIBeacon(iBeacon); if (newClosestPuck == null) { if (iBeacon.getProximity() == IBeacon.PROXIMITY_IMMEDIATE) { EventBus.postEvent(Trigger.TRIGGER_ZONE_DISCOVERED, iBeacon); } else { leaveCurrentZone(); } } else if (!newClosestPuck.equals(mClosestPuck)) { leaveCurrentZone(); enterNewZone(newClosestPuck); } } private void leaveCurrentZone() { if (mClosestPuck == null) { return; } broadcastNewClosestPuck(null); Trigger.trigger( mClosestPuck, Trigger.TRIGGER_LEAVE_ZONE); mClosestPuck = null; } private void enterNewZone(Puck newClosestPuck) { mClosestPuck = newClosestPuck; broadcastNewClosestPuck(mClosestPuck); Trigger.trigger( mClosestPuck, Trigger.TRIGGER_ENTER_ZONE); } public Puck getCurrentLocation() { return mClosestPuck; } public void broadcastNewClosestPuck(Puck puck) { EventBus.postEvent(Trigger.TRIGGER_CLOSEST_PUCK_CHANGED, puck); } }