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:api:1.0.9') {
transitive=true
}
}
By default we use Kotlin coroutines for the api calls. If you would prefer to use RxJava, add the following to your app build.gradle
aswell:
dependencies {
...
implementation ('com.arrive.android:api-rx:1.0.9') {
transitive=true
}
}
Initialize the Arrive instance from within your Application.onCreate() method:
Kotlin
override fun onCreate() {
super.onCreate()
val config = ArriveConfig.Builder(this,"API_KEY")
.setEnvironment(Environment.SANDBOX || Environment.PRODUCTION)
.setPaymentAuthorizationToken(...)
.build()
Arrive.init(config)
}
Java
@Override public void onCreate() {
super.onCreate();
ArriveConfig config = new ArriveConfig.Builder(this,"API_KEY")
.setEnvironment(Environment.SANDBOX || Environment.PRODUCTION)
.setPaymentAuthorizationToken(...)
.build();
Arrive.init( 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.getInstance().configuration.authenticatedTokenManager
val isLoggedIn = authenticatedTokenManager.isLoggedIn
Logout user:
val authenticatedTokenManager = Arrive.getInstance().configuration.authenticatedTokenManager
authenticatedTokenManager.clear()
Google Pay Setup
Setup
Make sure you have set the PaymentAuthorizationToken
when initializing the Arrive SDK. 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:<version>'
}
In your AndroidManifest.xml
include the google wallet meta-data tag.
<meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true"/>
Requesting Google Pay Nonce
val googlePayPayment = GooglePayPayment.newInstance(fragment)
CoroutineScope(Dispatchers.IO).launch {
val googlePaytNonce = googlePayPayment.requestOneTimePayment("USD", "5.00")
withContext(Dispatchers.Main) {
when (googlePaytNonce) {
is GooglePayNonce.Created -> {
val nonce = googlePaytNonce.nonce
val email = googlePaytNonce.email
}
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.
Payment Methods
In order to be able to add new payment methods within the SDK you will need a payment authorization token for production
and sandbox
environments. This will need to be set using the ArriveConfig.Builder
when initializing the library.
Add Payment Method
val creditCardTokenizer = CreditCardTokenizer.newInstance(Fragment || AppCompatActivity)
val body = CreatePaymentMethodRequestBody(
cardNumber = ...,
cvv = ...,
expirationDate = ..., // Should be formatted using “MM/YY”
postalCode = ...,
label = ...
)
CoroutineScope(Dispatchers.IO).launch {
val response = ArriveApiClient.getInstance().paymentMethodService.createPaymentMethod(creditCardTokenizer, body)
withContext(Dispatchers.Main) {
when (response) {
is NetworkResponse.Success -> {
val paymentMethod = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
}
Kotlin Sample Calls
Get Locations
Coroutines
val latitude = ...
val longitude = ...
val query = QueryParam.Builder()
.setCoordinates(latitude, longitude)
.build()
CoroutineScope(Dispatchers.IO).launch {
val response = ArriveApiClient.getInstance().locationsService.getLocations(query)
withContext(Dispatchers.Main) {
when (response) {
is NetworkResponse.Success -> {
val locations = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
}
RxJava
val latitude = ...
val longitude = ...
val query = QueryParam.Builder()
.setCoordinates(latitude, longitude)
.build()
ArriveRxApiClient.getInstance().locationsService.getLocations(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
when (response) {
is NetworkResponse.Success -> {
val locations = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
Add a Vehicle
Coroutines
val requestBody = VehicleRequestBody(
licensePlateNumber = "1B5 2R9",
label = "The pickup truck",
isDefault = true,
licensePlateState ="CA"
)
CoroutineScope(Dispatchers.IO).launch {
val response = ArriveApiClient.getInstance().vehicleService.addVehicle(requestBody)
withContext(Dispatchers.Main) {
when (response) {
is NetworkResponse.Success -> {
val addedVehicle = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
}
RxJava
val requestBody = VehicleRequestBody(
licensePlateNumber = "1B5 2R9",
label = "The pickup truck",
isDefault = true,
licensePlateState ="CA"
)
ArriveRxApiClient.getInstance().vehicleService.addVehicle(requestBody)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
when (response) {
is NetworkResponse.Success -> {
val addedVehicle = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
Get Quotes
Coroutines
val latitude = ...
val longitude = ...
val query = QueryParam.Builder()
.setCoordinates(latitude, longitude)
.build()
CoroutineScope(Dispatchers.IO).launch {
val response = ArriveApiClient.getInstance().quoteService.getQuotes(
query = query,
startTime = startTime,
endTime = endTime
)
withContext(Dispatchers.Main) {
when (response) {
is NetworkResponse.Success -> {
val addedVehicle = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
}
RxJava
val latitude = ...
val longitude = ...
val query = QueryParam.Builder()
.setCoordinates(latitude, longitude)
.build()
ArriveRxApiClient.getInstance().quoteService.getQuotes(
query = query,
startTime = startTime,
endTime = endTime
).subscribe {
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 |
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 |
Coroutines
val body = BookingRequestBody(...)
CoroutineScope(Dispatchers.IO).launch {
val response = ArriveApiClient.getInstance().bookingService.bookParking(body)
withContext(Dispatchers.Main) {
when (response) {
is NetworkResponse.Success -> {
val booking = response.body
}
is NetworkResponse.ServerError -> {...}
is NetworkResponse.NetworkError -> {...}
}
}
}
Java Sample Calls
Get Locations
QueryParam query = new QueryParam.Builder()
.setCoordinates(..., ...)
.setDistance(1)
.build();
ArriveRxApiClient.getInstance()
.getLocationsService()
.getLocations(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<NetworkResponse<List<Location>, ApiError>>() {
@Override
public void onNext(NetworkResponse<List<Location>, ApiError> networkResponse) {
if (networkResponse instanceof NetworkResponse.Success) {
List<Location> locations = ((NetworkResponse.Success<List<Location>>) networkResponse).getBody();
} else if (networkResponse instanceof NetworkResponse.ServerError) {
...
} else if (networkResponse instanceof NetworkResponse.NetworkError) {
...
}
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
Add a Vehicle
VehicleRequestBody requestBody = new VehicleRequestBody(
"1B5 2R9",
"The Pickup Truck",
true,
"CA"
);
ArriveRxApiClient.getInstance()
.getVehicleService()
.addVehicle(requestBody)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<NetworkResponse<Vehicle, ApiError>>() {
@Override
public void onNext(NetworkResponse<Vehicle, ApiError> networkResponse) {
if (networkResponse instanceof NetworkResponse.Success) {
Vehicle addedVehicle = ((NetworkResponse.Success<Vehicle>) networkResponse).getBody();
} else if (networkResponse instanceof NetworkResponse.ServerError) {
...
} else if (networkResponse instanceof NetworkResponse.NetworkError) {
...
}
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
Get Quotes
QueryParam queryParam = new QueryParam.Builder()
.setCoordinates(..., ...)
.build();
ArriveRxApiClient.getInstance().getQuoteService()
.getQuotes(query, startTime, endTime, null, null, userEmail)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<NetworkResponse<List<Quote>, ApiError>>() {
@Override
public void onNext(NetworkResponse<List<Quote>, ApiError> networkResponse) {
if (networkResponse instanceof NetworkResponse.Success) {
List<Quote> quotes = ((NetworkResponse.Success<List<Quote>>) networkResponse).getBody();
} else if (networkResponse instanceof NetworkResponse.ServerError) {
...
} else if (networkResponse instanceof NetworkResponse.NetworkError) {
...
}
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
Changelog
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