Integrate static maps API in an android project

This blog article will illustrate about how an event is located on a map in the Open Event Android app. The app found it necessary to have a feature where users will be able to view the location of the event they are interested in. So we, at Open Event Android took a different approach to map out the location. Let us delve into that !

Google provides us with two ways to map out a location in an android app. One requires us to implement the GoogleMaps Sdk, which is a quite a tiresome process. But the other is a quite less popular way and it doesn’t receive enough recognition. It is the static maps API approach.

This API by Google had been released quite a few years ago and is seemingly one of the most useful and interesting APIs among all of them. So the question is, how does this fit in ?

It is a simple API to use. We provide latitude and longitude of a place. The API then sends us an image response which shows us exactly where the place is located in a world map. Quite Unique!

Let us run through the steps about how it is implemented in the Open Event Android app.

The project uses Picasso to load images as it has a cache feature, which basically means it stores the image as long as the app is running. To start with, the code below shows how to load an image.

//load image

Picasso.get()

      .load(loadMap(event))

      .placeholder(R.drawable.ic_map_black_24dp)

      .into(rootView.image_map)

This will seem to a bit complex at first, but lets just head into it steadily.

.load() function is used to load an image from a given URL. .placeholder() is used to provide the view with a placeholder image if the image hasn’t been loaded properly. .into() is used to load the image ( or the placeholder ) into the view.

We can see that in the .load() function, we are using another function .loadMap(). Let us go through the .loadMap() function

fun loadMap(event: Event): String{

  //location handling

  val mapUrlInitial = “https://maps.googleapis.com/maps/api/staticmap?center=”

  val mapUrlProperties = “&zoom=12&size=1200×390&markers=color:red%7C”

  val mapUrlMapType = “&markers=size:mid&maptype=roadmap”
  val latLong: String = “” +event.latitude + “,” + event.longitude
  return mapUrlInitial + latLong + mapUrlProperties + latLong + mapUrlMapType

}

The .loadMap() function has many declared variables. This is the heart of the whole process.

So what is required for the static maps API to give us an image is that we make an http request with a given url, for which an image response (URL) is received. Let us run through the meaning and utility of these variables. Yes, all of them have a completely different meaning!

The mapUrlInitial variable is always the same while making an API call. It has a query of center ( ?center ) which specifies that we want the location to be centered in the map.

The mapUrlProperties variable contains a string where you control the actual zooming of the image response you will get, the size ofthe image and the color of the marker which will point out our place.

The mapUrlMapType variable is a string where you can actually determine the marker size you want and the type of the map. We are using a roadtype map in the app.

Finally latLong is a string which concatenates the latitude and the longitude of the place we want to pinpoint!

We then concatenate all of these strings to form a feasible Url. The Url is then loaded as we have seen above, in the Picasso code. One thing we can notice is that an event object is always required for all of this to happen, because we are able to fetch the position details using the event object! Final Code:-

fun loadMap(event: Event): String{

  //location handling

  val mapUrlInitial = “https://maps.googleapis.com/maps/api/staticmap?center=”

  val mapUrlProperties = “&zoom=12&size=1200×390&markers=color:red%7C”

  val mapUrlMapType = “&markers=size:mid&maptype=roadmap”
  val latLong: String = “” +event.latitude + “,” + event.longitude
  return mapUrlInitial + latLong + mapUrlProperties + latLong + mapUrlMapType

}
//load image

Picasso.get()

      .load(loadMap(event))

      .placeholder(R.drawable.ic_map_black_24dp)

      .into(rootView.image_map)

After all this doing, we are finally able to receive the static image perfectly.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, Static Maps API

Implementing Custom Tab Viewing on click of an Organizer link in Open Event Android

The Open Event Android app shows a list of organizer social links in the Event Details section. The API fetches the type of social link and the link to the page. For example, if a facebook icon based link is clicked, the default browser will open the facebook page of the Organizer. Our aim was to open the page in the app itself so that the user won’t have to juggle between two different apps. This blog will thus illustrate about how the implementation of a CustomTab is done.

We will move onto the SocialLinkViewHolder file. There we will remove the code for which an explicit intent used to be fired everytime a SocialLink was clicked. We initially add the dependency to the app.gradle file.

implementation “com.android.support:customtabs:27.1.1”

We can clearly observe that custom tabs is supported by the Support library of Android.

We add another field in SocialLinksViewHolder model class. This field will be a Context type field. context will be required in future steps.

class SocialLinksViewHolder(itemView: View, private var context: Context) : RecyclerView.ViewHolder(itemView) {

}

Our next step is to change the overridden function  onCreateViewHolder in the the SocialLinks RecyclerAdapter class. We will now return both the view and the parent context in the method. We return parent context, as it is required in the ViewHolder.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SocialLinksViewHolder {

  val view = LayoutInflater.from(parent.context).inflate(R.layout.item_social_link, parent, false)

  return SocialLinksViewHolder(view, parent.context)

}

We now declare the function showCustomTab() in the bind function of the SocialLinkViewHolder class. Error will be resolved as we still haven’t initialised the function showCustomTab(). This function will be fired only when the link is clicked. That’s why we are firing the function in the onClickListener of itemView

fun bind(socialLink: SocialLink) {

 …

 …
  itemView.setOnClickListener{

      setUpCustomTab(context, socialLink.link)

  }

}

Heading onto initialising the function showCustomTab(). The below code represents the function.

private fun setUpCustomTab(context: Context, url: String) {

  var finalUrl = url

  if (!url.startsWith(“http://”) && !url.startsWith(“https://”)) {

      finalUrl = “http://$url

  }
  CustomTabsIntent.Builder()

          .setToolbarColor(ContextCompat.getColor(context, R.color.colorPrimaryDark))

          .setCloseButtonIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_arrow_back_white_cct_24dp))

          .setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left)

          .setExitAnimations(context, R.anim.slide_in_left, R.anim.slide_out_right)

          .build()

          .launchUrl(context, Uri.parse(finalUrl))

}

Breaking the function into two parts. Firstly, we are verifying the validity of the Url. We are appending http:// to the Url if the Url is not present like that. We skip it, if it is indeed a valid Url

var finalUrl = url

  if (!url.startsWith(“http://”) && !url.startsWith(“https://”)) {

      finalUrl = “http://$url

  }

Then, comes the actual part of the CustomTabsIntent builder. We create a Chrome CustomTab by setting properties regarding toolbar colour, close-button icon, starting and ending animations!

  CustomTabsIntent.Builder()

          .setToolbarColor(ContextCompat.getColor(context, R.color.colorPrimaryDark))

          .setCloseButtonIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_arrow_back_white_cct_24dp))

          .setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left)

          .setExitAnimations(context, R.anim.slide_in_left, R.anim.slide_out_right)

          .build()

          .launchUrl(context, Uri.parse(finalUrl))

setToolbarColor is used to set the toolbar colour of the intent. In this case it was our colourPrimaryDark. setCloseButtonIcon() takes a close icon Bitmap as input. setStartAnimations() takes care of the animation which will occur when the Tab is opened.

build will build the CustomTabIntent and launchUrl will launch the TabIntent with the url being finalUrl.

Thus, we can are clearly able to achieve the feat of showing the web page in the app itself using a CustomTab.

Additional Resources

Tags: GSoC18, FOSSASIA, Open Event, Android, CustomTabsIntent

Show number of tickets in Orders Under User Fragment

This blog will illustrate about how we, at Open Event Android, have implemented the way to show the number of tickets of every type of ticket bought by the user in Orders Under User fragment itself!

1. Modify ordersUnderUser function in Order API

The ordersUnderUser function in Order API doesn’t include attendees. So, if we we need to show the number of attendees which is basically the number of tickets, we have to include all the attendee ids.

@GET(“/v1/users/{userId}/orders?filter=[{\”name\”:\”status\”,\”op\”:\”eq\”,\”val\”:\”completed\”}]&include=event,attendees&fields[attendees]=id”)

   fun ordersUnderUser(@Path(“userId”) userId: Long): Single<List<Order>>

1. Modify the orderUnderUser function in viewmodel

Now, we have to modify the OrderViewModel where we will make changes in the orderUnderUser function. We create a LiveData of attendees number which will contain a list of attendee ids received from the order.

val attendeesNumber = MutableLiveData<ArrayList<Int>>()

Then, in the orderUnderUser function, we add a line of code of setting the value of the LiveData.

This basically takes the orders as a list and as List<AttendeeId> is a variable inside the object of Order, we map out the same ArrayList full of attendee ids!

           }.subscribe({

               order = it

               attendeesNumber.value = it.map { it.attendees?.size } as ArrayList<Int>

               val query = buildQuery(it)

               if (idList.size != 0)

                   eventsUnderUser(query)

               else

                   progress.value = false

           }

2. Modify OrdersUnderUserFragment code

We add a observer in the Fragment file. The observer will fire up whenever there is a value received for attendeesNumber. It will fire up the function setAttendeeNumber in ordersRecyclerAdapter.

ordersUnderUserVM.attendeesNumber.observe(this, Observer {

   it?.let {

       ordersRecyclerAdapter.setAttendeeNumber(it)

   }

})

3. Modify OrdersRecyclerAdapter code

We add a variable called attendeesNumber in the file. Another function called setAttendeeNumber is also made to set the Attendee number. We then proceed to bind the number to the view holder.

var attendeesNumber =  ArrayList<Int>()

fun setAttendeeNumber(number: ArrayList<Int>) {

   attendeesNumber = number

}

override fun onBindViewHolder(holder: OrdersViewHolder, position: Int) {

   holder.bind(eventAndOrderIdentifier[position].first, clickListener, eventAndOrderIdentifier[position].second, attendeesNumber[position])

}

4. Modify the ViewHolder of an Order

We create a conditional which thus sets up the message of “See N Tickets” or “See 1 Ticket” based on the ticket number.

if (attendeesNumber == 1) {

   itemView.ticketsNumber.text = “See ${attendeesNumber} Ticket”

} else {

   itemView.ticketsNumber.text = “See ${attendeesNumber} Tickets”

}

Thus, this was how, tickets number viewing was implemented in the app.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, Number of Tickets

Implementing Forgot Password Feature in the Open Event Android app

This blog will illustrate about how forgot password feature is implemented in the Open Event Android app. This feature is quite necessary for users as the user might forget his or her password whenever he needs to login. Thus, this feature utilises only the email of the user. When clicked on “I forgot my password” option, the user is sent an email to reset his/her password!

1. Create the RequestToken, Email and RequestToken response models

The first step is to create all required models. We actually send a POST request to the server and the body of it contains the RequestToken model. The response received is contained in the RequestToken  response model. We create an Email model for our use.

RequestToken class:-

data class RequestToken(val data: Email)

Email class:-

data class Email(val email: String)

RequestTokenResponse class:-

data class RequestTokenResponse(val message: String)

2. Show the option only when correct Email Id is entered.

We create a verification method to verify the email. The app will only send a POST request using a correct email. This method is present in the ViewModel.

fun showForgotPassword(email: String): Boolean {

   if (email.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(email).matches()) {

       return true

   }

   return false

}

2. Update the Login Fragment file.

We add an onclicklistener to the forgot password option. This will call a function in the ViewModel. The function is sendResetPasswordEmail which utilises the actual email of the user entered.

rootView.forgotPassword.setOnClickListener {

   loginActivityViewModel.sendResetPasswordEmail(email.text.toString())

}

3. Update the ViewModel code

We create the sendResetPasswordEmail method. Below code represents that.

fun sendResetPasswordEmail(email: String) {

   compositeDisposable.add(authService.sendResetPasswordEmail(email)

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe {

               progress.value = true

           }.doFinally {

               progress.value = false

           }.subscribe({

               requestTokenSuccess.value = verifyMessage(it.message)

           }, {

               error.value = “Email address not present in server. Please check your email”

           }))

}

3. Create an observer for the requestTokenSuccess variable in the Fragment.

Thus, the final task at hand is to create an observer to observe the vaue of requestTokenSuccess variable. Once the variable is set to a message “Email Sent”, we can clearly notify the user that the email is sent perfectly! (The feature image represents this)

Thus, this is how we have implemented the forgot password feature in the app.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, Forgot Password

Adding Acknowledgements section in the Settings of the app

The Open Event Android app uses innumerable open source softwares. Fortunately, android allows us to give credit to each OSS (Open Source Software). We can do this just by writing a few lines of code. In this blog we will illustrate about how we are showing a list of acknowle- dgements in the Open Event Android app.

First of all we will add all the dependencies required. In the app level gradle file, we will add the plugin.

apply plugin: ‘com.google.gms.oss.licenses.plugin’

We then add the implementation of oss licences in the app level file of build.gradle

implementation ‘com.google.android.gms:play-services-oss-licenses:16.0.0’

After that we add the classpath to the project level gradle file in dependencies.

classpath “com.google.gms:oss-licenses:0.9.2”

The google() function must be added in maven for this to work. So, after adding all these dependencies, we click the Sync button in Android Studio. We wait for the project to refresh.

In our Settings Fragment file, we add another preference called “Acknowledgements”. Let us head into the code below. In the SettingsFragment file.

if (preference?.key == resources.getString(R.string.key_acknowledgements)) {

startActivity(Intent(context, OssLicensesMenuActivity::class.java))

return true

}

As we can see, we have added a conditional in the onPreferenceClickListener. If the user clicks on a preference whose key is “key_acknowledgements”, an OssLicensesMenyActivity opens in the app. This is a predefined activity and is only available after we have successfully integrated oss licences. Heading onto some xml code for the feature. We move onto settings.xml file.

<Preference

android:key=”@string/key_acknowledgements”

android:title=”@string/acknowledgements_settings” />

This section is present under the main PreferenceScreen. It is nested and is actually present under a PreferenceCategory named “About”. The final thing we do to complete the process successfully is store our strings in the strings.xml file.

<string name=”key_acknowledgements”>key_acknowledgements</string>

<string name=”acknowledgements_settings”>Acknowledgements</string>

Thus, this is how we are able to show the user a list of acknowledgements successfully in the Open Event android app.

Additional Resources

Tags: GSoC18, FOSSASIA, Open Event, Android, OSS

Show social links in open event android

This blog will illustrate about how social links are are cached in the Open Event Android Project. We needed social link viewing in the app so that the User is able to reach out to the organizer in a much better way. After that we needed caching to store the social links in our database. Let’s head to the code.

1. Change the social link model

The first step is to change the social link model. We are caching social links using the Room Database. So, we need to add the annotation @Entity to the top of the model to represent the model as a table of type SocialLink in the database. We then add the annotation @PrimaryKey to the id field as every table in a room database requires a primary key for reference.

Social link model code :-

@Type(“social-link”)

@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class)

@Entity

data class SocialLink(

       @Id(IntegerIdHandler::class)

       @PrimaryKey

       val id: Int,

       val link: String,

       val name: String

)

2. Create the SocialLinksDao file

This file lists all the possible methods by which the entity in a database can be reached. These methods help in fetching information from the database, as well as updating information in the database. We implement three methods, insertSocialLinks(), deleteAll() and getAllSocialLinks(). insertSocialLinks() helps us in putting social links in the database. deleteAll() helps in deleting all social links from the database. getAllSocialLinks() helps in fetching all social links of a specific eventId

@Dao

interface SocialLinksDao {

   @Insert(onConflict = OnConflictStrategy.REPLACE)

   fun insertSocialLinks(socialLinks: List<SocialLink>)

   @Query(“DELETE FROM SocialLink”)

   fun deleteAll()

   @Query(“SELECT * from SocialLink WHERE event = :eventId”)

   fun getAllSocialLinks(eventId: Long): Flowable<List<SocialLink>>

}

We receive Social Links in the form of a Flowable while fetching from database. As Flowable cannot handle errors correctly, doOnFinally() will not work and thus doOnNext() is used in the SocialLinksViewModel file.

fun loadSocialLinks(id: Long) {

   compositeDisposable.add(socialLinksService.getSocialLinks(id)

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe({

               progress.value = true

           }).doOnNext({

               progress.value = false

           }).subscribe({

               socialLinks.value = it

           }, {

               Timber.e(it, “Error fetching Social Links”)

               error.value = “Error fetching Social Links”

           }))

}

3. Change the SocialLinksService file for storing social links in Database

We change the SocialLinksService file. We change it in such a way that social links are fetched from the server, only when they don’t exist in the database ( for a particular eventId )

fun getSocialLinks(id: Long): Flowable<List<SocialLink>> {

  //fetch social links

   val socialFlowable = socialLinksDao.getAllSocialLinks(id)

   return socialFlowable.switchMap {

       if (it.isNotEmpty())

           socialFlowable

       else

           socialLinkApi.getSocialLinks(id)

                   .map {

                       socialLinksDao.insertSocialLinks(it)

                   }

                   .flatMap {

                       socialFlowable

                   }

   }

}

We will go through the code in steps. First of all, we declare a variable, socialFlowable, which is assigned a value of a Flowable<List<SocialLinks>> fetched from the database. The getAllSocialLinks(id) takes eventId as parameter. Secondly, we assign a switchMap which is used to return the value of socialFlowable if it is not empty. So, if there have been some social links fetched from the database, we just return them without even making a network call.

If socialFlowable is actually empty, the else part of the conditional takes care of the network call. getSocialLinks(id) return a Flowable<List<SocialLinks>> object. .map is used to insert all the fetched social links. .flatMap is used to return socialFlowable to the function after the process.

4. Modify the network call in SocialLinkApi file

SocialLinks have relationship with event. To store them in the database, we distinguish each of the SocialLinks using eventId. The eventId will not be fetched in the regular network call, so we modify it.

interface SocialLinkApi {

   @GET(“events/{id}/social-links?include=event&fields[event]=id&page[size]=0”)

   fun getSocialLinks(@Path(“id”) id: Long): Flowable<List<SocialLink>>

}

We thus include event relationship. The JSON response will now contain a an event model inside relationships. The event model will have its id and type (event).

4. Create EventId model

This model will contain the eventId. The model will be of type event.

@Type(“event”)

data class EventId(

       @Id(LongIdHandler::class)

       val id: Long

)

4. Modify the SocialLink model to include relationship with event

We modify the model to include event relationship. As we need to store just the id of the event, we create another class named EventId and use a TypeConverter to convert the EventId model to a primitive type Long while compilation. We also include a foreign key which references the id of the event with the event field in the SociaLink model.

@Type(“social-link”)

@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class)

@Entity(foreignKeys = [(ForeignKey(entity = Event::class, parentColumns = [“id”], childColumns = [“event”], onDelete = CASCADE))])

data class SocialLink(

       @Id(IntegerIdHandler::class)

       @PrimaryKey

       val id: Int,

       val link: String,

       val name: String,

       @ColumnInfo(index = true)

       @Relationship(“event”)

       var event: EventId? = null

)

4. Create the TypeConverter

We create the TypeConverter for converting EventId to just the id (Long).

class EventIdConverter {

   @TypeConverter

   fun fromEventId(eventId: EventId): Long{

       return eventId.id

   }

   @TypeConverter

   fun toEventId(id: Long): EventId{

       return EventId(id)

   }

}

4. Modify the Modules.kt file and the OpenEventDatabase

In the OpenEventDatabase file, we include the SocialLink entity to the Database. The annotation @TypeConverters is used to specify the type of TypeConverter to be used. Here we use EventIdConverter. In the abstract class, we include a function socialLinksDao() of type SocialLinksDao. It will fetch all the available functions from the file.

@Database(entities = [Event::class, User::class, SocialLink::class, Ticket::class], version = 1)

@TypeConverters(EventIdConverter::class)

abstract class OpenEventDatabase : RoomDatabase() {

   abstract fun eventDao(): EventDao

   abstract fun userDao(): UserDao

   abstract fun ticketsDao(): TicketsDao

   abstract fun socialLinksDao(): SocialLinksDao

}

In the Modules.kt file we add EventId class to the JSONAPIConverterfactory.

Retrofit.Builder()

       .client(get())

       .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

       .addConverterFactory(JSONAPIConverterFactory(objectMapper, Event::class.java, User::class.java, SignUp::class.java, Ticket::class.java, SocialLink::class.java, EventId::class.java))

       .addConverterFactory(JacksonConverterFactory.create(objectMapper))

       .baseUrl(baseUrl)

       .build()

In the same file, we add another factory object. This is about the fetching of the function of socialLinksDao in the OpenEventDatabase file.

factory {

   val database: OpenEventDatabase = get()

   database.socialLinksDao()

}

Thus, after all the modifications, we are able to achieve the feat of caching of social links in the app successfully!

References

Tags: GSoC18, FOSSASIA, Open Event, Android, SocialLinks Caching

Implementing a TypeConverter in the Open Event Android Project

In the Open Event Android project, we fetch events from the server. After fetching them from the server, we implement caching by inserting them into the Room Database. The problem with Room Database is that it can only have primitive fields in the model! You can’t have an object Person() directly inserted to the database. As an example, it can only take input of a integer or a String.

But it can be a case sometimes, when we want a Person() object to be stored. This is where a TypeConverter comes in. What it basically does is that, it takes an input of a non-primitive field, and then converts it into a required primitive one. Let’s just go through it.

In the OpenEvent Android project we need to fetch social links. Social links have a relationship with the event related. So we are actually fetching two models, one is the SocialLink and another one is EventId. The SocialLink model is present below:-

@Type(“social-link”)

@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class)

@Entity(foreignKeys = [(ForeignKey(entity = Event::class, parentColumns = [“id”], childColumns = [“event”], onDelete = CASCADE))])

data class SocialLink(

       @Id(IntegerIdHandler::class)

       @PrimaryKey

       val id: Int,

       val link: String,

       val name: String,

       @ColumnInfo(index = true)

       @Relationship(“event”)

       var event: EventId? = null

)

We can clearly observe that the model has primitive fields such as id, link and name. But now we have another field of the object EventId, which is not primitive. Lets observe the model of EventId:-

@Type(“event”)

data class EventId(

       @Id(LongIdHandler::class)

       val id: Long

)

This model has only one primitive field, that is id. So our motive is to insert the object EventId() into the RoomDatabase. This is where a TypeConverter comes in.

A TypeConverter must have methods which convert the object into a required primitive field, and another one which initialises a new object with the field. Let’s see our TypeConverter:-

class EventIdConverter {

   @TypeConverter

   fun fromEventId(eventId: EventId): Long{

       return eventId.id

   }

   @TypeConverter

   fun toEventId(id: Long): EventId{

       return EventId(id)

   }

}

@TypeConverter annotation belongs to Room. The function fromEventId() specifies that it returns the id of the object EventId. The function toEventId() specifies that it makes a new EventId object using the field id.

We see that we are just returning the id of the EventId object. Why did we need all of this then? We could have easily just used a long eventId in the SocialLink model instead of the object Eventid!

The reason is simple. If we clearly observe both the models, we can see that @Type annotations are used. The thing is, the JSON request which is captured, returns us a model where we have nested models. Let us observe the JSON.

   {

                “type”: “social-link”,

                “id”: “1”,

                “relationships”: {

                    “event”: {

                              “links”: {

                                       “self”: “/v1/social-links/1/relationships/event”,

                                       “related”: “/v1/social-links/1/event”

                              },

                              “data”: {

                                       “type”: “event”,

                                       “id”: “76”

                              }

                    }

                },

                “attributes”: {

                    “link”: “https://www.facebook.com/eventyay/?ref=br_rs”,

                    “name”: “Facebook Url”

                },

                “links”: {

                    “self”: “/v1/social-links/1”

                }

    },

We clearly observe that there are two models present with two different types. One is the SocialLink in attributes. The other one is EventId in data inside the event field. That one has a

type of “event”. Thus the schema is mapped differently and that is why we are using another object.

We have now made our TypeConverter, how do we implement it?

We just add the annotation @TypeConverters(…) on top of the database of the app.

@Database(entities = [Event::class, User::class, SocialLink::class], version = 1)

@TypeConverters(EventIdConverter::class)

abstract class OpenEventDatabase : RoomDatabase() {

   abstract fun eventDao(): EventDao

   abstract fun userDao(): UserDao

   abstract fun socialLinksDao(): SocialLinksDao

}

There we have it, we have successfully implemented a TypeConverter. After compilation, we observe the schema of the generated database. ( A json file )

“fields”: [

 {

   “fieldPath”: “id”,

   “columnName”: “id”,

   “affinity”: “INTEGER”,

   “notNull”: true

 },

 {

   “fieldPath”: “link”,

   “columnName”: “link”,

   “affinity”: “TEXT”,

   “notNull”: true

 },

 {

   “fieldPath”: “name”,

   “columnName”: “name”,

   “affinity”: “TEXT”,

   “notNull”: true

 },

 {

   “fieldPath”: “event”,

   “columnName”: “event”,

   “affinity”: “INTEGER”,

   “notNull”: false

 }

We observe that the field event has been created with the type of an integer! Thus, this is the process by which a TypeConverter is successfully implemented in the Open Even Android app.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, TypeConverter

Transform an avatar into a Circular One

This blog article will illustrate us about how to convert an image into a circular shape. We will go through the process of converting a conventional image to a circular cropped one. Nowadays circular images are more preferred than normal ones as they provide us with more finesse and it seriously looks good to the eye. Just for example, see WhatsApp! How would it have looked if the user image would have been in a square conventional shape!

Converting an image into a circular one is quite easy and we at Open Event Android have used a sort of a simple algorithm to convert it. Let us head straight to the code.

We will use the Picasso library to load an image. The library provides us with a transform function which takes input of type of transform we want to do.

The fetching of image code:-

Picasso.get()

       .load(image_url)

       .placeholder(R.drawable.ic_person_black_24dp)

       .transform(CircleTransform())

       .into(rootView.avatar)

As you can see, we are passing a CircleTransform class. Let’s get onto the CircleTransform class then.

It contains methods which are overridden. The first one is where the magic happens. The second one just returns the key value of “circle”.

@Override

public Bitmap transform(Bitmap source) { … }

@Override

public String key() {

   return “circle”;

}

The below code is present in the first method.

int size = Math.min(source.getWidth(), source.getHeight());

int x = (source.getWidth() – size) / 2;

int y = (source.getHeight() – size) / 2;

Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);

if (!squaredBitmap.equals(source)) {

   source.recycle();

}

We initially proceed by declaring a size variable which gets the minimum of the height and width. This is due to the fact that a circle can have only one radius!

We head onto creating a bitmap. Basically we create a new bitmap using the source. The coordinates of starting and ending pixels are provided by x and y. The next two fields are actually width and height, which is size in both cases. ( We want a perfect Circle )

As we have a new bitmap, we can now remove the Bitmap source from memory by calling the recycle function.

Bitmap.Config config = source.getConfig() != null ? source.getConfig() : Bitmap.Config.ARGB_8888;

Bitmap bitmap = Bitmap.createBitmap(size, size, config);

We now create a config variable which fetches the config of the original source. If the source is null, it takes the value of ARGB_8888 which basically means the colour of the bitmap should have four channels Alpha, Red, Green, Blue. Thus a new bitmap is created using that config.

Now we are going to draw on the bitmap. The bitmap has been only configured, but it still is empty. So we create a Canvas and a Paint object ( Basically we are going to paint the squared bitmap on the bitmap! )

Canvas canvas = new Canvas(bitmap);

Paint paint = new Paint();

BitmapShader shader = new BitmapShader(squaredBitmap,

       BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);

paint.setShader(shader);

paint.setAntiAlias(true);

We observe that we use a shader to shade the bitmap. So we create a new BitmapShader object with a squaredBitmap as one of its inputs. The Shader.TileMode.CLAMP constant defines that the edge corners should be used to fill the extra space.

We then set the shader to the setShader method of Paint. Setting antiAlias to true will smooth out edges of what is being drawn. Thus our painting tools are ready.

float r = size / 2f;

canvas.drawCircle(r, r, r, paint);

squaredBitmap.recycle();

return bitmap;

Thus we are using the canvas to paint the bitmap as a circle. The variable r specifies the radius of the circle. .drawCircle is a function which helps us in drawing out a perfect circle. The masterpiece is made using paint.

As the drawing has already been done, we don’t need squaredBitmap to be in memory anymore. Finally we return the Bitmap bitmap. Final Code:-

public class CircleTransform implements Transformation {

   @Override

   public Bitmap transform(Bitmap source) {

       int size = Math.min(source.getWidth(), source.getHeight());

       int x = (source.getWidth() – size) / 2;

       int y = (source.getHeight() – size) / 2;

       Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);

       if (!squaredBitmap.equals(source)) {

           source.recycle();

       }

       Bitmap.Config config = source.getConfig() != null ? source.getConfig() :      Bitmap.Config.ARGB_8888;

       Bitmap bitmap = Bitmap.createBitmap(size, size, config);

       Canvas canvas = new Canvas(bitmap);

       Paint paint = new Paint();

       BitmapShader shader = new BitmapShader(squaredBitmap,

               BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);

       paint.setShader(shader);

       paint.setAntiAlias(true);

       float r = size / 2f;

       canvas.drawCircle(r, r, r, paint);

       squaredBitmap.recycle();

       return bitmap;

   }

   @Override

   public String key() {

       return “circle”;

   }

}

That’s all is required to have a perfectly circular image!

References

Tags: GSoC18, FOSSASIA, Open Event, Android, CircularTransform

Using RxAndroid to implement Signing up in the Open Event Android Application

In the Open Event Android Project, we utilise RxAndroid for making network calls. This blog will illustrate about how the process of signing up in the app is done by making a network call using RxAndroid!

In the open event android app, users can sign up in the app, which means that they can create an account and thus their user details can be stored in the server.

The work flow in the app is that at first, the user will be signing up and then he or she will be automatically logged into the app. Thus we make two network calls. One is for signing up and another is for logging in automatically.

Let us proceed to the code of signing up. In this blog we will only handle the backend part. The below code is the model of sign up.

@Type(“user”)

@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class)

data class SignUp(

   @Id(IntegerIdHandler::class)

   var firstName: String? = null,

   var lastName: String? = null,

   var email: String? = null,

   var password: String? = null

)

Next, we create a Sign Up fragment where we setup the onClickListener of the sign up button. The below code represents that.

rootView.signUpButton.setOnClickListener {

   signUp.email = usernameSignUp.text.toString()

   signUp.password = passwordSignUp.text.toString()

   signUp.firstName = firstNameText.text.toString()

   signUp.lastName = lastNameText.text.toString()

   confirmPassword = confirmPasswords.text.toString()

   signUpActivityViewModel.signUp(signUp, confirmPassword)

}

We can clearly observe that we are taking information from fields and putting them inside the object of SignUp. After that, we are calling a function from view model to sign up. Let us have a look at the viewmodel file. The signUp function is present below.

fun signUp(signUp: SignUp, confirmPassword: String) {

   email = signUp.email

   password = signUp.password

   if (hasErrors(email, password, confirmPassword)) return

   compositeDisposable.add(authService.signUp(signUp)

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe {

               progress.value = true

           }.doFinally {

               progress.value = false

           }.subscribe({

               signedUp.value = it

               Timber.d(“Success!”)

           }, {

               error.value = “Unable to SignUp!”

               Timber.d(it, “Failed”)

           }))

}

Here we can clearly observe that we are adding a RxAndroid network call to a composite disposable. This network call is utilising a function present in AuthService. The .subscribeOn represents the thread where the hard work will occur. The .observeOn represents the thread where actual information is to be reflected. Thus, here we can see that once, the user signs up, the value of the MutableLiveData variable is set to the signed up user. After this, we have an observer in the Fragment file, where it calls a function login whenever the signing up is taken care of. Thus both processes occur simultaneously!

References

Tags: GSoC18, FOSSASIA, Open Event, Android, RxJava, RxAndroid, SignUp

Handling Configuration Changes by implementing ViewModels in the Open Event Android App

This blog article will illustrate the use and implementation of a ViewModel in the Open Event Android app. A ViewModel basically handles all the unneeded problems a user faces when there is a configuration change. This article will take the case of implementing a SignUp ViewModel for SignUp Fragment in the app. Configuration change means that if you have your app set at portrait mode at present and suddenly due to involuntary screen rotation, your app is now being presented in landscape mode, or vice-versa.

Before MVVM, if this type of change occurred, we used to use the onSaveInstance method to persist the data we wanted. But after the introduction of MVVM, the process has been simplified.

After the introduction of a class called ViewModel, we can directly handle all configuration changes now. Let us head onto how a ViewModel is used in the Open Event Android app.

What is a ViewModel ?

As per the definition given in the documentation, a ViewModel is a class which is designed to store and manage UI-related data in a lifecycle conscious way. If the system has a drastic change in its UI related component, the data which is being fetched should be stored in some place, like it needs to be persisted so that after the Ui component gets refreshed and formed again, there is no need to fetch the data yet again !

Implementing a ViewModel:

We use ViewModels regularly in our app code. For example, these are used whenever we code for a fragment or an activity. This is because, we need to persist the logic of accessing data, and those functions are present in a view model. The LiveData persists the data and thus configuration changes are handled. Let us see an example of a SignUpViewModel in the app.

Let us head onto the code of SignUpFragmentViewModel.

class SignUpFragmentViewModel(private val authService: AuthService) : ViewModel() {

    …

}

We clearly observe that our ViewModel takes in an input of an AuthService object. Let’s create our LiveData variables! ( We will assume that all this takes place inside the ViewModel class )

val progress = MutableLiveData<Boolean>()

val error = SingleLiveEvent<String>()

val signedUp = MutableLiveData<User>()

val loggedIn = SingleLiveEvent<Boolean>()

Progress variable is of MutableLiveData type. This means, that the progress is stored in the variable and it will persist even after configuration changes, error variable stores the error faced during SignUp (if any). signedUp variable contains the User data being fetched from the server. loggedIn variable is actually a boolean which contains the data whether the user is logged in or not. We declare two more variables for our use.

var email: String? = null

var password: String? = null

Now we head onto some functions to be utilised. The signUp function code is present below.

fun signUp(signUp: SignUp, confirmPassword: String) {

   email = signUp.email

   password = signUp.password

   if (hasErrors(email, password, confirmPassword)) return

   compositeDisposable.add(authService.signUp(signUp)

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe {

               progress.value = true

           }.doFinally {

               progress.value = false

           }.subscribe({

               signedUp.value = it

               Timber.d(“Success!”)

           }, {

               error.value = “Unable to SignUp!”

               Timber.d(it, “Failed”)

           }))

}

This function is utilised in SignUpFragment. The variables email and password store the actual email and password in SignUpFragmentViewModel. We then utilise a RxAndroid call to make a POST request of creating a User in the server. The signedUp variable then stores the User created in the server. If an error is faced, the error message is stored in the LiveData.

Then finally we create the login function, which is actually called once a user has signed up successfully!

fun login(signUp: SignUp) {

   email = signUp.email

   password = signUp.password

   compositeDisposable.add(authService.login(email.nullToEmpty(), password.nullToEmpty())

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe {

               progress.value = true

           }.doFinally {

               progress.value = false

           }.subscribe({

               loggedIn.value = true

               Timber.d(“Success!”)

           }, {

               error.value = “Unable to Login automatically”

               Timber.d(it, “Failed”)

           }))

}

In this function we clearly set the value of LiveData loggedIn to true when the User actually logs in. Thus, we can clearly see the use of a ViewModel in an app. This is how SignUpFragmentViewModel is implemented in the Open Event Android app

Resources

  1. Android Documentation for LiveData
  2. Android Documentation for ViewModel

Tags: GSoC18, FOSSASIA, Open Event, Android, MVVM, ViewModel, LiveData