Arrive Android SDK

Introduction

The Arrive Android SDK enables developers to find and book parking using the Arrive platform. Contained within are network requests and responses that can be used to access the Arrive API via native implementations as well as fully fleshed out model objects. Each SDK function provides access to the API without the overhead of building an integration from the ground up.

You will need an API key to use with the SDK. If you do not already have one, send an email to partner-support@arrive.com to request credentials. There will be different API keys that correspond to production and sandbox environments respectively. In order to use each environment, the proper key will need to match.

Setup

In order to import the SDK, add the arrive-android-sdk.appspot.com endpoint to the repositories in your top level build.gradle file. Set the required credentials.

allprojects {
    repositories {
        ...
        maven {
            credentials {
                username arriveUser
                password arrivePassword
            }
            url "https://arrive-android-sdk.appspot.com"
        }
    }
}

In your app build.gradle module, include the following dependency:

dependencies {
  ...
  implementation 'com.arrive.android:sdk:$arrive_version'
}

Initialize the Arrive instance from within your Application.onCreate() method:

override fun onCreate() {
  super.onCreate()

  val config = ArriveConfig.Builder(this,"API_KEY")
    .setEnvironment(Environment.SANDBOX || Environment.PRODUCTION)
    .setPaymentAuthorizationToken(...)
    .build()

  Arrive.initialize(config)
}

Token Management

Public Token

The public token is managed by the SDK so there should be no need to manually retrieve it. If you need to access the public token for any reason you can use the PublicTokenManager.

Authenticated Token

The authentication token is automatically stored and refreshed after a successful login api call is made. You can check the users logged in state:

val authenticatedTokenManager = Arrive.configuration.authenticatedTokenManager
val isLoggedIn = authenticatedTokenManager.isLoggedIn

Logout user:

val authenticatedTokenManager = Arrive.configuration.authenticatedTokenManager
authenticatedTokenManager.clear()

Observe logged in state via a couroutines Flow:

Arrive.configuration.authenticatedTokenManager.isLoggedInFlow.collect { isLoggedIn ->
    if (isLoggedIn) {
    ...
    } else {
    ...
    }
}

Payments

Make sure you have set the PaymentAuthorizationToken when initializing the Arrive SDK.

PayPal & Google Pay Setup

AndroidManifest.xml

<activity
    android:name="com.arrive.android.sdk.payments.PaymentActivity"
    android:exported="true">

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="${applicationId}.braintree" />
    </intent-filter>
</activity>

If you plan on using Google Pay in app you will need to add Google Play Services Wallet to your build.gradle.

dependencies {
  ...
  implementation 'com.google.android.gms:play-services-wallet:18.1.3'
}

Requesting Google Pay Nonce

CoroutineScope().launch {
    Arrive.paymentMethodService.requestGooglePayNonce(FragmentActivity, "USD", "5.00").collect { nonce ->
        when (nonce) {
            is GooglePayNonce.Loading -> {...}
            is GooglePayNonce.Created -> {...}
            is GooglePayNonce.Canceled -> {...}
            is GooglePayNonce.Error -> {...}
        }
    }
}

If successful, GooglePayNonce.Created is returned which includes the nonce (The payment token) and the users Google Pay Email (Needed for guest checkout). GooglePayNonce.Canceled is returned if the user cancels during the Google Pay flow and GooglePayNonce.Error is returned if there is an error with getting the payment nonce.

Requesting PayPay Nonce

CoroutineScope().launch {
    Arrive.paymentMethodService.requestPayPalForPayment(FragmentActivity, "USD", "5.00", locationName).collect { nonce ->
        when (nonce) {
            is PayPalNonce.Loading -> {...}
            is PayPalNonce.Created -> {...}
            is PayPalNonce.Canceled -> {...}
            is PayPalNonce.Error -> {...}
        }
    }
}

If successful, PayPalNonce.Created is returned which includes the nonce (The payment token). PayPalNonce.Canceled is returned if the user cancels during the PayPay flow and PayPalNonce.Error is returned if there is an error with getting the payment nonce.

Credit Card Payment

CoroutineScope().launch {
    val response = Arrive.paymentMethodService.createPaymentMethod(
        cardNumber = ...,
        cvv = ...,
        expirationDate = ..., // Should be formatted usingMM/YY”
        postalCode = ...,
        label = ... // User can give a name to this card
        )
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val paymentMethod = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
         }
    }
}

Rate Tiers

The SDK lets you retrieve multiple quotes for a single location, representing different rate tiers that are available to the user. Three rate tiers are retrieved at most, and the maximum duration of each rate tier is 24 hours.

In order to get different rate tiers, you should first get the initial quote using any of the relevant methods in QuoteService, and then call QuoteService.getAssociatedRateBuckets(quote: Quote) where quote is the initial quote. This method returns a list of 1-3 rate buckets, which are essentially a quote and a pricing object associated with it, with the initial quote always being the first item in that list.

Get rate tiers with Coroutines

CoroutineScope(Dispatchers.IO).launch {
    val initialQuote = ... //See 'Get Quotes' section for details on how to retrieve the initial quote
    val response = Arrive.quoteService.getAssociatedRateBuckets(initialQuote)
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val rateBuckets: List<RateBucket> = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
        }
    }
}

Samples

Get Locations

API reference

val latitude = ...  
val longitude = ...  

val query = QueryParam.Builder()  
    .setCoordinates(latitude, longitude)  
    .build()

CoroutineScope(Dispatchers.IO).launch {
    val response = Arrive.locationsService.getLocations(query)
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val locations = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
         }
    }
}

Add a Vehicle

API reference

CoroutineScope(Dispatchers.IO).launch {
    val response = Arrive.vehicleService.addVehicle(
        licensePlateNumber = "1B5 2R9",
        label = "The pickup truck",
        isDefault = true,
        licensePlateState ="CA")
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val addedVehicle = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
         }
    }
}

Get Quotes

API reference

val latitude = ...  
val longitude = ...  

val query = QueryParam.Builder()  
    .setCoordinates(latitude, longitude)  
    .build()

CoroutineScope(Dispatchers.IO).launch {
    val response = Arrive.quoteService.getQuotes(
        query = query,
        startTime = startTime,
        endTime = endTime
    )
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val quotes = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
         }
    }
}

Book Parking

API reference

BookingRequestBody

Parameter Type Description
quoteId String Selected PurchaseOption id
finalPrice String Purchase price that comes from Bookings.getPreview api call
paymentMethodNonce String? One-time use payment method ex. Google Pay nonce
savedPaymentToken String? PaymentMethod id from previously saved payment method
addOnIds List<String>? List of AddOn ids to purchase with quote
couponCode String? Valid Coupon Code from user
autoApplyCoupon Boolean Whether to automatically apply coupons attached to the customer
plateNumber String? Adds a license plate to the booking
vehicleId Long? Adds license plate of the vehicle that corresponds to the Vehicle id
email String? Required if user is not logged in
val body = BookingRequestBody(...)

CoroutineScope(Dispatchers.IO).launch {
    val response = Arrive.bookingService.bookParking(body)
    withContext(Dispatchers.Main) {
        when (response) {
            is NetworkResponse.Success -> {
                val booking = response.body
            }
            is NetworkResponse.ServerError -> {...}
            is NetworkResponse.NetworkError -> {...}
         }
    }
}

BLE Reservations

The BLE extension allows you to open BLE gates for reservations.

Setup

To add this extension add the gradle dependency to your build.gradle. (If you are adding both the base sdk and this extension, make sure you are using matching versions of both.)

dependencies {
  ...
  implementation 'com.arrive.android:sdk-ext-ble:2.1.1'
}

When configuring the SDK, you will need to add the BLE extension to the builder.

.addExtension(ArriveBleExtension(context, IntegrationType.BLE_FLASH))

Accessing the ReservationBleManager

The ReservationBleManager is contained within the ArriveBleExtension class. In order to open gates with a Booking you will need to access the ReservationBleManager.

You can save your instance of the ArriveBleExtension or retreive it via Arrive.getExtension<ArriveBleExtension>(). Then, you can call ArriveBleExtension.reservationBleManager to get the ReservationBleManager

Opening an entry gate with a reservation

reservationBleManager.openEntranceGate( `Booking)

Opening an exit gate with a reservation

reservationBleManager.openExitGate( `Booking)

Flash SDK

Introduction

The Flash SDK enables developers to register and check users Flash Monthly accounts. You will need user credentials in order to access the SDK. If you do not already have them, send an email to partner-support@arrive.com to request credentials.

Setup

In your app build.gradle module, include the following dependency:

dependencies {
  ...
  implementation ''com.flashparking:flash-access:1.0.1''
}

Register Monthly User

This will start the device registration process for Flash Monthly using FlashParkingManager. The user can only register the phone number associated with their monthly account.

FlashParkingMananger.registerDevice(context: Context)

Returns Monthly Customer Status

Checks to see if the user is associated with a Flash Monthly account using FlashParkingManager

FlashParkingManager.getMonthlyCustomer(UUID loginToken, final UUID locationId, FlashAsyncEvents eventHandler)

If the customer is registered in the system and has at least one monthly parking location associated with their account it will return true. If the user has no monthly account or it fails it will throw an exception.

Returns Customer Information

Asynchronously returns a FlashMobileCustomer

FlashParkingMananger.getCustomerAsync(UUID loginToken, final UUID locationId, final FlashAsyncEvents eventHandler)

Sample code

FlashParkingManager.getCustomerAsync(loginToken, locationId, new FlashAsyncEvents() {
  @Override
  public void FlashAsyncStartedRequest() {
  }

  @Override
  public void FlashAsyncFinished(String methodName, Object Data) {
    FlashMobileCustomer customer =(FlashMobileCustomer) Data;
  }

  @Override
  public void FlashAsyncFinishedWithException(Exception ex) {
  }

  @Override
  public void FlashAsyncEndedRequest() {
  }
});

Open Gate

First the device and customer must be registered with a flash monthly account. To open a gate within bluetooth range use class FlashGateMananger and call checkInOutMonthly() passing in the customer id. Make sure both the device location permission and bluetooth are enabled.

FlashGateMananger.checkInOutMonthly(UUID customerId)

This call Validates the provided customer ID and vends the entry/exit gate. Below is a sample.

mFlashGateManager = new FlashGateManager();
    
mFlashGateManager.initialize(this, 1, 1, true, true, true);
    
SharedPreferences sharedPref = mContext.getSharedPreferences("com.flashparking.example.customerId", Context.MODE_PRIVATE);
String customerId = sharedPref.getString("customerId", null);

if(customerId != null)
    mFlashGateManager.checkInOutMonthly(UUID.fromString(customerId));
     

Creating QR code

Koisks allow for flash monthly customers to scan QR codes. You can use glide or an image libary of your choice in order to create monthly qr codes. Here is a sample of how to create a monthly QR code using the flash customer id.

scanCodeImageView.loadScanCode(BarcodeFormat.QR_CODE, "FVUN${customerId.replace("-", "")}")
fun loadScanCode(format: ScanCodeFormat, data: String, qrErrorCorrectionLevel: String? = null) {
    val barcode = createBarcode(data, format, qrErrorCorrectionLevel)

    if (!hasSetPadding()) setPadding(scanCodePadding)

    layoutParams.height = barcode.height + paddingTop + paddingBottom
    layoutParams.width = barcode.width + paddingStart + paddingEnd
    layoutParams = layoutParams

    GlideAppWrapper(context)
        .load(barcode)
        .into(view)
}
private fun createGlideBarcode(data: String, format: BarcodeFormat, qrErrorCorrectionLevel: String? = null): GlideBarcode {
    val scanCodeHeight: Int
    val scanCodeWidth: Int

    when (format) {
        BarcodeFormat.QR_CODE -> {
            scanCodeWidth = 128dp
            scanCodeHeight = 128dp
        }
      else -> throw IllegalStateException("No sizes available for format: $format")
    }
    return GlideBarcode(data, format, scanCodeWidth, scanCodeHeight, qrErrorCorrectionLevel)
}

Changelog

V2.5.15

  • Updated sdk to target android 31
  • Added operatingHours to Location
  • Removed BleVendIngeration support
  • Added VenueAndEventService.getVenueEvents(...) to support pagination

V2.5.14

  • Added support for retrieving Tickets from the BookingService
  • Added additional parameters to TicketEndpoints

V2.5.13

  • Added fees and coupons to TicketPreviewEndpoint

V2.5.12

  • Added support for RecommendationsAndQuotes

V2.5.11

  • Added nullable parameters to TicketPreview

V2.5.10

  • Added additonal paramters to TicketService.getSpgPreview(..)

V2.5.9

  • Added TapToPayService to SDK
  • Added additonal paramters to TicketRequestBody

V2.5.8

  • Added additonal parameters to TicketService

V2.5.7

  • Added additonal parameters to TicketPreview

V2.5.6

  • Added externalTicketId to TicketPreview in order to support DTC

V2.5.4

  • Resolved issue with Seller.logo

V2.5.3

  • Added LocationEmbedded to TapToInfo to retrieve Seller

V2.5.2

  • Added BookingService.bookingShare(...) to get shared bookings
  • Changed CreditCardTokenizer.tokenize(...) to public to allow purchases
  • Fixed flash java 8 issue
  • Relocated Flash SDK code
  • Updated Flash SDK to support checking for customers.monthlyLocations

V2.3.3

  • Resolved issue with InternalGooglePayClient
  • Resolved issue with Location.state

V2.3.2

  • Resolved crash with InternalGooglePayClient

V2.3.1

  • Added adjustable rate bucket end times to QuoteService.getAssociatedRateBuckets
  • Updated to gradle 7.0.2
  • Updated to kotlin version 1.5.31
  • Fixed 24 hour case for endTime in QuoteService
  • Added Fee to bookings Preview

V2.1.1

  • Updated to Braintree v4

V2.1.0

  • Updated to Braintree 4.6.0
  • Moved all payment handling to the PaymentMethodService

V2.0.0

  • Updated to Kotlin 1.5.20
  • Refactored and simplfied data models
  • Added the ability to extend the base SDK.
  • Added support for BLE Reservations via the BLE extension
  • Removed support for RxJava - If RxJava support is required, you may utilize the wrapper library maintained by the Kotlin team.

v1.1.0

  • Added QuoteService.getOnStreetQuotes to query for on street parking information
  • Updated to Kotlin 1.4.30
  • Added Availability to the Event model

v1.0.14

  • QuoteService.getAssociatedRateBuckets(quote: Quote) now includes the original quote in the returned list.

v1.0.13

  • Added QuoteService.getAssociatedRateBuckets(Quote)

v1.0.12

  • Added GooglePayPayment.isReadyToPay

v1.0.11

  • Guest user is now auto logged in if they are purchasing a booking through guest checkout with a new account email

v1.0.10

  • Changed Fee.type to use FeeType enum
  • Added Validation to PurchaseOption to check if license plate is required
  • Added CreatePaymentMethodRequestBody.isDefault to allow setting payment method as default during PaymentMethodService.createPaymentMethod(...)
  • Updated AccountService.resetPassword to auto log user in if password is successfully reset
  • Updated Retrofit to 2.9.0 and OkHttp to 4.7.2

v1.0.9

  • Fixed proguard issue when using minify in builds
  • Updated SpaceAvailability.status to an Enum AvailabilityStatus
  • Updated Availability.status to an Enum AvailabilityStatus
  • Updated Display properties to Enum DisplayStatus
  • Added GooglePayPayment to handle getting nonce for Google Pay payment
  • Added PayPalPayment to handle getting nonce for PayPal payment

v1.0.8

  • Added isTimeTBD to Booking model
  • Added sellerId to QueryParam option to allow searching quotes by seller id
  • Added review to Booking.embedded which will only be present when user has created a review for that booking
  • Changed addOnIds from a String type to List<String> in BookingPreviewRequestBody, BookingRequestBody and OslTicketRequestBody

v1.0.7

  • Added Event model to ParkingPass.embedded
  • Created TicketChangeVehicleRequestBody model for TicketsService.changeVehicle
  • Created BookingChangeVehicleRequestBody model for BookingsService.changeVehicle
  • Renamed Event returned in autocomplete to AutocompleteEvent
  • Switched to use single Event model besides for autocomplete
  • Added BookingShare to Booking.embedded to check whether a booking has been shared
  • Added isShareManageable to Booking to check if a booking can be shared

v1.0.6

  • Added LocationService.createReview(...) to create review for a location
  • Added LocationService.getReviews(...) to get all reviews for a specified location
  • Added CancellableStatus Model which replaces isCancellable within PurchaseOption
  • Added CancellableStatus property to PurchaseOption and Booking Models

v1.0.5

  • Updated Retrofit and Moshi libraries
  • Added new class CreditCardTokenization to handle tokenizing user payment methods
  • Updated PaymentMethodService.createPaymentMethod to handle card tokenization and creating payment method through the arrive api

v1.0.4

  • Added AccountService.getIntegrationTokens() to load tokens for ble
  • Added AccountService.openInugoGate(...) which is used to open ble gates via api call
  • Added onDemandDisclaimers to the Location model
  • Added isLogged(): Flow<Boolean> to the AuthenticatedTokenManager which allows listening to the logged in state
  • Renamed TicketService.validateTicket(...) to TicketService.updateTicket(...) which still allows validation but also now allows updating the payment method of an active ticket
  • Replaced Location.onDemandOptions with Location.onDemandIntegrations since onDemandOptions are deprecated from the arrive api
  • Updated to use JDK 8 version of the Kotlin plugin dependency

v1.0.3

  • Replaced passValue with fullPrice in Booking

v1.0.2

  • Added passValue to ParkingPass.validation.display object to determine if passes price should be displayed on the parking pass

v1.0.1

  • Added Token validation check during initialization (Will auto refresh public tokens or will clear authenticated token if no longer valid)
  • Added new optional parameters BookingsQueryParam, Sort to BookingService.getBookings
  • Replaced usages of Proximity class with LatLng
  • Changed QueryParam class variables visibility to public
  • Changed PublicToken and Token Models to match api directly using Unix Timestamp (seconds) rather than using milliseconds
  • Changed expiration variable in PublicTokenManager and AuthenticatedTokenManager to expirationInSeconds for clarity
  • Added webUrl variable to ParkingPass model

v1.0.0

  • Initial Release
Last updated March 15, 2021