Downloading Buildings

Once we have a building stored in the NEON Cloud, we want to direct the NEON Location Service to download all the buildings in our area so we can use them for tracking.

We’re going to:

  1. Create a background Thread to handle the potentially heavy network operations of downloading buildings and floorplans.
  2. Create a Runnable that, every 5 seconds, pulls the current view bounds from the activity and issues a request to download buildings in that area.
  3. Create an INeonBuildingListener that will verify that we have downloaded the buildings that we have created.

Handler and Looper Configuration

First, let’s declare a Handler that points to the main thread, and a Looper with an associated Handler as shown below.

private Handler mainHandler = new Handler(Looper.getMainLooper());

private Looper dataLooper;
private Handler dataHandler;

In our onCreate() we can start a new Thread and set the Looper and Handler as shown below.

/*
 * Create a new background thread, call Looper.prepare() so it
 * can handle posts, and grab a dataHandler for loading
 * buildings and floor plan images
 */
Thread t = new Thread(new Runnable() {
    public void run() {
        Looper.prepare();
        dataLooper = Looper.myLooper();
        dataHandler = new Handler(dataLooper);
        Looper.loop();
    }
});
t.start();

In our shutdown code we should quit the looper.

private void shutdown()
{
    disconnectFromNeonAPI();

    if (dataLooper != null) {
        dataLooper.quit();
        dataLooper = null;
    }
}

NEON Building Listener

Next, we should implement INeonBuildingListener in our activity and an add an onComplete() method that will be called with a list of buildings as shown below.

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback, LocationSource, INeonLocationListener, INeonEventListener, INeonBuildingListener {
    @Override
    public void onComplete(List<NeonBuilding> list, DownloadResult downloadResult) {
        if (downloadResult == DownloadResult.SUCCESS)
            Log.i("NEONSampleApp", "got " + list.size() +" buildings");
        else
            Log.i("NEONSampleApp", "result failed with: " + list.toString());
    }
    ...
}

We want to start loading buildings once we have successfully logged into our NEON account, so let’s post a new function that loads buildings on the success event as shown below.

@Override
public void onEvent(NeonEventType type, INeonEvent event) {
    Log.i("MapsActivity", "Got an event of type: " + type.toString());
    switch (type)
    {
        case AUTHENTICATION:
            AuthenticationEvent ae = (AuthenticationEvent)event;
            if (ae.getType() == null)
                break;
            Log.i("MapsActivity", "Got an event of type: " + ae.getType().toString());

            switch (ae.getType())
            {
                case NO_CREDENTIALS_SET: NeonSettings.startLoginActivityForResult(LOGIN_ACTIVITY_REQUEST_CODE, MapsActivity.this, false); break;
                case MANDATORY_UPDATE_REQUIRED: NeonSettings.upgradeNeonLocationServices(MapsActivity.this, true); break;
                case UNRESOLVED_AUTHENTICATION_ERROR: Toast.makeText(getApplicationContext(), "Error in login.  Please visit NEON Settings page to resolve errors",Toast.LENGTH_LONG).show(); break;
                case SUCCESS:
                    Log.i("MapsActivity", "successfully logged in to Neon Location Service");

                    //starts loading buildings for display
                    mainHandler.post(loadBuildingsRunnable);
                    break;
                default: break;
            }
            break;
        default: break;
    }
}

 /**
    * Every five seconds, this runnable will get the area in view on the screen
    * and issue a request to download all the buildings for that area.
    * The work will be performed on the dataHandler, which points to a background thread.
    * Results will be returned on the buildingCallback, where they will be drawn to the screen
    * Because the DownloadOption is CACHED, the first request to download buildings in this
    * area will take longer as it has to hit the network, but subsequent calls will be much quicker
    * since the buildings are already cached in the Neon Location Service.
    */
private Runnable loadBuildingsRunnable = new Runnable() {
    @Override
    public void run() {
        Log.i("MapsActivity", "downloading building");

        //get bounds from google map and convert to LatLongRect
        final LatLngBounds viewingRect = mMap.getProjection().getVisibleRegion().latLngBounds;
        final LatLongRect bounds = new LatLongRect(new LatLong(viewingRect.southwest.latitude, viewingRect.southwest.longitude), new LatLong(viewingRect.northeast.latitude, viewingRect.northeast.longitude));

        NeonEnvironment.downloadBuildingsInArea(bounds, dataHandler, MapsActivity.this, DownloadOptions.CACHED);

        mainHandler.postDelayed(this, 5000);
    }
};

Don’t forget to remove the callback when you shut down.

private void shutdown()
{
    disconnectFromNeonAPI();

    if (dataLooper != null) {
        dataLooper.quit();
        dataLooper = null;
    }

    mainHandler.removeCallbacks(loadBuildingsRunnable);
}