Search code examples
javaibeacon-androidkontakt.io

Kontakt.io SDK: Is there a way to hard code in a list of beacons so that I can pull from them randomly whenever I need to search for a beacon?


I am programming an app that is intended to help children get up and active in the classroom by assigning questions to beacons. The app works like this:

  1. enter a gamecode from the api (similar to Kahoot)
  2. start scanning for beacons
  3. upon finding and being within a meter of a certain beacon, enter the question screen and ask a question that the user must answer.
  4. repeat steps 2 and 3 until out of questions.

The problem in question comes from the 2nd step of the process. I am looking to get a list of all beacons I have (either hardcoded into the app, or gathering them on the fly) so that I can easily just pick a random beacon and have the user go find that beacon to answer the next question. When I try to hard code in the beacons, the distancing does not work (it just reads 0.0), and when I try to gather them while the app runs, the app will crash or hang whenever the beacons update. I am using the Kontakt Android SDK.

I have tried putting for loops in onIBeaconsUpdated to gather from the list of updating beacons and just pick from those randomly, but it will constantly change the beacon that it is looking for, making it impossible for the user to complete.

I have also tried making a list of beacons using the details from beacons on the Kontakt.io web panel, but I must be missing a step that links these generated beacons to the physical beacons or something because listOfHardcodedBeacons.get(0).getDistance() will always return 0.0 no matter which beacon you're looking at or how far you are from the beacon.

Please excuse my terrible code:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_scanner);
        distanceTV = findViewById(R.id.distanceText);
        setupProximityManager();
        fillBeaconList(listOfHardcodedBeacons);
        randint = random.nextInt(listOfHardcodedBeacons.size());

        super.onCreate(savedInstanceState);
    }

    private void fillBeaconList(List<IBeaconDevice> beaconList) {

        //beacon #1
        BeaconDevice beacon1 = new BeaconDevice.Builder()
                .uniqueId("q0hg")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(1)
                .minor(0)
                .address("D3:95:F8:1E:CB:6E")
                .build();

        //beacon #2
        BeaconDevice beacon2 = new BeaconDevice.Builder()
                .uniqueId("eHI1")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(1)
                .minor(1)
                .address("CF:FE:16:60:0D:50")
                .build();

        //beacon #3
        BeaconDevice beacon3 = new BeaconDevice.Builder()
                .uniqueId("R9Bq")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(1)
                .minor(2)
                .address("CF:81:E3:B6:D1:D0")
                .build();

        //beacon #4
        BeaconDevice beacon4 = new BeaconDevice.Builder()
                .uniqueId("zUiD")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(55910)
                .minor(12620)
                .address("DC:65:69:47:93:26")
                .build();

        //beacon #5
        BeaconDevice beacon5 = new BeaconDevice.Builder()
                .uniqueId("QR18")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(30785)
                .minor(10106)
                .address("D7:1F:B8:AF:85:B2")
                .build();

        //beacon #6
        BeaconDevice beacon6 = new BeaconDevice.Builder()
                .uniqueId("uhnG")
                .proximityUUID(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
                .major(48261)
                .minor(35926)
                .address("CC:2C:82:59:93:C6")
                .build();

        beaconList.add(beacon1);
        beaconList.add(beacon2);
        beaconList.add(beacon3);
        beaconList.add(beacon4);
        beaconList.add(beacon5);
        beaconList.add(beacon6);
    }

    //TODO: figure out list of known beacons and do stuff
    private IBeaconListener createIBeaconListener() {

        return new IBeaconListener() {
            @Override
            public void onIBeaconDiscovered(IBeaconDevice ibeacon, IBeaconRegion region) {

                //Toast.makeText(getApplicationContext(), String.format("I found beacon %s! :D", ibeacon.getUniqueId()), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onIBeaconsUpdated(List<IBeaconDevice> ibeaconList, IBeaconRegion region) {

                /* This was my original code. It pulls the closest beacon. It isn't what I want because I don't want the user to keep pulling questions from the same beacon.

                String rounded = df.format(ibeaconList.get(0).getDistance());
                distanceTV.setText(String.format("Distance to %s: ", ibeaconList.get(0).getUniqueId()) + rounded);
                if(ibeaconList.get(0).getDistance() <= 2.7432) { //2.7432 = 9 feet
                    distanceTV.setText("Found it! :D");
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            toQuestions();
                        }
                    }, 1000);
                    proximityManager.stopScanning();
                }
                */

            }

            @Override
            public void onIBeaconLost(IBeaconDevice ibeacon, IBeaconRegion region) {
                //Toast.makeText(getApplicationContext(), String.format("I lost beacon %s! D:", ibeacon.getUniqueId()), Toast.LENGTH_SHORT).show();
            }

        };
    }

    public void toQuestions() {

        Intent intent = new Intent(this, QuestionActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        this.startActivity(intent);
        finish();
    }

As of now, the code will only pull from the first beacon in ibeaconList and tell the user to go to that one, but that isn't ideal for the goal of the app. As stated earlier, I want to pull from a random list of predefined beacons and just shuffle through the list randomly. I have written and deleted probably 4 different non functioning solutions to my problem and I'm just not sure what to do at this point. Any help would be appreciated. I did my best to make this as clear as possible but if you still need more information I will be happy to provide it.


Solution

  • to anyone that may read this, I figured out the answer. What I did was this:

    IBeaconFilter filter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            setContentView(R.layout.activity_scanner);
            distanceTV = findViewById(R.id.distanceText);
            fillBeaconList(listOfHardcodedBeacons);
            randint = random.nextInt(listOfHardcodedBeacons.size());
            filter = new IBeaconUniqueIdFilter(listOfHardcodedBeacons.get(randint).getUniqueId());
    
            setupProximityManager();
    
            super.onCreate(savedInstanceState);
        }
    
    private void setupProximityManager(){
            proximityManager = ProximityManagerFactory.create(this);
    
            //configure proximity manager basic options
            proximityManager.configuration()
                    //using ranging for continuous scanning or MONITORING for scanning with intervals
                    .scanPeriod(ScanPeriod.RANGING)
                    //using BALANCED for best performance/battery ratio
                    .scanMode(ScanMode.LOW_LATENCY)
                    //OnDeviceUpdate callback will be received with 1 second interval
                    .deviceUpdateCallbackInterval(TimeUnit.MILLISECONDS.toMillis(20));
    
            //setting up iBeacon and Eddystone spaces listeners
            //proximityManager.setSpaceListener(createSpaceListener());
    
            //setting up iBeaconListener to only listen for random beacon (filter)
            proximityManager.filters().iBeaconFilter(filter);
            proximityManager.setIBeaconListener(createIBeaconListener());
            proximityManager.setEddystoneListener(new SimpleEddystoneListener() {
            });
    
        }
    

    This let me keep my existing (commented) code in onIBeaconsUpdated without any messy lists. This solution takes a random int that is created on Activity start, and then uses that int as the index for the list of hardcoded beacons, which is then used as the uniqueID for the filter. When the Proximity Manager starts scanning, it will only detect beacons of that uniqueID. It was a solution I haven't seen anywhere else, so I am posting here for posterity.