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 usingMM/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

API reference

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

API reference

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

API reference

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

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? 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
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

API reference

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

API reference

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

API reference

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 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 April 22, 2020