# Android Integration

Getting Started
Push Notification
AnybillCore
AnybillAuth
AnybillBill
AnybillCategory
AnybillLog
AnybillOCR
AnybillWarranty
AnybillTree
AnybillDocsManager
AnybillUBA
AnybillExpenses
AnybillUI

# Getting Started

# Resolving the SDK using gradle:

The anybill SDK for android is hosted in a maven repository. To resolve the sdk using gradle, follow these steps:

  1. Add this to your project's 'build.gradle'. You can find your credentials in the provided integration documents.
allprojects {
    repositories {
        mavenCentral()
        maven {
            url "https://anybill.jfrog.io/artifactory/anybill_android_sdk"
            credentials {
                username = {username}
                password = {password}
            }
        }
    }
}
  1. Add the desired anybill modules to your module's 'build.gradle'. The following modules are required for the basic usage of the SDK: core, auth, bill, log, category
dependencies {
    implementation 'de.anybill.anybill_android_sdk:core:{latest version}'
    implementation 'de.anybill.anybill_android_sdk:log:{latest version}'
    implementation 'de.anybill.anybill_android_sdk:auth:{latest version}'
    implementation 'de.anybill.anybill_android_sdk:bill:{latest version}'
    implementation 'de.anybill.anybill_android_sdk:category:{latest version}'
}

# Setting the client Id

Within the provided integration documents you are going find a client ID. To set the Client ID in your app, add the ID as meta data value anybill_client_id in your app's manifest file. This is going to allow us to hook all of your API activity to your Client Id which can be used for analytics or support purposes later on.

<application
...
>
    ...
    <meta-data android:name="anybill_client_id" android:value="{your_client_id}"/>
    ...
</application>

# Usage of the SDK

# Error Handling

The anybill sdk uses a custom error handling model. All methods return a type of the sealed class ApiResult including the return object on success and/or information about the error which occurred. Detailed description of the possible error codes can be found in the corresponding documentation of the methods.

sealed class ApiResult<out T> {

    /**
     * Success\
     * Used when Api Call and serialization was successful\
     *
     * @param T Type [T] of the object which should be returned if Api call and JSON Serialization was successful
     * @property value Object of type [T] holding the serialized result of the api call
     */

    data class Success<out T>(val value: T) : ApiResult<T>()

    /**
     * Generic error\
     * Used if a generic error occurs during execution of the api call or serialization of the received data\
     * Possible GenericError's of each operation are listed in the method documentation
     *
     * @property code Nullable [Int] with the Error Code of the exception
     * @property error Nullable [ErrorResponse] object with further exception information
     */

    data class GenericError(val code: Int? = null, val error: ErrorResponse? = null) : ApiResult<Nothing>()

    /**
     * Empty success\
     * Used if the execution of the api call and the serialization of the result was successful without a need of a result object (e.g. token operations)\
     *
     */

    object EmptySuccess : ApiResult<Nothing>()

    /**
     * Network error\
     * Used if a network error occurs during execution of the api call (e.g. timeout, no network)
     */

    object NetworkError : ApiResult<Nothing>()
}

Example usage of the error model based on the user info method of the AuthProvider.

fun getUserInfo() {
        viewModelScope.launch {
            when (val userInfoTask = AuthProvider.getUserInfo()) {
                is NetworkError -> //Error Handling
                is GenericError -> {
                    when(userInfoTask.code){
                        NO_USER -> //Error Handling
                        NOT_SYNCABLE -> //Error Handling
                        SERVER_ERROR -> //Error Handling
                        REFRESH_ERROR -> //Error Handling
                    }       
                }
                is Success -> {
                    userInfoTask.value //Further implementation
                }
            }
        }
    }

Back to top

# Push Notification

The anybill sdk also provides the possibility to receive push notification from anybill's firebase project. Push notifications are used for reminders of users' warranties and certain login verifications. The usage of push notification is optional for the core modules of the anybill sdk. Features which require the usage of push notification are marked.

How to implement push notifications for the anybill sdk:

# Without an existing Firebase project

If the app is in background, push notification are handled by google services and displayed automatically. If the app is in foreground push notification are caught by the FirebaseMessagingService and have to be displayed manually. The anybill sdk provides an interface which can be implemented to your own notification service to display notifications:

  1. Create a notification handler:
class YourNotificationHandler(private val context: Context) : AnybillMessagingHandler {

    override fun onAnybillMessageReceived(remoteMessage: RemoteMessage) {
        // Display Notification
    }
    
}
  1. Initialize anybill notification service with your messaging handler in the onCreate() of your application.
class YourApplication : Application() {
        override fun onCreate() {
            super.onCreate()

            // Initialize the anybill Firebase project

            AnybillFirebaseInitializer.initFirebaseProject(this)

            // Set your notification handler

            AnybillMessagingService.setMessageHandler(YourNotificationHandler(this))

        }
    }

Notifications can be customized or extended with Intents to navigate different views.

# With an existing Firebase project

If your app already uses a Firebase project, the anybill Firebase project can be initialized as a secondary Firebase project. To ensure that the anybill project is not initialized as the default project, call the initializing method after your default project was initialized. The default Firebase project gets initialized by the FirebaseInitProvider. To enable a manual initialization of the Firebase project, disable the provider by adding this to your app's manifest:

<application>
    ...
    <provider
        android:name="com.google.firebase.provider.FirebaseInitProvider"
        android:authorities="${applicationId}.firebaseinitprovider"
        tools:node="remove"
    />
    ...
</application>

The default project can now be initialized using FirebaseApp.initializeApp() in the onCreate() method of your application:


    class YourApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            
            // Initialize your default Firebase project

            FirebaseApp.initializeApp(this)

            // Initialize the anybill Firebase project

            AnybillFirebaseInitializer.initFirebaseProject(this)
        }
    }

Implementation of notifications for a secondary project differs if the default project also uses Firebase Messaging:

# Default project does not use Firebase Messaging

If the default project does not use Firebase Messaging, displaying notification works as described in the section Without an existing Firebase project. To make sure that the anybill project is not initialized as the default project, initialize your Firebase project before initializing the anybill Firebase project (see step 2). The anybill sdk provides an interface which can be implemented to your own notification service to display notifications:

  1. Create an notification handler:
class YourNotificationHandler(private val context: Context) : AnybillMessagingHandler {

    override fun onAnybillMessageReceived(message: RemoteMessage) {
        // Display Notification
    }
    
}
  1. Initialize anybill notification service with your messaging handler in the onCreate() of your application.
class YourApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            
            // Initialize your default Firebase project

            FirebaseApp.initializeApp(this)

            // Initialize the anybill Firebase project

            AnybillFirebaseInitializer.initFirebaseProject(this)

            // Set your notification handler

            AnybillMessagingService.setMessageHandler(YourNotificationHandler(this))

        }
    }

# Default project uses Firebase Messaging

Firebase only allows the usage of one FirebaseMessagingService. To allow the anybill sdk to execute operation on specific notifications the notification you receive in your notification service have to be given to the anybill sdk.

  1. Sample code of your notification service:
class YourNotificationService : FirebaseMessagingService(), AnybillMessagingHandler {

    //Initialize an instance of AnybillMessagingService

    private val anybillMessagingService by lazy {
        AnybillMessagingService.getInstance(this)
    }

    //In the onMessageReceived of your FirebaseMessagingService:
    //Check if a notification should be handled by the anybill sdk by calling isAnybillMessage()
    //Let anybill messaging service handle the notification

    override fun onMessageReceived(message: RemoteMessage) {
        if (message.isAnybillMessage()) {
            anybillMessagingService.onAnybillMessageReceived(message)
        } else {
            //Display Notification
        }
        super.onMessageReceived(message)
    }

    //Firebase Messaging Tokens mostly get reset for both projects simultaneously.
    //To reset the notification token of the logged in anybill user, call onNewAnybillToken in your onNewToken method

    override fun onNewToken(token: String) {
        anybillMessagingService.onNewAnybillToken()
        super.onNewToken(token)
    }

    //Display the received anybill notification after it was handled by the anybill sdk.

    override fun onAnybillMessageReceived(messageBody: RemoteMessage) {
        //Display Notification
    }
}

Back to top

# AnybillCore

The AnybillCore module contains several constants and functionalities which are shared between all anybill modules. Constants and methods of the Core Module should not be accessed from the app module.

# AnybillAuth

The Auth module contains authentication functions used in the anybill sdk. Most authentication functions can be accessed using the Singleton AuthProvider. Most functions of the AuthProvider are suspend functions and have to be called in a coroutine scope.

# Login

To login a user you can choose between logging in an anybill user, an anonymous user or use your own authentication token (token user).

Anybill user

Anybill users can be logged in using the loginUser() method of the AuthProvider. It requires valid login information of a registered anybill user (email and password).

    fun loginUser(email: String, password: String) {
        viewModelScope.launch {
           when (val loginCall = AuthProvider.loginUser(email = email, password = password)){
               is ApiResult.EmptySuccess -> //Success
               is ApiResult.GenericError -> //Error Handling
               is ApiResult.NetworkError -> //Error Handling
           }             
        }
    }

Anonymous user

By using this functionality the SDK creates an anonymous user account in the background with no email or password. With this authentication method the user can use the anybill services (with certain restrictions) as if he had no user account. The anonymous user account can be converted to a normal anybill account with email and password later on.

    fun loginAnonymousUser() {
        when (val createAnonymousCall = AuthProvider.createAnonymousUser()){
               is ApiResult.EmptySuccess -> //Success
               is ApiResult.GenericError -> //Error Handling
               is ApiResult.NetworkError -> //Error Handling
           }
    }

After an App or its data gets deleted, the anonymous account cannot be restored because there is no reference to the old account left.

Linked token user

If your App has an own user account system you can link an anybill user to your user account by using the linked token user:

  • Get a token from the anybill Partner API by linking you account system to an anybill id.
  • Create an instance of TokenUser with the received token-information
  • Login the TokenUser with the anybill sdk
    fun loginTokenUser(tokenUser: TokenUser){
        when(val tokenLogin = AuthProvider.loginTokenUser(tokenUser)){
            is ApiResult.EmptySuccess -> //Success
            is ApiResult.GenericError -> //Error Handling
        }
    }

When using the token user variant you'll have to check for a failing Refresh Token Call on every API call you invoke. When the error is triggered you'll have to retrieve new authentication information from the anybill Partner API.


when(val userInfoCall = AuthProvider.getUserInfo()){
    is ApiResult.Success -> //Success
    is ApiResult.GenericError -> {
        when (userInfoCall.code) {
            AuthErrors.REFRESH_ERROR -> {
                // Retrieve new token information from anybill Partner API
            }
        }
    }
    is ApiResult.NetworkError -> // Error Handling
}

Further information about the login possibilities can be found on the anybill sdk authentication page..

# Register

To register a new anybill user use the preRegisterUser() method, which sends a validation code to the given user email. You can validate a register code with the validateRegistrationCode() method. Use the registerUser() method to complete the registration process. By setting autoLogin to true the user is going to be automatically logged in after a successful registration.

    viewModelScope.launch {
        when(val preRegisterCall = anybillAuth.preRegisterUser(email, firstname)){
            is EmptySuccess -> //Successfully sent code to email
            is GenericError -> //Error Handling
            is NetworkError -> //Error Handling
        }
    }

    //Let user type in registration code

    viewModelScope.launch{
        when(val registerCall = anybillAuth.registerUser(email,password, code)){
            is Success -> //Success
            is GenericError -> //Error Handling
            is NetworkError -> //Error Handling
        }
    }

# Deprecated: Register

To register a new anybill user use the registerUser() method. It requires a valid email, a password which fulfills anybill's password policy (at least 8 characters with 1 lowercase, 1 capital letters and 1 number) and the first name of the user. Other information of the user is optional. By setting autoLogin to true the user is going to be automatically logged in after a successful registration (default = false).

    fun registerUser(email: String, password: String, firstName: String) {
        viewModelScope.launch {
            val anybillUser = AuthProvider.registerUser(email, password, firstName, autoLogin = true)
        }
    }

# Converting an anonymous user to an anybill user

Anonymous users have restricted access to the anybill functions (e.g. restricted amount of bills). If the app user wants to take use of all the anybill functions he has to create an anybill account. To retain the data of an anonymous user the anybill sdk provides a function to convert an anonymous user to an anybill user.

    fun convertAnonymousUser(email: String, password: String, firstName: String, gender: String) {
        viewModelScope.launch {
            AuthProvider.convertAnonymousUserToAnybillUser(email, password, firstName, gender)
        }
    }

Afterwards the user can login into his anybill account using his login credentials.

# Get QR Code Data

Certain points of sale allow anybill users to receive bills by scanning a QR code on the user's phone. To get the jsonObject which should be included in the QR Code use the getQRCodeDatamethod.

    viewModelScope.launch {
        when(val qrCodeCall = AuthProvider.getQRCodeData()){
            is Success -> //Success
            is GenericError -> //Error Handling
            is NetworkError -> //Error Handling
        }
    }

The function 'getQRCodeData()' returns a 'QRCodeData' object which can be converted to a JSON String using its 'toJson()' function. Most QRCode libraries using 'TEXT' as ContentType can be initialized with the produced JSON String.

More information about QR Code data can be found on the anybill developer page (opens new window).

# Logout

Logging out an user deletes all of user's app data including cached bills, authentication information and app settings (of the anybill sdk).

    AuthProvider.logoutUser()

# Delete Account

Deleting an anybill account is irreversibly. The account can not be retrieved afterwards.

    fun deleteUser() {
        viewModelScope.launch {
            anybillAuth.deleteCurrentUser()
        }
    }

# TokenProvider

The TokenProvider is used for internal authentication operations. The public methods should not be accessed from the app itself. It might cause problem during the anybill authentication process.

Back to top

# AnybillBill

Singleton BillProvider grants access to the anybill bill functions. Most functions of the BillProvider are suspend functions and have to be called in a coroutine scope.

# Retrieving bills

With its caching technique the anybill sdk stores the user's bills in a local database and updates them using the anybill backend when needed. The anybill sdk provides a LiveData object bills representing a list of bills which can be observed in your app. By calling BillProvider's updateBills() method the LiveData object gets filled with the locally cached bills first and updated with the newly modified/added bills as soon as the API operation completed.

Sample-Code for your ViewModel:


    //This LiveData can be observed in your View
    val billList : LiveData<List<Bill>> = BillProvider.bills

    //Call this to initialize/fill/update the LiveData object
    fun updateBills(){
        viewModelScope.launch{
            when(val updateCall = BillProvider.updateBills()){
                is Success -> // Success
                is EmptySuccess -> // User has no Bill
                is GenericError -> // Error Handling
                is NetworkError -> // Error Handling
            }
        }
    }

To receive all of the user's bills without the caching process the anybill sdk provides the getBills() (includes parameters for pagination) method.


    val billList: MutableLiveData<Bill> = MutableLiveData<Bill>()

    fun getAllBills(){
        viewModelScope.launch {
            when (val billCall = BillProvider.getBills()) {
                is NetworkError -> //Error Handling
                is GenericError -> //Error Handling
                is Success -> {
                    billList.postValue(billCall.value)
                }
            }
        }
    }

# Filtering by Catgeory

To filter the user's bill list by Category, call getBillsByCategory() with the id of the Category.

fun getBillsByCategory(categoryId: String){
    viewModelScope.launch {
        when (val filterCall = BillProvider.getBillsFromCategory(categoryId)){
            is Success -> //Success
            is GenericError -> // Error Handling
            is NetworkError -> // Error Handling
        }
    }
}

# Adding bills

The anybill SDK provides several ways to add a Bill to the users account.

QR-Code on POS-Terminal

To add a bill from the QR-Code on the POS-Terminal use a common QR-Code Reader and extract the information of the shown QR Code. The data includes an URL with the ID of the Bill which should be added to the users account. Add the bill by calling one of the listed Methods with your preferred parameter type.

  //Depending on the QR Code Scanner you are going to receive a String or URL object.
  //
  //Format:
  //  https://getmy.anybill.de/#/bill/b45f4b77-9c3c-454a-8b87-08d8bbd02f7e
  //
  //You can choose to parse and validate the URL before using the sdk or call one of the following methods:

  BillProvider.addBillByID(billID: String)

  BillProvider.addBillByURL(url: URL)

  BillProvider.addBillByString(url: String)

QR-Code on Mobile-Screen

To add a bill with a QR-Code on the user's phone screen use AuthProviders QR-Code Data method. (See auth module documentation)

# Exporting bills

PDF

To share or save a bill as PDF exportBillAsPDF(bill: Bill) returns an URI of a PDF file in the cached directory of the app. The Uri can then be shared using an intent or saved on the user's device. (Generation of the PDF file takes up to 30 sec)

Share PDF in other apps:

    fun exportBillToPDF(bill: Bill) {
        viewModelScope.launch {
            when (val exportCall = billProvider.exportBillAsPDF(bill)) {
                is ApiResult.Success -> startIntent(exportCall.value)
                is ApiResult.GenericError -> //Error Handling
                is ApiResult.NetworkError -> //Error Handling
            }
        }
    }
    private fun startIntent(uri: Uri) {
        val intent = Intent()
        intent.action = Intent.ACTION_SEND
        intent.type = "application/pdf"
        intent.putExtra(Intent.EXTRA_STREAM, uri)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        context.startActivity(share)
    }

ZIP

If the user wants to get hold of all his bills as PDF files, the anybill SDK provides an endpoint to generate a .zip file containing the user's bills and send it to his email address.

    fun exportAllBills() {
        viewModelScope.launch {
            when(val exportCall = billProvider.exportBills()){
                is ApiResult.EmptySuccess -> //Success
                is ApiResult.GenericError -> //Error Handling
                is ApiResult.NetworkError -> //Error Handling
            }
        }
    }

# Deleting bills

Deleting a bill is irreversible. After deleting a bill with BillProvider.deleteBill(bill: Bill) the LiveData object should be updated using updateBills().

    fun deleteBill(bill: Bill) {
        viewModelScope.launch {
            when(val deleteBill = billProvider.deleteBill(bill)){
                is ApiResult.EmptySuccess -> //Success
                is ApiResult.GenericError -> //Error Handling
                is ApiResult.NetworkError -> //Error Handling
            }
        }
    }

Back to top

# AnybillCategory

Singleton CategoryProvidergrants access to the anybill category functions. Most functions of the CategoryProvider are suspend functions and have to be called in a coroutine scope.

# Retrieving categories

With its caching technique the anybill sdk stores the categories in a local database and updates them using the anybill backend when needed. The anybill sdk provides a LiveData object categories representing a list of bills which can be observed in your app. By calling CategoryProvider's updateCategories() method the LiveData object gets filled with the locally cached category first and updated with the newly modified/added categories as soon as the API operation completed.

Sample-Code for your ViewModel:


    //This LiveData can be observed in your View
    val categoryList : LiveData<List<Category>> = CategoryProvider.categories

    //Call this to initialize/fill/update the LiveData object
    fun updateCategories(){
        viewModelScope.launch{
            CategoryProvider.updateCategories()
        }
    }

If you don't want to use Live Data objects, you can use updateAndGetAllCategories() which will return all Anybill Categories from the API.

# Getting a category by id

Retrieving a single Category object by its ID can be done with the getCategoryById() functions.


    fun getCategoryById(id: String){
      when(val categoryCall = CategoryProvider.getCategoryById(id)){
        is ApiResult.Success -> //Continue with categoryCall.value
        is ApiResult.GenericError -> //Error Message
        is ApiResult.NetworkError -> //Error Message
      }
    }

# Matching a store name with a category

When creating a ScanBill the name of the vendor can be set manually. Using getCategoriesByName() the vendor name can be matched with a BillCategory/List of BillCategory which then can be displayed to the user as a suggestion.


    fun getCategoryByName(name: String){
      when(val categoryCall = CategoryProvider.getCategoryByName(name){
        is ApiResult.Success -> //Continue with categoryCall.value
        is ApiResult.GenericError -> //Error Message
        is ApiResult.NetworkError -> //Error Message
      }
    }

Back to top

# AnybillLog

The Log Module is used by other modules to log internal errors of the anybill SDK to Datadog. No information of your users or your app are being logged. Accessing the Log module's functions in your app code might affect support and analytics processes.

Back to top

# AnybillOCR

Singleton OCRProvidergrants access to the anybill ocr functionalities. Most functions of the OCRProvider are suspend functions and have to be called in a coroutine scope.

# Adding a bill using the OCR feature

The OCR feature allows users to scan a paper receipt using their smartphone camera. The provided image is processed and converted into a ScanBill model. To add a bill using the OCR feature follow these steps:

  1. Uploading image of paper receipt

To upload an image of the paper receipt retrieve an image using the camera or media option of the device and use the addBillWithOCRScan() function with an InputStream of the image which should be scanned.

    fun getOCRScanForImage(inputStream: InputStream) {
        viewModelScope.launch {
            when (val ocrScanCall = OCRProvider.addBillWithOCRScan(inputStream)) {
                is ApiResult.Success -> // Continue editing ScanBillDTO from ocrScanCall.value
                is ApiResult.GenericError -> //Error Handling
                is ApiResult.NetworkError -> //Error Handling
            }
        }
    }

OCR Scan can take up to 20 seconds.

  1. Editing and finalizing the new bill

The returned ScanBillDTO contains all values which were recognized by the OCR Scan. These values can now be edited and/or extended. To finalize the bill, call updateScannedBill() with the received/extended DTO object.


    //Several ways to add a LineItem using the extension functions ScanBillDTO.addLineItem()

    scanBillDTO.sellerName = "YourVendor"
    scanBillDTO.addLineItem(10.0, 2.0, QuantityMeasure.Count, "Item")

    val lineItem = LineItemDTO(null, 1.0, QuantitityMeasure.Kilogram, 3.45)
    scanBillDTO.addLineitem("Item" ,lineItem)

    viewModelScope.launch {
        when (val ocrScanCall = ocrProvider.updateScannedBill(scanBillDTO)) {
            is ApiResult.Success -> // Success
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }

# Manually creating a Scanbill without image

ScanBills can also be created manually without using the OCR feature by creating an instance of ScanBillDTO with the required values and using addBillWithoutScan().

fun createNewBill(){
    val newBill = ScanBillDTO(
            sellerName: "Seller",
            date: "2021-07-19T13:10:06.8684491+00:00",
            fullAmountInclVat: 10.0,
            foreignCurrency: "EUR",
            categoryId: "fa93e0bb-49b0-4077-1681-08d8be38cdb0",
            paymentTypeInformation: PaymentTypeInformationDTO(10.0, PaymentType.Cash)
        )
    viewModelScope.launch {
        when (val addCall = ocrProvider.addBillWithoutScan(newBill)) {
            is ApiResult.Success -> // Success
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }
}

# Retrieving an image of a ScanBill

To display the image of a previously added ScanBill use getImageOfScannedBill(). On success this method returns a ByteArray containing the file information of the image. This can then be converted to a Bitmap and displayed to the user.


val bitMap : MutableLiveData<Bitmap> = MutableLiveData()

fun getImageOfBill(billId: String){
    viewModelScope.launch {
        when (val getCall = ocrProvider.getImageOfScannedBill(billId)) {
            is ApiResult.Success -> createBitMapFromBytes(getCall.value)
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }
}

private fun createBitMapFromBytes(bytes: ByteArray){
    val bitmap: Bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
    bitMap.postValue(bitmap)
}

Back to top

# AnybillWarranty

The warranty module provides methods to manage user's warranties. Warranties can be set for a bill or individual line items and can be connected to a push notitication reminder.

# Retrieving and updating warranties

With its caching technique the anybill sdk stores the warranties in a local database and updates them using the anybill backend when needed. The anybill sdk provides a Live Data object representing a list of warranties which can be observed in your app. By calling WarrantyProvider's updateWarranties() method the observable object gets filled with the locally cached warranties first and updated with the newly modified/added warranties as soon as the API operation completed.

Sample-Code for your ViewModel :


    //This LiveData can be observed in your View
    val categoryList : LiveData<List<Category>> = WarrantyProvider.warranties

    //Call this to initialize/fill/update the LiveData object
    fun updateWarranties(){
        viewModelScope.launch{
            WarrantyProvider.updateWarranties()
        }
    }

If you don't want to use Live Data object, you can use getNetworkWarranties()which will return all Warranties of the user.

# Adding a new warranty

Using the WarrantyDTO struct, you can create a new warranty for a user's bill:

* `articleName`:    Specifies the article's name        (Optional)
* `billId`:         The bill's id                       (Required)
* `lineItemId`:     The lineitem's id                   (Optional)
* `warrantyDate`:   Warranty's due date in ISO 8601     (Required)
* `reminders`:      Reminder object                     (Optional)

Each LineItem and articleName can only have one Warranty. The warranty's due date can not be in the past.

    val warrantyDTO = WarrantyDTO(
                    articleName = "Apple", 
                    billid = "ce763c6a-4ae8-4l7c-df7f-08d9dfc2g23a", 
                    lineItemId = null, 
                    warrantyDate = "2021-12-31T14:00:00Z", 
                    reminders = emptyList())

    when(val warrantyCall = WarrantyProvider.addWarranty(warrantyDTO)){
        is NetworkError -> //Error Handling
        is GenericError -> //Error Handling
        is Success -> {
            warrantyCall.value //Further implementation
        }
    }

# Activating / deactivating notifications for a specific Warranty

Notifications for each Warranty can be activated or deactivated with updateWarrantyNotificationIsActivated(). This method takes two parameters:

* `warrantyId`:     The warranty's id                                   (Required)
* `activated`:      Boolean indicating the desired activation status    (Required)
    when(val notificationUpdateCall = WarrantyProvider.updateWarrantyNotificationIsActivated(warrantyId, activated)){
            is NetworkError -> //Error Handling
            is GenericError -> //Error Handling
            is Success -> {
                notificationUpdateCall.value //Further implementation using the updated Warranty object
            }
        }

Back to top

# AnybillTree

Offers a „tree level system“ to enhance the motivation of the user by planting trees for scanned receipts.

# Retrieving all Tree Levels

To retrieve a list of all TreeLevels use the getAllTreeLevels() functions

    viewModelScope.launch {
        when (val treeCall = TreeProvider.getAllTreeLevels()) {
            is ApiResult.Success -> treeCall.value
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }

# Get Trees planted by user

Returns the amount of trees which were planted by the user's bills

    viewModelScope.launch {
        when (val treeCall = TreeProvider.getTreesPlantedByUser()) {
            is ApiResult.Success -> treeCall.value
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }

# Retrieving complete Tree Info

Information like the amount of trees planted by the user, the total amount of trees planted and the tree levels can all be accessed separately. By calling getTreeInfo() all this information can be retrived within one call.

    viewModelScope.launch {
        when (val treeCall = TreeProvider.getTreeInfo()) {
            is ApiResult.Success -> treeCall.value
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }

Back to top

# AnybillDocsManager

Allows the export of receipts directly to a document manager like GetMyInvoices (opens new window) or Fileee (opens new window).

# Fileee

Fileee enables mutliple document related features:

  • Export receipts to fileee directly and automatically with one click
  • Link email accounts and services such as Dropbox or GoogleDrive
  • All private documents in a single overview
  • Share receipts and documents with your family and friends

The anybill sdk provides the possibility to link an anybill account to an existing Fileee account and export anybill bills to the fileee storage. By starting an external authentication process in a web view the user can login into his Fileee account and grant the permission to bind both accounts.

To start the authentication process call startAuthorizationRequest(context: Context, responseActivity: Class<*>) of the FileeeProvider. responseActivity being the Activity which should be called when the authentication process finished or the user exists the web view.


viewModelScope.launch {
    when (val fileeeRequest = FileeeProvider.startAuthorizationRequest(context, MainActivity::class.java)) {
        is EmptySuccess -> // Success (Auth Process started)
        is GenericError -> // Error Handling
        is NetworkError -> // Error Handling
    }
}

To set your application as a receiver of the redirect set the redirect scheme in your app's build gradle:


android {
    ...

    manifestPlaceholders = [
        'appAuthRedirectScheme': 'de.fileee'
    ]

    ...
}

In the onResume() of your responding Activity catch the Intent sent by the redirecting web view:


//In your Activity

override fun onResume() {
    super.onResume()
    if (intent != null){
        viewModel.checkForFileeeAuthResponse(intent)
    }
}

//In your ViewModel:

fun checkForFileeeAuthResponse(intent: Intent) {
    viewModelScope.launch {
        when (val fileeeRequest = FileeeProvider.checkForAndHandleFileeeAuthResponse(intent)) {
            is ApiResult.Success -> // Successfully connected accounts
            is ApiResult.EmptySuccess -> // No Auth Response found
            is ApiResult.GenericError -> // Error Handling
            is ApiResult.NetworkError -> // Error Handling
        }
    }
}

# Export bills to Fileee

To export an anybill bill to the fileee documents use exportBillsToFileee(billIds: ArrayList<String>).


val billIds = ["c2c613b7-7ba2-47e7-8b94-08d8bbd02f7e", "2a0af38a-6d45-43cc-8b95-08d8bbd02f7e"]

viewModelScope.launch {
    when (val fileeeRequest = FileeeProvider.exportBillsToFileee(billIds) {
        is ApiResult.Success -> // Successfully exported bills
        is ApiResult.GenericError -> // Error Handling
        is ApiResult.NetworkError -> // Error Handling
    }
}

# GMI

GetMyInvoice is a central invoice management software for your business with several features:

  • Invoice management with automatic download
  • Export receipts to GetMyInvoices directly and automatically with one click
  • Overview of all exported receipts
  • Export to many accounting tools

With the anybill sdk anybill accounts can either be binded with an existing GMI Account or connected by creating a new GMI Account.

# Binding with an existing GMI Account

To bind an anybill account with an existing GMI Account, the user has to manually copy an API Key from its GMI Account:

To access the API Key the user has to follow these steps:

  1. Login on the GMI page: https://de4-login.getmyinvoices.com/api_access.php
  2. Click on the plus symbol in the top left corner
  3. Select your name
  4. Click Save
  5. Copy the API Key shown next to your name

Link both accounts by using updateGMIAccountApiKey(apiKey: String) with the received API Key as parameter.


viewModelScope.launch {
    when (GMIProvider.updateGMIAccountApiKey(apiKey)) {
        is EmptySuccess -> // Successfully updated GMI api key
        is GenericError -> // Error Handling
        is NetworkError -> // Error Handling
    }
}

# Create a new GMI Account

The anybill sdk also enables to create a new GMI Account using the anybill app API. To create a GMI Account use the GMIAccountDTO model.


val gmiAccount = GMIAccountDTO(
                  companyName = "exampleName",
                  country = 76,
                  email = "example@anybill.de,
                  firstName = "example",
                  language = GMILanguageDTO.DE,
                  password = "example"
                )

viewModelScope.launch {
    when (GMIProvider.createGMIAccount(gmiAccount)) {
        is EmptySuccess -> // GMI Account was successfully created
        is GenericError -> // Error Handling
        is NetworkError -> // Error Handling
    }
}

# Exporting bills to GMI

To export an anybill bill to GMI use exportBillsToGMI(billIds: ArrayList<String>):


val billIds = ["c2c613b7-7ba2-47e7-8b94-08d8bbd02f7e", "2a0af38a-6d45-43cc-8b95-08d8bbd02f7e"]

viewModelScope.launch {
    when (val fileeeRequest = FileeeProvider.exportBillsToGMI(billIds) {
        is ApiResult.Success -> // Successfully exported bills
        is ApiResult.GenericError -> // Error Handling
        is ApiResult.NetworkError -> // Error Handling
    }
}

Back to top

# AnybillUBA

UBA (Universal Banking Access) Module allows users to create a connection to multiple bank accounts via PSD2 interface for a full financial overview.

# Connecting and managing bank accounts

To connect a new bank account and/or manage already connected bank accounts anybill provides a webview interface. To open the webview interface you have to register an ActivityResultLauncher in your Fragment/Activity. You can handle the result of the UBA Webview in this ResultLauncher

// In your Activity/Fragment

private val resultLauncher = this.registerUBAResultLauncher { ubaState ->
        when (ubaState) {
            UBAState.SUCCESSFULLY_LINKED -> //Success
            UBAState.FAILED_LINKING -> //Eror Handling
        }
    }

To start the UBA Webview use manageBankAccounts of the UBAProvider with the registered ResultLauncher and the Context of the View you are calling from. You can determine wether the Webview should directly open a bank login by setting the optional Boolean addBankLogin to true (e.g. if the user hasn't connected a bank account yet). Leaving the Boolean on the default value will open a bank account overview where the user can manage his current bank accounts.

//In your ViewModel

fun manageBankAccounts(launcher: ActivityResultLauncher<Intent>, context: Context) {
    viewModelScope.launch {
        when(val ubaManageCall = UBAProvider.manageBankAccounts(launcher, context, true)){
            is ApiResult.EmptySuccess -> //Webview was started
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }
}

# Retrieving transactions

To retrieve all connected bank accounts including their psd2 transactions use getBankAccountsWithTransaction().

viewModelScope.launch {
    when (val bankAccounts = UBAProvider.getBankAccountsWithTransaction()) {
        is ApiResult.Success -> //Success
        is ApiResult.GenericError -> //Error Handling
        is ApiResult.NetworkError -> //Error Handling
    }
}

# Synchronize bank accounts

Due to legal reasons every PSD2 connection has to be reconnected manually every 90 days. The returned bank accounts from getBankAccountsWithTransaction() contain a Boolean flag isSynced. If the Boolean value is false the bank account has be reconnected by the app user. To open a Webview where the user can synchronize his bank accounts use syncBankAccount().

Register an ActivityResultLauncher in your Fragment/Acitivity

// In your Activity/Fragment

private val resultLauncher = this.registerUBAResultLauncher { ubaState ->
        when (ubaState) {
            UBAState.SUCCESSFULLY_LINKED -> //Success
            UBAState.FAILED_LINKING -> //Eror Handling
        }
    }

Start the Webview using syncBankAccount() with the bankId of the bank which should be synchronized, the registered ResultLauncher and the Context of the View you are calling from.

//In your ViewModel

fun syncBankAccounts(bankId: String, launcher: ActivityResultLauncher<Intent>, context: Context) {
    viewModelScope.launch {
        when(val syncCall = UBAProvider.syncBankAccount(bankId, launcher, context)){
            is ApiResult.EmptySuccess -> //Webview was started
            is ApiResult.GenericError -> //Error Handling
            is ApiResult.NetworkError -> //Error Handling
        }
    }
}

# Linking bills and transactions

Using the anybill's UBA Module an anybill user can link a bill with a transaction. The linked IDs are then included in the Bill/Transaction Model.

viewModelScope.launch {
    when (val linkCall = UBAProvider.linkTransactionWithBill(transactionId, billId)) {
        is ApiResult.EmptySuccess -> //Success
        is ApiResult.GenericError -> //Error Handling
        is ApiResult.NetworkError -> //Error Handling
    }
}

Linked bills/transactions can be unlinked using the provided methods of the UBAProvider.


UBAProvider.unlinkBillByTransactionId(transactionId)

//or 

UBAProvider.unlinkBillByBillId(billId)

Back to top

# AnybillExpenses

Coming Soon

# AnybillUI

Coming Soon

Back to top