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

Continue ReadingImplementing a TypeConverter in the Open Event Android Project

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

Continue ReadingTransform an avatar into a Circular One

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

Continue ReadingUsing RxAndroid to implement Signing up in the Open Event Android Application

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

Continue ReadingHandling Configuration Changes by implementing ViewModels in the Open Event Android App

Implementing Favorite Event option in the Toolbar of Event Details in the Open Event Android App

The Open Event Android app allows users to favorite any Event. The users can easily see their favorited events in a section called Favorites. This blog will illustrate about how favorite event option has been implemented in the Toolbar of Event Details section of the Open Event Android app.

We will straightaway move onto the code of event_details.xml. We can clearly observe that we need to implement a menu item in the toolbar or action bar. So, to implement that, we first need to add a favorite icon in the support actionbar. To do that, we add a menu item in the event_details.xml file present under the menu folder.

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <group android:id="@+id/event_menu">
      ...
  </group>
  
  <item
      android:id="@+id/eventShare"
      android:icon="@drawable/ic_share_white_24dp"
      android:title="@string/event_share"
      app:showAsAction="always"/>

  <item
      android:id="@+id/favoriteEvent"
      android:icon="@drawable/ic_baseline_favorite_border_white_24px"
      android:title="@string/favorite_event"
      app:showAsAction="always"/>
</menu>

We can clearly observe that a menu item having an id of favoriteEvent has been added. We create a vector drawable with a heart shaped having only border. We also create a vector drawable of heart shaped filled with white colour. android:icon is set to the border icon as default.

We have made our desired changes in the xml files. Now we will head onto the code of EventDetailsViewModel. Basically, we want the user to be able to favorite the event. So we add the function setFavoriteEvent to the ViewModel.

fun setFavorite(eventId: Long, favourite: Boolean) {
compositeDisposable.add(eventService.setFavorite(eventId, favourite)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
               Timber.d("Success")
}, {
               Timber.e(it, "Error")
               error.value = "Error"
}))
}

This function utilises eventId, which is of long type and favourite, which is of Boolean type. This function makes an RxAndroid network call to set favorite as false or true to the event having the eventId. Heading onto the code in EventDetailsFragment. In the onOptionsItemSelected function, we add the favoriteEvent item case in the switch statement.

R.id.favoriteEvent -> {
eventViewModel.setFavorite(eventId, !(eventShare.favorite))
if (eventShare.favorite) {
setFavoriteIcon(R.drawable.ic_baseline_favorite_border_white_24px)
} else {
setFavoriteIcon(R.drawable.ic_baseline_favorite_white_24px)
}
return true

}

We clearly observe that we are setting the eventId and boolean fields in the .setFavorite function. There is a function called setFavoriteIcon, present in a conditional which sets the appropriate favorite icon.

Let us head onto the code of the function setFavoriteIcon

private fun setFavoriteIcon(id: Int){
menuActionBar?.findItem(R.id.favoriteEvent)?.icon = context?.let { ContextCompat.getDrawable(it, id) }
}

We observe that the code is quite straight forward, We just replace the menu item icon of id favoriteEvent with id. We add a conditional in the ViewModel observer of event. This conditional helps in setting the correct favorite image icon even after configuration changes.

eventViewModel.event.observe(this, Observer {
if (eventShare.favorite) {
setFavoriteIcon(R.drawable.ic_baseline_favorite_white_24px)
}
})

This is how we have successfully implemented a Favorite Event option in the Toolbar of EventDetailsFragment.

Additional Resources

Continue ReadingImplementing Favorite Event option in the Toolbar of Event Details in the Open Event Android App