Region Help

Hi Everyone,

I have been trying out the Android SDK, mainly 'beaconManager.startRanging(ALLESTIMOTEBEACONS)' to get the nearest Beacon but it seems that signals from Beacons always overlaps each other, for example if I placed 3 Beacons in a triangle shape where the distance between each two Beacons is ~6 meters signals from the different Beacons always overlaps each other and the Android application keeps jumping from one Beacon to another

Of course when I try to serve some content from an online server this gets worse since the network traffic and the size of the online content will make effect as well

Did you face a similar situation? Any ideas how to resolve this issue?

Thanks
Dawlet

Hi there—when you say that your Android application "jumps" from beacon to beacon, what does that mean?

So I am associating each Beacon with an image so the code checks the nearest Beacon and hence display the associated image but when I go to the nearest Beacon with the mobile application the images of one of the far Beacons might appear for a couple of seconds first before I get the image associated with the nearest Beacon, I will add the code I am using:

public class MainActivity extends Activity
{
private static final String ESTIMOTEPROXIMITYUUID = "B9407F30-F5F8-466E-AFF9-25556B57FE6D";

private static final Region ALL_ESTIMOTE_BEACONS = new Region("regionId", ESTIMOTE_PROXIMITY_UUID, null, null);

protected static final String TAG = "EstimoteiBeacon";

private static final int NOTIFICATION_ID = 123;

BeaconManager beaconManager;
NotificationManager notificationManager;

ImageView image;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (android.os.Build.VERSION.SDK_INT > 9)
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());

    image = (ImageView) findViewById(R.id.white_image);

    beaconManager = new BeaconManager(this);
    notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    //---by default you scan 5s and then wait 25s for this demo, you will scan more frequently---
    beaconManager.setBackgroundScanPeriod(TimeUnit.SECONDS.toMillis(1), 0);

    beaconManager.setMonitoringListener(new MonitoringListener()
    {
        @Override
        public void onEnteredRegion(Region region, List<Beacon> beacons)
        {
            if (isAppInForeground(getApplicationContext()))
            {
                Toast.makeText(getApplicationContext(), "Entered region", Toast.LENGTH_LONG).show();
                //---range for beacons---
                try
                {
                    beaconManager.startRanging(ALL_ESTIMOTE_BEACONS);
                }
                catch (RemoteException e)
                {
                    Log.e(TAG, "Cannot start ranging", e);
                }
                //---range for beacons---
            }
            else
            {
                postNotification("Entered region");
            }
        }

        @Override
        public void onExitedRegion(Region region)
        {
            if (isAppInForeground(getApplicationContext()))
            {
                Toast.makeText(getApplicationContext(), "Exited region", Toast.LENGTH_LONG).show();
            }
            else
            {
                postNotification("Exited region");
            }

            //---stop ranging for beacons---
            try
            {
                beaconManager.stopRanging(ALL_ESTIMOTE_BEACONS);
            }
            catch (RemoteException e)
            {
                Log.e(TAG, e.getLocalizedMessage());
            }
            //---stop ranging for beacons---
            runOnUiThread(new Runnable()
            {
                @Override
                public void run()
                {
                    //---clear the first group of EditText fields---
                }
            });
        }
    });
    //---called when beacons are found---
    beaconManager.setRangingListener(new
    BeaconManager.RangingListener()
    {
        @Override public void onBeaconsDiscovered(Region region, final List<Beacon> beacons)
        {
            Log.d(TAG, "Ranged beacons: " + beacons);

            runOnUiThread(new Runnable()
            {
                @Override
                public void run()
                {
                    if (beacons.size() > 0)
                    {
                        //NOTE: You should pass url and imageID
                        String url = "http://domainname.com/imgserver.php";

                        Beacon iBeacon1 = null;
                        //---get the first iBeacon---
                        iBeacon1 = beacons.get(0);

                        //Check which Beacon is the nearest Beacon and get its Major, Minor and RSSI
                        String nearestBeaconMajor = String.valueOf(iBeacon1.getMajor());
                        //String nearestBeaconMinor = String.valueOf(iBeacon1.getMinor());
                        //String nearestBeaconRSSI  = String.valueOf(iBeacon1.getRssi());

                        //Concatenate UUID + Major + Minor + RSSI
                        String imageID = ESTIMOTE_PROXIMITY_UUID + "-" + nearestBeaconMajor;
                        //Set the image served from the server
                        SetImageResource(url, imageID, image);
                    }
                    else
                    {
                        String url = "http://domainname.com/imgserver.php";
                        String imageID = "DefaultImage";
                        //Set the default image
                        SetImageResource(url, imageID, image);                        }
                }
            });
        }
    });
}

@Override
protected void onStart()
{
    super.onStart();

    notificationManager.cancel(NOTIFICATION_ID);
    beaconManager.connect(new BeaconManager.ServiceReadyCallback()
    {
        @Override
        public void onServiceReady()
        {
            try
            {
                beaconManager.startMonitoring(ALL_ESTIMOTE_BEACONS);
            }
            catch (RemoteException e)
            {
                Log.d(TAG,
                "Error while starting monitoring");
            }
        }
    });
}

//---stop ranging for beacons when activity is killed---
@Override
protected void onStop()
{
    super.onStop();
    try
    {
        beaconManager.stopRanging(ALL_ESTIMOTE_BEACONS);
    }
    catch (RemoteException e)
    {
        Log.e(TAG, "Cannot stop", e);
    }
}

@Override
protected void onDestroy()
{
    super.onDestroy();
    notificationManager.cancel(NOTIFICATION_ID);
    beaconManager.disconnect();
}

private void postNotification(String msg)
{
    Intent notifyIntent = new Intent(MainActivity.this, MainActivity.class);

    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivities(MainActivity.this, 0, new Intent[] { notifyIntent }, PendingIntent.FLAG_UPDATE_CURRENT);

    Notification notification = new Notification.Builder(MainActivity.this)
                                                .setSmallIcon(R.drawable.ic_launcher)
                                                .setContentTitle("Monitoring Region")
                                                .setContentText(msg)
                                                .setAutoCancel(true)
                                                .setContentIntent(pendingIntent)
                                                .build();
    notification.defaults |= Notification.DEFAULT_SOUND;
    notification.defaults |= Notification.DEFAULT_LIGHTS;
    notificationManager.notify(NOTIFICATION_ID,
    notification);
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    // Inflate the menu; this adds items to the
    // action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

//---helper method to determine if the app is in the foreground---
public static boolean isAppInForeground(Context context)
{
    List<RunningTaskInfo> task = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1);
    if (task.isEmpty())
    {
        return false;
    }
    return task.get(0)
                .topActivity
                .getPackageName()
                .equalsIgnoreCase(context.getPackageName());
}

/**
 * @param url http://http://domainname.com/imgserver.php
 * @param imageID B9407F30-F5F8-466E-AFF9-25556B57FE6D-55473
 * @param image The ImageView you want to display
 */
public void SetImageResource(String url, String imageID, final ImageView image)
{
    getRedirectUrlAndDownloadImageBitmapFromServer(url, imageID, new ICallBack()
    {
        @Override
        public Object doIt(Object... params)
        {
            // TODO Auto-generated method stub
            final Bitmap myBitmap = (Bitmap) params[0];
            if(myBitmap!= null)
            {
                runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // TODO Auto-generated method stub
                        image.setImageBitmap(myBitmap);
                    }
                });
                Log.e("BITMAP", "OK DONE");
            }

            return null;
        }
    });
}

/**
 * @param url http://http://domainname.com/imgserver.php
 * @param imageID B9407F30-F5F8-466E-AFF9-25556B57FE6D-55473
 * @param onCompleteCallback It runs when getting redirected URL success
 */
public static void getRedirectUrlAndDownloadImageBitmapFromServer(String url, String imageID, final ICallBack onCompleteCallback)
{
    class RedirectUrlAsync extends AsyncTask<String, Void, Bitmap>
    {
        @Override
        protected Bitmap doInBackground(String... params)
        {
            String url = params[0];
            String imageID = params[1];

            HttpParams httpParams = new BasicHttpParams();
            ConnManagerParams.setMaxTotalConnections(httpParams, 100);
            HttpConnectionParams.setConnectionTimeout(httpParams, 20 * 1000);
            HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset (httpParams, HTTP.UTF_8);

            SchemeRegistry schemeRegistry = new SchemeRegistry();
            schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));

            ClientConnectionManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);

            HttpClientParams.setRedirecting(httpParams, false);

            HttpClient client = new DefaultHttpClient(cm, httpParams);

            HttpGet method = new HttpGet(url + "?ID=" + imageID);
            HttpResponse resp;

            String imageName = "";
            try
            {
                resp = client.execute(method);

                if(resp.getEntity().getContentLength() > 0)
                {
                    StringBuilder sb = new StringBuilder();
                    try
                    {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
                        String line = null;

                        while ((line = reader.readLine()) != null)
                        {
                            sb.append(line);
                        }
                    }
                    catch (IOException e) { e.printStackTrace(); }
                    catch (Exception e) { e.printStackTrace(); }

                    imageName = sb.toString();
                }
            }
            catch (ClientProtocolException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            String redirectedUrl = "";

            Pattern titleFinder = Pattern.compile("<img[^>]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>" , Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
            Matcher regexMatcher = titleFinder.matcher(imageName);
            while (regexMatcher.find())
            {
                redirectedUrl = "http://domainname.com/imgserver.php" + regexMatcher.group(1);
                Log.e("redirectedUrl", redirectedUrl);
            }

            Bitmap myBitmap = DownloadImageFromServer(redirectedUrl);

            return myBitmap;
        }

        @Override
        protected void onPostExecute(Bitmap myBitmap)
        {
            // TODO Auto-generated method stub

            onCompleteCallback.doIt(myBitmap);

            super.onPostExecute(myBitmap);
        }

        @Override
        protected void onPreExecute()
        {
            // TODO Auto-generated method stub
            super.onPreExecute();
        }
    }

    RedirectUrlAsync async = new RedirectUrlAsync();
    async.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, imageID);
}

private static Bitmap DownloadImageFromServer(String url)
{
    InputStream in;
    try
    {
        in = new java.net.URL(url).openStream();
        Bitmap myBitmap = BitmapFactory.decodeStream(in);

        return myBitmap;
    }
    catch (MalformedURLException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return null;
}

public interface ICallBack
{
    public Object doIt(Object... params);
}

}

Got it, thanks. Distance is just an approximation based on the strength of the signal and is this susceptible to fluctuations. This is something that you'll have to code around I'm afraid.

One idea: only change the image after the beacon has been consistently reported as the closest one for a few seconds.

Another idea: compute an average of the beacon distance over the past few seconds, to smooth out any fluctuations.

Ok, that's what I thought as well but was double checking in case there is a method/API in the SDK that can solve such issue

Thank you

Is there anything in the API that does this?

For me having something that can determine your fixed beacon which is determined from a sample set after X readings is very important part of the platform