Ticket Details in the Open Event Android App

After entering all the attendee details and buying a ticket for an event the user expects to see the ticket so that he can use it later. This is why ticket details are shown in a separate fragment in the Open Event Android App. Let’s see how the tickets fragment was made in the Open Event Android App.

Two things that we require from the previous fragment are the event id and the order identifier so that we show the information related to the event as well as the order.

if (bundle != null) {
id = bundle.getLong(EVENT_ID, -1)
orderId = bundle.getString(ORDERS)


We are requesting data from the following two endpoints. In the first GET request we are passing the order identifier in the URL and we get the list of attendees from the server. In the second endpoint we simply pass the event identifier and get the event details from the server.


fun attendeesUnderOrder(@Path("orderIdentifier") orderIdentifier: String): Single<List<Attendee>>

fun getEventFromApi(@Path("eventIdentifier") eventIdentifier: Long): Single<Event>


Here we are observing the attendees live data and adding the list of attendees returned from the server to the recyclerview so that we can show the user all the details of the attendees like the first name, last name etc. We then notify the adapter that the list of attendees have been added. In the end we log the number of attendees so that it is easier to debug in case there are any bugs.

orderDetailsViewModel.attendees.observe(this, Observer {
it?.let {
Timber.d("Fetched attendees of size %s", ordersRecyclerAdapter.itemCount)


As mentioned earlier we need the event id and order identifier to show event and attendee related information to the user so here we are using the event id and appending it to the url. We are sending a GET request in a background thread and storing the list of events returned from the server in a mutable live data. In case of any errors we log it and show the error message to the user. Similarly we will use the order identifier to get the list of orders from the server and show it to the user.

event.value = it
}, {
Timber.e(it, "Error fetching event %d", id)
message.value = "Error fetching event"


After fetching the list of attendees and event details, the only thing that we need to do is extract the important information and show it to the user so we pass the order and event objects to the ViewHolder. This can be done simply by using the attendee and event objects and accessing the fields required.

itemView.name.text = "${attendee.firstname} ${attendee.lastname}"
itemView.eventName.text = event?.name
itemView.date.text = "$formattedDate\n$formattedTime $timezone"



  1. ReactiveX official documentation: http://reactivex.io/
  2. Retrofit Android: http://square.github.io/retrofit/
  3. Google Android Developers Recycler View: https://developer.android.com/guide/topics/ui/layout/recyclerview
Continue ReadingTicket Details in the Open Event Android App

Automating Play Store releases in Open Event Android with Fastlane

In Open Event Android we are using fastlane to automate the process of releasing the app to the Play Store, otherwise without this tool we would have to sign the release apk everytime before making a release but with fastlane all we need to do is just merge the development branch with the master branch and the updated app is released in Play Store.

The first thing that we need to do is encrypt the signing keys and upload it to the repository so we will create an encrypted tar file of the signing keys and upload it to the repository.

Now we want to decrypt the tar file everytime we merge our development branch into the master branch. We will create a bash script which does the above task which looks like this.

set -e


if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "fossasia/open-event-android" -o "$TRAVIS_BRANCH" != "$DEPLOY_BRANCH" ]; then
 echo "We decrypt key only for pushes to the master branch and not PRs. So, skip."
exit 0

# Decrypt keys
openssl aes-256-cbc -K $encrypted_59a1db41ee4d_key -iv $encrypted_59a1db41ee4d_iv -in ./scripts/secrets.tar.enc -out ./scripts/secrets.tar -d
tar xvf ./scripts/secrets.tar -C scripts/


We need to define all these variables written after the $ sign as our Travis environment variables.

Next thing that we need to do is sign the apk so first thing that we check in the update-apk script is if we are on the master branch. We take the unsigned apk and sign it with the following commands.

 cp open-event-master-app-playStore-release-unsigned.apk open-event-master-app-playStore-release-unaligned.apk
jarsigner -verbose -tsa http://timestamp.comodoca.com/rfc3161 -sigalg SHA1withRSA -digestalg SHA1 -keystore ../scripts/key.jks -storepass $STORE_PASS -keypass $KEY_PASS open-event-master-app-playStore-release-unaligned.apk $ALIAS


The following command zipaligns the app

 ${ANDROID_HOME}/build-tools/27.0.3/zipalign -v -p 4 open-event-master-app-playStore-release-unaligned.apk open-event-master-app-playStore-release.apk


After signing the release apk, the final step is to publish the app to the playstore. The following command will install fastlane.

gem install fastlane


This command will take the signed apk and upload it in alpha channel of the playstore.

fastlane supply --apk open-event-master-app-playStore-release.apk --track alpha --json_key ../scripts/fastlane.json --package_name $PACKAGE_NAME


That’s it! Now releasing an update to the playstore is as simple as sending a pull request to the master branch. A process that would have otherwise required few minutes has been reduced to just seconds.


  1. Fastlane Official Site: https://fastlane.tools/
  2. Fastlane Android Documentaion: https://docs.fastlane.tools/getting-started/android/release-deployment/
  3. Fastlane Repository: https://github.com/fastlane/fastlane
Continue ReadingAutomating Play Store releases in Open Event Android with Fastlane

Building Open Event Android for F-Droid

According to the official website F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. Only apps that are open source and use only open source libraries are accepted in F-Droid. Let’s see what steps were taken to publish Open Event Android in F-Droid.

We need to build a different app for F-Droid because it cannot use any proprietary  libraries so instead of making a new app we made two flavors of the same app. One that would be published in playstore and the other for F-Droid. It is really easy to make different flavors of the same app. Just add the following lines in the build.gradle

flavorDimensions "default"

productFlavors {
fdroid {
dimension "default"

playStore {
dimension "default"


We specify the flavor dimension in the first line because all product flavors must have a flavor dimension and then we specify the name of our product flavors. That’s all that is required to make various flavors of an app after that we just need to remove the proprietary libraries from the F-Droid build.

When we have finished building the app we can start with the process of submitting the app to F-Droid. We need to send a Merge Request to the F-droid repository. Let’s see what all steps are required before we could submit our Merge Request to include our app in F-Droid

We need to clone the fdroid server directly from the source so that we have all the latest changes and set the path.

git clone https://gitlab.com/fdroid/fdroidserver.git
export PATH="$PATH:$PWD/fdroidserver"


Then clone your fork of the fdroiddata repository and open the directory. You need to write your own username here.

git clone https://gitlab.com/nikit19/fdroiddata.git
cd fdroiddata


The following command will make a template for you in the metadata directory, the name of the file is our application id. If you open the metadata folder you will see text files of all the apps that are published in F-Droid. If you want to publish an app in F-droid it is this file that needs to be sent in merge request. There are no other changes that needs to be done, we just have to fill the fields in the meta file correctly.

cp templates/app-minimal metadata/com.eventyay.attendee.txt


After filling all the details in the meta file we need to commit those changes. The following command will check if there are any syntax errors in the meta file.

fdroid readmeta


Finally we need to build the app. The following command builds the app from the source repository so if we have followed all the steps correctly, the app would build successfully and then we can send the merge request.

fdroid build -v -l com.eventyay.attendee



  1. Quick Start: https://gitlab.com/fdroid/fdroiddata/blob/master/README.md#quickstart
  2. Making merge requests: https://gitlab.com/fdroid/fdroiddata/blob/master/CONTRIBUTING.md#merge-requests


Continue ReadingBuilding Open Event Android for F-Droid

Implement Charges Endpoint in the Open Event Android App

In the Open Event Android App we first create an attendee and then the order when a user tries to buy a ticket but the end user will only know that the transaction is successful when money gets deducted from his account. This is where we use the charges endpoint of the Open Event Server to complete the payment. Let’s see how this is being done in the Open Event Android App.

We are sending a POST request from the app to the following endpoint. The order identifier is added in the url to uniquely identify the order for which we are charging the user. The body of the POST request contains the Charge object with the required information.

fun chargeOrder(@Path("orderIdentifier") orderIdentifier: String, @Body charge: Charge): Single<Charge>


This is how the model class Charge looks like. We specify the type at the top. Then we can either send a stripe or paypal token to the server which contains all the details of the payment. Message and status fields are returned from the server after the getting a success response. The status is true if the payment has been successfully made otherwise it is false. The message field gives us the feedback about what kind of error we got if the payment was not successful otherwise we get a success response.

data class Charge(
val id: Int,
val stripe: String? = null,
val paypal: String? = null,
val message: String? = null,
val status: Boolean? = null


This is how we send the stripe token to the server. If the card details are correct then we receive the stripe token in the onSuccess method and send it to the server using the Charges endpoint.

override fun onSuccess(token: Token) {
//Send this token to server
val charge = Charge(attendeeFragmentViewModel.getId().toInt(), token.id, null)



This is the function that is being used to send the POST request. It takes a charge object as an argument and then we use that in the chargeOrder method. The orderIdentifier variable that we see in the chargeOrder function is a lateinit variable that gets initialized when an order has been returned from the server. When this request is being made in the background thread we show user the progress bar. When we receive a success response from the server, we update the value of the message variable with the value returned from the server and show it to the user. If the value of the status is true that means that the payment has been successfully completed.

fun completeOrder(charge: Charge) {
compositeDisposable.add(orderService.chargeOrder(orderIdentifier.toString(), charge)
.doOnSubscribe {
progress.value = true
}.doFinally {
progress.value = false
message.value = it.message
paymentCompleted.value = it.status

if (it.status != null && it.status) {
Timber.d("Successfully charged for the order!")
} else {
Timber.d("Failed charging the user")
}, {
message.value = "Payment not completed!"
Timber.d(it, "Failed charging the user")


  1. ReactiveX official documentation: http://reactivex.io/
  2. Vogella RxJava 2 – Tutorial: http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial: https://www.androidhive.info/RxJava/
Continue ReadingImplement Charges Endpoint in the Open Event Android App

Ticket Details in the Open Event Android App

It is important to show the user the ticket details before he makes the payment so that he is aware how much does each ticket costs. Let’s see how this is being done in the Open Event Android App.

This function will query the database with a list of ids and return the tickets with the matching ids.

@Query("SELECT * from Ticket WHERE id in (:ids)")
fun getTicketsWithIds(ids: List<Int>): Single<List<Ticket>>


To use the above function we first need the ids of the tickets that needs to be shown so we loop through the ticketIdAndQty variable and everytime the quantity is greater than 0 we add the id of the ticket to the array list.

val ticketIds = ArrayList<Int>()
ticketIdAndQty?.forEach {
if (it.second > 0) {


The next step is to call getTicketsWithIds function in a background thread because it is a database operation and we don’t make calls to the database in the main thread. If the call to the database is successful we store the list of tickets in a variable otherwise an error message is shown to the user.

tickets.value = it
}, {
Timber.e(it, "Error Loading tickets!")


To display the values to the user we just have to use the ticket variable and use its various fields but subtotal is not present in the variable so we have to calculate it. We just multiply the price of the ticket  with its quantity. So we pass the adapter position in the qty array to get the quantity of the current ticket and multiply it with the price.

val subTotal: Float? = ticket.price?.toFloat()?.times(qty[adapterPosition])


Ticket details are only shown if the user clicks on the textview which says view so we adjust the visibility of the ticket details in this simple if else statements and also update the textview value accordingly.

if (rootView.view.text == "(view)") {
rootView.ticketDetails.visibility = View.VISIBLE
rootView.view.text = "(hide)"
} else {
rootView.ticketDetails.visibility = View.GONE
rootView.view.text = "(view)"



  1. Room official documentation :https://developer.android.com/topic/libraries/architecture/room
  2. Vogella RxJava 2 – Tutorial : http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial : https://www.androidhive.info/RxJava/
Continue ReadingTicket Details in the Open Event Android App

Tickets fragment in the Open Event Android App

Ticketing is one of the most important part of any event management system. In the Open Event Android App you can view all the ticket details of any event. Let’s see how this was accomplished.

This is how our TicketApi class looks. We are sending a GET request to get the list of all tickets associated with an event. Id here is the event id which is used to uniquely identify an event.

interface TicketApi {

fun getTickets(@Path("id") id: Long): Flowable<List<Ticket>>



In the ticket details screen we see the event details on top. We are loading these details from the event which is stored in the database, we use the event id to query the database and load these details.

This is how the events are loaded from the database. We are observing the live data variable event and calling the loadEventDetails methods as soon as the value of the variable changes.

ticketsViewModel.event.observe(this, Observer {
it?.let { loadEventDetails(it) }



Let’s have a look at the loadEvent function that is there in our ViewModel. It only requires one parameter that is our event id. We first check if the id is correct or not then load the events in a background thread and report the error if there is any.

fun loadEvent(id: Long) {
if (id.equals(-1)) {
throw IllegalStateException("ID should never be -1")
event.value = it
}, {
Timber.e(it, "Error fetching event %d", id)
error.value = "Error fetching event"


The following function is used to load the event details with the date formatted in the appropriate manner. We first use the function getLocalizedDateTime to get the localized date in string and then format the date according to our needs.

private fun loadEventDetails(event: Event) {
rootView.eventName.text = event.name
rootView.organizerName.text = "by ${event.organizerName.nullToEmpty()}"
val dateString = StringBuilder()
val startsAt = EventUtils.getLocalizedDateTime(event.startsAt)
val endsAt = EventUtils.getLocalizedDateTime(event.endsAt)
rootView.time.text = dateString.append(EventUtils.getFormattedDate(startsAt))
.append(" - ")
.append(" • ")


That’s all that is needed to make the tickets screen. This how the fragment looks in the app.


  1. ReactiveX official documentation : http://reactivex.io/
  2. Vogella RxJava 2 – Tutorial : http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial : https://www.androidhive.info/RxJava/
Continue ReadingTickets fragment in the Open Event Android App

See events according to location in Open Event Android

In the Open Event Android App until now we were just showing a list of events from the server randomly. This does not makes sense because users are interested in events near them. So it was decided to show events according to a given location in the app. Let’s see how we achieved this.

API endpoint

The API endpoint which we are using looks something like this


This endpoint will return all the events that are happening in India. You can try to copy the above URL in your browser and see the output yourself.

Android Implementation

This function is used to return a list of events which are happening in the location specified by the user. It also saves these events in the local database of the app.

fun getEventsByLocation(locationName: String): Single<List<Event>> {
return eventApi.searchEvents("name", locationName).map {


The following piece of code gets called when the user clicks on the submit button in the soft keyboard. If the query is not empty then we store the value of the location in a variable and use it to load events according to location.

eventsViewModel.locationName = rootView.locationEdittext.text.toString()
return@OnEditorActionListener true


We are using a function named loadLocationEvents() above. Let’s have a look what this function is doing. We are using the location name that the user provides in a constant called query then we are sending a GET request to the server to get all events that are happening in that location name. All these networking request are happening in a background thread. As soon as we start the network operation we set the value of progress to true, this will show the progress bar to the user and as soon as the network request has been completed we remove the progress bar.

fun loadLocationEvents() {
val query = "[{\"name\":\"location-name\",\"op\":\"ilike\",\"val\":\"%$locationName%\"}]"

progress.value = true
progress.value = false
events.value = it
}, {
Timber.e(it, "Error fetching events")
error.value = "Error fetching events"


That’s it. We can now enter a name of the location and we will get a list of all the events that happening there


  1. ReactiveX official documentation : http://reactivex.io/
  2. Vogella RxJava 2 – Tutorial : http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial : https://www.androidhive.info/RxJava/
Continue ReadingSee events according to location in Open Event Android

Searching Events in Open Event Android

In the Open Event Android App searching events from the list of hundreds of events that are stored in the server is indeed an important task. As in the future there will be more events and the user might not be able to see a particular event that he might want to attend. So let’s see how the searching functionality was implemented in the app.

API endpoint?

The API endpoint which we are using looks something like this

https://open-event-api-dev.herokuapp.com/v1/events?sort=name&filter=[{“name”:”name”,”op”:”ilike”,”val”:”%Test Event%”}]

This endpoint returns all the events whose “name” field contains the words “Test Event” in ascending order and since the “ilike” operator is used the results are not case sensitive.

Android Implementation

The following lines are used to send a GET request to the server. We have two query parameters that is sort and filter. They are required so that we can get the list of events in ascending order and filter out the correct events. The function returns a list of events.

fun searchEvents(@Query("sort") sort: String, @Query("filter") eventName: String): Single<List<Event>>


Now this is where the magic happens. This function returns the list of events that have the same name as the user has searched and stores it in the database. So all these events are stored offline as well.

fun getSearchEvents(eventName: String): Single<List<Event>> {
return eventApi.searchEvents("name", eventName)
.map {


We are using a SearchView to search the events. What happens here is that the user searches somethings and presses search, onQueryTextSubmit method gets called and it passes the search query to the ViewModel and loads the events.

searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
//Do your search
searchViewModel.searchEvent = query
loadEventsAgain = true
return false


To add the search icon in the toolbar we just need to add the following code in the XML file. We specify the id of the searchView so that we can reference it in our code. The value of showAsAction is “always” which means the searchView icon will always be visible in the toolbar.

<group android:id="@+id/search_menu">
app:showAsAction="always" />


That’s all that we require to search events in the app.


  1. SearchView Official Android Documentation https://developer.android.com/training/search/setup
  2. ViewModel Official Android Documentation https://developer.android.com/topic/libraries/architecture/viewmodel
  3. Retrofit Official Documentation  http://square.github.io/retrofit/
Continue ReadingSearching Events in Open Event Android

Offline support for Open Event Android with ROOM

So until now we were fetching data from the server and directly displaying it to the user in the Open Event Android app. There are several problems in this approach if the user changes the fragment and then returns to the same fragment he will have to fetch the data again, valuable network gets wasted. There is also no offline support. So we decided to introduce a local database in the app. ROOM was without a doubt our first choice

What is ROOM?

According to the official documentation,

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

The library helps you create a cache of your app’s data on a device that’s running your app. This cache, which serves as your app’s single source of truth, allows users to view a consistent copy of key information within your app, regardless of whether users have an internet connection.

Let’s get started

Integration of the ROOM database majorly consists of 3 steps

1 Create an entity

2 Create a DAO

3 Create a Database

That’s it, you are done !

Create the entity

There are just 2 requirements in order to make a model an entity

First use @Entity annotation on the model class to make it an entity. Secondly you need at least one field with @PrimaryKey annotation.

This entity class represents a table in database and all its fields are the columns of the table.

data class Event(
val id: Long,
val name: String,
val identifier: String,
val startsAt: String)

Create DAO

Data Access Object or DAO for short are used to tell the database how to to put the data.

We can use the following annotations to perform simple SQL queries in ROOM

@Insert, @Update, @Delete for proper actions: inserting, updating and deleting records

@Query for creating queries — we can make select from the database

interface EventDao {
@Insert(onConflict = REPLACE)
fun insertEvents(events: List<Event>)

@Insert(onConflict = REPLACE)
fun insertEvent(event: Event)

@Query("DELETE FROM Event")
fun deleteAll()

@Query("SELECT * from Event ORDER BY startsAt DESC")
fun getAllEvents(): Flowable<List<Event>>

@Query("SELECT * from Event WHERE id = :id")
fun getEvent(id: Long): Flowable<Event>

Create the Database

We need to create a public abstract class that extends RoomDatabase

After that annotate the class to be a Room database, declare the entities that belong in the database and set the version number. Listing the entities will create tables in the database.

Define the DAOs that work with the database. Make the database a singleton to prevent having multiple instances of the database opened at the same time. A lot has been said let’s have a look at the code now.

@Database(entities = [Event::class, User::class], version = 1)
abstract class OpenEventDatabase : RoomDatabase() {

abstract fun eventDao(): EventDao

abstract fun userDao(): UserDao
OpenEventDatabase::class.java, "open_event_database")

The important thing here is that all operations must be done in the background thread. You can do it by using AsyncTask, Handler, RxJava or anything else.


  1. Room Official Documentation : https://developer.android.com/topic/libraries/architecture/room
  2. Google Code Lab :https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0
Continue ReadingOffline support for Open Event Android with ROOM

Generating a Stripe token in the Open Event Android App

To implement the payment functionality in the Open Event Android App using credit cards we are using Stripe. Let’s see how this is being done.

We are taking the sensitive information about the user like the card details and sending it to Stripe’s servers which will return a token encrypting users information which we will use to send it to the Open Event Server to complete the payment.

We need to add the library in the build.gradle file in the dependency block.

implementation 'com.stripe:stripe-android:6.1.2'


Next we add Stripe’s Card Input Widget in our layout file. This widget is used to input card details like the card number, expiry date and CVC. It automatically validates the card details. It has a minimum width of 320 px. By default we are setting it’s visibility to gone that is it won’t be visible unless the user selects the payment option.

android:visibility="gone" />


The visibility of the input field for card details is determined here. We want to show the Input widget only when the user selects Stripe as the mode of payment.

override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
selectedPaymentOption = paymentOptions[p2]
if (selectedPaymentOption == "Stripe")
rootView.cardInputWidget.visibility = View.VISIBLE
rootView.cardInputWidget.visibility = View.GONE


Next we store the user’s card details in a variable. If any of the details be it card number, expiry date or CVC isn’t correct, the value of the variable becomes null and then we show a message to the user.

val cardDetails: Card? = cardInputWidget.card

if (cardDetails == null)
Toast.makeText(context, "Invalid card data", Toast.LENGTH_LONG).show()


This is the most important part where we receive the Stripe token. We are sending a request in a background thread to the Stripe servers using the Stripe API_KEY. In the onSuccess method we receive the token if everything went successfully while in the onError method we display the errors to the user.

cardDetails?.let {
context?.let { contextIt ->
object : TokenCallback {
override fun onSuccess(token: Token) {
//Send this token to server
Toast.makeText(context, "Token received from Stripe", Toast.LENGTH_LONG).show()

override fun onError(error: Exception) {
Toast.makeText(context, error.localizedMessage.toString(), Toast.LENGTH_LONG).show()



  1. Stripe Android documentation: https://stripe.com/docs/mobile/android  
  2. Stripe Documentation: https://stripe.com/docs/quickstart
  3. Stripe Android Github: https://github.com/stripe/stripe-android
Continue ReadingGenerating a Stripe token in the Open Event Android App