Implementation of scanning in F-Droid build variant of Open Event Organizer Android App

Implementation of scanning in F-Droid build variant of Open Event Organizer Android App

Open Event Organizer App (Eventyay Organizer App) is the Android app used by event organizers to create and manage events on the Eventyay platform.

Various features include:

  1. Event creation.
  2. Ticket management.
  3. Attendee list with ticket details.
  4. Scanning of participants etc.

The Play Store build variant of the app uses Google Vision API for scanning attendees. This cannot be used in the F-Droid build variant since F-Droid requires all the libraries used in the project to be open source. Thus, we’ll be using this library: https://github.com/blikoon/QRCodeScanner 

We’ll start by creating separate ScanQRActivity, ScanQRView and activity_scan_qr.xml files for the F-Droid variant. We’ll be using a common ViewModel for the F-Droid and Play Store build variants.

Let’s start with requesting the user for camera permission so that the mobile camera can be used for scanning QR codes.

public void onCameraLoaded() {
    if (hasCameraPermission()) {
        startScan();
    } else {
        requestCameraPermission();
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode != PERM_REQ_CODE)
            return;

    // If request is cancelled, the result arrays are empty.
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        cameraPermissionGranted(true);
    } else {
        cameraPermissionGranted(false);
    }
}



@Override
public boolean hasCameraPermission() {
    return ContextCompat.checkSelfPermission(this, permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
}

@Override
public void requestCameraPermission() {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERM_REQ_CODE);
}


@Override
public void showPermissionError(String error) {
    Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
}

public void cameraPermissionGranted(boolean granted) {
    if (granted) {
        startScan();
    } else {
        showProgress(false);
        showPermissionError("User denied permission");
    }
}

After the camera permission is granted, or if the camera permission is already granted, then the startScan() method would be called.

@Override
public void startScan() {
    Intent i = new Intent(ScanQRActivity.this, QrCodeActivity.class);
    startActivityForResult(i, REQUEST_CODE_QR_SCAN);
}

QrCodeActivity belongs to the library that we are using.

Now, the processing of barcode would be started after it is scanned. The processBarcode() method in ScanQRViewModel would be called.

public void onActivityResult(int requestCode, int resultCode, Intent intent) {

    if (requestCode == REQUEST_CODE_QR_SCAN) {
        if (intent == null)
            return;

        scanQRViewModel.processBarcode(intent.getStringExtra
            ("com.blikoon.qrcodescanner.got_qr_scan_relult"));

    } else {
        super.onActivityResult(requestCode, resultCode, intent);
    }
}

Let’s move on to the processBarcode() method, which is the same as the Play Store variant.

public void processBarcode(String barcode) {

    Observable.fromIterable(attendees)
        .filter(attendee -> attendee.getOrder() != null)
        .filter(attendee -> (attendee.getOrder().getIdentifier() + "-" + attendee.getId()).equals(barcode))
        .compose(schedule())
        .toList()
        .subscribe(attendees -> {
            if (attendees.size() == 0) {
                message.setValue(R.string.invalid_ticket);
                tint.setValue(false);
            } else {
                checkAttendee(attendees.get(0));
            }
        });
}

The checkAttendee() method:

private void checkAttendee(Attendee attendee) {
    onScannedAttendeeLiveData.setValue(attendee);

    if (toValidate) {
        message.setValue(R.string.ticket_is_valid);
        tint.setValue(true);
        return;
    }

    boolean needsToggle = !(toCheckIn && attendee.isCheckedIn ||
        toCheckOut && !attendee.isCheckedIn);

    attendee.setChecking(true);
    showBarcodePanelLiveData.setValue(true);

    if (toCheckIn) {
        message.setValue(
            attendee.isCheckedIn ? R.string.already_checked_in : R.string.now_checked_in);
        tint.setValue(true);
        attendee.isCheckedIn = true;
    } else if (toCheckOut) {
        message.setValue(
            attendee.isCheckedIn ? R.string.now_checked_out : R.string.already_checked_out);
        tint.setValue(true);
        attendee.isCheckedIn = false;
    }

    if (needsToggle)
        compositeDisposable.add(
            attendeeRepository.scheduleToggle(attendee)
                .subscribe(() -> {
                    // Nothing to do
                }, Logger::logError));
}

This would toggle the check-in state of the attendee.

Resources:

Library used: QRCodeScanner

Pull Request: feat: Implement scanning in F-Droid build variant

Open Event Organizer App: Project repo, Play Store, F-Droid

Close Menu