# Android Integration
# General Information
# Base Modules
AnybillBase
-
Authentication
- Login
- Register (Deprecated)
- Convert an anonymous user
- Get QR Code Data
- Logout
- Delete Account Receipt
- Retrieving receipts
- Filtering by Catgeory
- Adding receipts
- Exporting receipts
- Deleting receipts Category (Deprecated)
- Retrieving categories
- Getting a Category by ID
- Matching a store name with a category
# Additional Modules
# 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:
- 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}
}
}
}
}
- Add the desired anybill modules to your module's 'build.gradle'. The base module required for the basic usage of the SDK
dependencies {
implementation 'de.anybill.anybill_android_sdk:base:{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>
# Change the api mode to staging
For developing purposes you can change the api environment to Staging by adding the Staging url (https://app.stg.anybill.de/) as value for the key anybill_base_url
in your AndroidManifest.xml:
<application
...
>
...
<meta-data android:name="anybill_base_url" android:value="https://app.stg.anybill.de/"/>
...
</application>
# Enable java.time for device under API 26
If you are targeting devices under API 26, we recommend enabling java.time usage for lower API usages. Add following line to your compile options in your app build.gradle:
compileOptions {
isCoreLibraryDesugaringEnabled = true
...
}
Exceptions
As our SDK has a minimum API Version of 24, we still support API 24 and 25. However without enabling java.time exceptions during saving of Tokens may occur.
# 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
INVALID_REFRESH_TOKEN -> //Error Handling
}
}
is Success -> {
userInfoTask.value //Further implementation
}
}
}
}
# Anybill App Link
The anybill SDK supports deep linking into your app from the anybill receipt website. The deep link either opens the app directly (if installed) or persists the data over an app installation. You can utilize this feature to redirect users to your app, acquire new users and add the receipt from the receipt website to an account.
To enable anybill AppLink, follow these steps:
- Acquire Applink URL provided by anybill with your unique path pattern by contacting us beforehand:
<intent-filter android:label="@string/app_name" android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Add your unique path pattern provided by anybill -->
<data android:scheme="https" android:host="applink.anybill.de"
android:pathPattern="/${your_path_pattern}" />
</intent-filter>
- Generate Digital Asset Links file using the Android Link Assistant:
Android Link Assistant (opens new window)
- Provide the returned file to anybill:
[
{
"relation": [
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
"package_name": "de.anybill.ui_module_integration_example",
"sha256_cert_fingerprints": [
"5D:78:62:8E:4B:6A:E8:33:BE:9A:94:0B:7D:24:30:4E:79:DF:D3:8B:E7:0C:42:8B:FD:72:3F:1D:36:BC:F6:C3"
]
}
}
]
⚠️ To enable anybill AppLink for your debug and release version, you'll have to generate multiple files and provide anybill both of them!
- Insert code to handle incoming app links:
In your MainActivity
:
// Handle incoming AppLinks when app was installed and not in background
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
// Check if app was opened by a deep link (AnybillAppLink) and forward the included receipt ID
// to the UI module
AnybillAppLink.checkForAnybillAppLinkIntent(intent) { data ->
if (data != null) {
//Add Receipt
}
}
}
// ...
// Handle incoming AppLinks when app was installed and in background
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.run {
// Check for app being opened by AnybillAppLink
AnybillAppLink.checkForAnybillAppLinkIntent(intent) { data ->
if (data != null) {
//Add Receipt
}
}
}
}
// ...
// Handle incoming AppLinks when app was not installed and is opened for the first time
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
AnybillAppLink.checkForAnybillAppLinkData(this) { data ->
if (data != null) {
//Add Receipt
}
}
}
# 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:
- Create a notification handler:
class YourNotificationHandler(private val context: Context) : AnybillMessagingHandler {
override fun onAnybillMessageReceived(remoteMessage: RemoteMessage) {
// Display Notification
}
}
- 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:
- Create an notification handler:
class YourNotificationHandler(private val context: Context) : AnybillMessagingHandler {
override fun onAnybillMessageReceived(message: RemoteMessage) {
// Display Notification
}
}
- 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.
- 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
}
}
# Default handling of push notifications
As just demonstrated, notifications are handled inside the onAnybillMessageReceived
method. The simplest way of handling the notifications is to just dislpay them
as local android notifications. However the SDK also offers the possibility to display them on UI components of the SDK as a snackbar. Additionally some special notifications trigger
specific actions when handled by the SDK such as automatically updating the list of receipts when a notifications arrives, stating that a new receipt has been added to the logged in account. To enable the SDK internal handling of notifications, the method handleAnybillNotification
of the class AnybillMessagingService
can be called like so:
override fun onAnybillMessageReceived(remoteMessage: RemoteMessage) {
anybillNotificationHandler.handleAnybillNotification(remoteMessage)
}
To avoid displaying of multiple snackbars/notifications it is advised to first check if the SDK UI is currently being used and based on the result either let the SDK handle the notification or let the implementing app display the notification.
# AnybillBase
# Authentication
The Base 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.INVALID_REFRESH_TOKEN -> {
// 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..
[DEPRECATED]
# 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
}
}
# Converting an anonymous user to an anybill user
Anonymous users have restricted access to the anybill functions (e.g. restricted amount of receipts). 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 receipts by scanning a QR code on the user's phone. To get the jsonObject which should be included in the QR Code use the getQRCodeData
method.
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 receipts, 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.
# Receipt
Singleton ReceiptProvider
grants access to the anybill receipt functions. Most functions of the ReceiptProvider are suspend functions and have to be called in a coroutine scope.
# Retrieving receipts
[DEPRECATED]
With its caching technique the anybill sdk stores the user's receipts in a local database and updates them using the anybill backend when needed.
The anybill sdk provides a LiveData object receipts
representing a list of receipts which can be observed in your app. By calling ReceiptProvider's updateBills()
method the LiveData object gets filled with the locally cached receipts first and updated with the newly modified/added receipts as soon as the API operation completed.
Sample-Code for your ViewModel:
//This LiveData can be observed in your View
val billList : LiveData<List<Receipt>> = ReceiptProvider.receipts
//Call this to initialize/fill/update the LiveData object
fun updateBills(){
viewModelScope.launch{
when(val updateCall = ReceiptProvider.updateBills()){
is Success -> // Success
is EmptySuccess -> // User has no Receipt
is GenericError -> // Error Handling
is NetworkError -> // Error Handling
}
}
}
To receive all of the user's receipts without the caching process the anybill sdk provides the getBills()
(includes parameters for pagination) method.
val billList: MutableLiveData<Receipt> = MutableLiveData<Receipt>()
fun getAllBills(){
viewModelScope.launch {
when (val billCall = ReceiptProvider.getBills()) {
is NetworkError -> //Error Handling
is GenericError -> //Error Handling
is Success -> {
billList.postValue(billCall.value)
}
}
}
}
# Filtering by Catgeory
To filter the user's receipt list by Category, call getBillsByCategory()
with the id of the Category.
fun getBillsByCategory(categoryId: String){
viewModelScope.launch {
when (val filterCall = ReceiptProvider.getBillsFromCategory(categoryId)){
is Success -> //Success
is GenericError -> // Error Handling
is NetworkError -> // Error Handling
}
}
}
# Adding receipts
The anybill SDK provides several ways to add a Receipt to the users account.
QR-Code on POS-Terminal
To add a receipt 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 Receipt which should be added to the users account. Add the receipt 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:
ReceiptProvider.addReceiptByID(billID: String)
ReceiptProvider.addReceiptByURL(url: URL)
ReceiptProvider.addReceiptByString(url: String)
QR-Code on Mobile-Screen
To add a receipt with a QR-Code on the user's phone screen use AuthProviders QR-Code Data method. (See auth module documentation)
# Exporting receipts
To share or save a receipt as PDF exportReceiptAsPDF(receipt: Receipt)
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 exportReceiptToPDF(receipt: Receipt) {
viewModelScope.launch {
when (val exportCall = billProvider.exportReceiptAsPDF(receipt)) {
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 receipts as PDF files, the anybill SDK provides an endpoint to generate a .zip file containing the user's receipts and send it to his email address.
fun exportAllReceipts() {
viewModelScope.launch {
when(val exportCall = billProvider.exportReceipts()){
is ApiResult.EmptySuccess -> //Success
is ApiResult.GenericError -> //Error Handling
is ApiResult.NetworkError -> //Error Handling
}
}
}
# Deleting receipts
Deleting a receipt is irreversible. After deleting a receipt with ReceiptProvider.deleteReceipt(receipt: Receipt)
the LiveData object should be updated using updateReceipts()
.
fun deleteReceipt(receipt: Receipt) {
viewModelScope.launch {
when(val deleteReceipt = billProvider.deleteReceipt(receipt)){
is ApiResult.EmptySuccess -> //Success
is ApiResult.GenericError -> //Error Handling
is ApiResult.NetworkError -> //Error Handling
}
}
}
[DEPRECATED]
# Category
Singleton CategoryProvider
grants 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 receipts 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
}
}
[DEPRECATED]
# AnybillStore
The store module provides information about stores that support the anybill system, or will support it in the near future. The stores and details are available via the StoreProvider
.
# Retrieving stores
To retrieve stores from the StoreProvider
the method
getStores(skip: Int?, take: Int?, creationTypeFilter: Set<BillCreationType>): LiveData<List<AnybillStore>>
is used.
The SDK uses caching and stores all stores, once they have been fetched from the backend.
It returns a LiveData that publishes the locally cached stores and automatically updates, if the cached stores change.
The parameters skip and take can be optionally used for pagination. If they are null the whole list of all supported stores is being returned. If at least one of them is not null, pagination is applied and the first skip
stores on the list of stores ordered by vendor name are being skipped and take
stores are being fetched from the database. So the overall result is a list of length take
with an offset in the stores list of skip
. Whenever take
is null it is treated as infinite.
The parameter creationTypeFilter
is used to optionally retrieve only stores that support a specific method of receipt creation (e.g. a store might not support displaying QR codes and therefore only gives out receipts via scanning the code off the smartphone display).
If the parameter is null, no filtering is applied and all stores can be retrieved. If the parameter is not null only stores which match at least one of the specified BillCreationTypes are being returned.
To fetch the latest list of stores from the backend the method updateStores(billCreationTypes: Set<BillCreationType>): ApiResult<Unit>
is used.
It returns an ApiResult that indicates if the network request succeeded or not. The optional parameter billCreationTypes can be used to filter the stores as previously mentioned. The difference is that this parameter causes filtering at request level, only returning stores that match the filter and therefore reducing bandwidth and local cache size. However if this parameter is not empty and therefore filtering is applied, there is no possibility to retrieve stores with other BillCreationTypes anymore until you call this method again with a different or no filter at all. This is intended for SDK integrations where the app only needs to support stores that all have the same receipt creation capabilities.
Sample-code for your viewmodel:
fun updateStores() {
viewModelScope.launch {
storeProvider.updateStores()
}
}
fun getStores(billCreationTypes: Set<BillCreationType> = setOf()) {
_currentLifeData?.let {
_storesToDisplay.removeSource(it)
}
viewModelScope.launch {
val result = storeProvider.getStores(creationTypeFilter = billCreationTypes)
_currentLifeData = result
_storesToDisplay.addSource(result) {
_storesToDisplay.postValue(it)
}
}
}
Where _storesToDisplay is a MediatorLiveData of Type List<AnybillStore>
# Retrieving store details
The AnybillStore
data model does not return all available information about a store. Some properties of a store have to be queried separately via the
getStoreDetails(storeId: String): LiveData<AnybillStoreDetails?>
method.
This method takes the ID of an AnybillStore
and returns an observable LiveData which also automatically updates, if the cached value of this AnybillStoreDetails
changes.
To update the local value of the store details use the method updateStoreDetails(storeId: String): ApiResult<AnybillStoreDetails?>
.
It returns an ApiResult that indicates if the network request succeeded or not. In the case of a successfully executed network request contains the store details in the ApiResult
value property, if you prefer to not use the LiveData returned by getStoreDetails
.
Sample-code for your viewmodel:
fun getStoreDetail(storeId: String): LiveData<AnybillStoreDetails?> {
return storeProvider.getStoreDetails(storeId)
}
fun updateStoreDetails(storeId: String) {
viewModelScope.launch {
storeProvider.updateStoreDetails(storeId)
}
}
# AnybillContentArea
The Content Area Module enables the Content Area feature, which enables the displaying of additional information (e.g. advertisement or news) on receipts. With this module the data of these content areas can be queried to display them in the SDK implementing app. Another possibility is to use this in conjunction with the Content Area UI Module for a working implementation out of the box.
# Retrieving a content area
Instances of ContentArea can be retrieved through the ContentAreaProvider. This Provider exposes the method getContentArea(contentAreaId: String), returning a LiveData of type content area which updates automatically, if an update is requested. The methods only parameter is the ID of the content area to fetch, which can be found in the receipt data model. The SDK utilizes caching and returns a local copy of the latest cached value.
Sample code for your viewmodel:
private val _contentArea: MediatorLiveData<ContentArea?> =
MediatorLiveData<ContentArea?>()
private var currentContentAreaLiveData: LiveData<ContentArea?>? = null
private val anybillContentAreaProvider = ContentAreaProvider
fun loadContentAreaById(contentAreaId: String) {
if (_contentArea.hasObservers()) {
currentContentAreaLiveData?.let {
_contentArea.removeSource(it)
}
}
_contentArea.addSource(anybillContentAreaProvider.getContentArea(contentAreaId)) {
_contentArea.postValue(it)
}
}
# Updating a content area
In order to fetch the latest content area data from the backend, the ContentAreaProvider exposes the method updateContentArea(contentAreaId: String), returning an ApiResult of Type ContentArea. The resulting ApiResult indicates, whether or not the backend request succeeded or failed. In case of a successful request the local cache is automatically updated and the new data automatically published via the aforementioned LiveData.
viewModelScope.launch {
handleApiResult(anybillContentAreaProvider.updateContentArea(contentAreaId))
}