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 using “MM/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
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
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
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
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 |
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
toLocation
- 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
toTicketPreview
in order to support DTC
V2.5.4
- Resolved issue with
Seller.logo
V2.5.3
- Added
LocationEmbedded
toTapToInfo
to retrieveSeller
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
inQuoteService
- Added
Fee
to bookingsPreview
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 theEvent
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 useFeeType
enum - Added
Validation
toPurchaseOption
to check if license plate is required - Added
CreatePaymentMethodRequestBody.isDefault
to allow setting payment method as default duringPaymentMethodService.createPaymentMethod(...)
- Updated
AccountService.resetPassword
to auto log user in if password is successfully reset - Updated Retrofit to
2.9.0
and OkHttp to4.7.2
v1.0.9
- Fixed proguard issue when using minify in builds
- Updated
SpaceAvailability.status
to an EnumAvailabilityStatus
- Updated
Availability.status
to an EnumAvailabilityStatus
- Updated
Display
properties to EnumDisplayStatus
- Added
GooglePayPayment
to handle getting nonce for Google Pay payment - Added
PayPalPayment
to handle getting nonce for PayPal payment
v1.0.8
- Added
isTimeTBD
toBooking
model - Added
sellerId
toQueryParam
option to allow searching quotes by seller id - Added
review
toBooking.embedded
which will only be present when user has created a review for that booking - Changed
addOnIds
from aString
type toList<String>
inBookingPreviewRequestBody
,BookingRequestBody
andOslTicketRequestBody
v1.0.7
- Added
Event
model toParkingPass.embedded
- Created
TicketChangeVehicleRequestBody
model forTicketsService.changeVehicle
- Created
BookingChangeVehicleRequestBody
model forBookingsService.changeVehicle
- Renamed
Event
returned in autocomplete toAutocompleteEvent
- Switched to use single
Event
model besides for autocomplete - Added
BookingShare
toBooking.embedded
to check whether a booking has been shared - Added
isShareManageable
toBooking
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 replacesisCancellable
withinPurchaseOption
- Added
CancellableStatus
property toPurchaseOption
andBooking
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 theLocation
model - Added
isLogged(): Flow<Boolean>
to theAuthenticatedTokenManager
which allows listening to the logged in state - Renamed
TicketService.validateTicket(...)
toTicketService.updateTicket(...)
which still allows validation but also now allows updating the payment method of an active ticket - Replaced
Location.onDemandOptions
withLocation.onDemandIntegrations
sinceonDemandOptions
are deprecated from the arrive api - Updated to use JDK 8 version of the Kotlin plugin dependency
v1.0.3
- Replaced
passValue
withfullPrice
inBooking
v1.0.2
- Added
passValue
toParkingPass.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
toBookingService.getBookings
- Replaced usages of
Proximity
class withLatLng
- Changed
QueryParam
class variables visibility to public - Changed
PublicToken
andToken
Models to match api directly using Unix Timestamp (seconds) rather than using milliseconds - Changed
expiration
variable inPublicTokenManager
andAuthenticatedTokenManager
toexpirationInSeconds
for clarity - Added
webUrl
variable toParkingPass
model
v1.0.0
- Initial Release