Fixing Notification Services Across EventYay

In this Blog-Post, I will show you, the fixing of multiple notification messages getting sent on multiple clicks or display of multiple messages for a single response.

What Caused the Problem?

In our Open Event Frontend, We are using Ember Notify Services to inject notification across the web app to show the notifications. Due to improper error handling on the client side sometimes we get multiple notification messages for a single click or for a single action. As shown in this picture.

How Did we Tackle It ?

Since the following issue was long pertaining, I solved the issue by adding a unique ID to each notification injected through the webapp. Since a unique ID was associated with each notification message, A single notification was getting displayed on each and every action irrespective of number of clicks or number of actions called. If an actions caused two notification to be simultaneously triggered, One of them on the basis of ID is suppressed so that a unique and understandable notification get shown.

Code Snippet:

Before the changes:

.then(() => {
          if (state === 'draft') {
            this.notify.success(this.l10n.t('Your event has been published successfully.'));
          } else {
            this.notify.success(this.l10n.t('Your event has been unpublished.'));
          }

After the changes:

this.notify.success(this.isNewInvite ? this.l10n.t('Role Invite sent successfully') : this.l10n.t('Role Invite updated successfully'), {
            id: 'man_role'
          });
        })
        .catch(() => {
          this.notify.error(this.l10n.t('Oops something went wrong. Please try again'), {
            id: 'man_role_error'
          });
        })

Pull Request : Open-Event-Frontend-3438

Issue : Open-Event-Frontend-3437

Tags :

OpenEvent, EventYay, Fossasia, Intern-2k19

Continue Reading

Addition Of Landing Pages For Mobile Apps

In this Blog-Post, I will showcase the landing pages on the Android frontend of Eventyay. As the Eventyay Android App has been launched on Play Store as well as F-Droid, We needed to make sure users know that they can access all the details regarding their event on their phones too. Now EventYay features two landing pages for each of its mobile apps :

EventYay Attendee App 

EventYay Orga App

Addition Of Landing Pages was a 3-Step Procedure, which is stated below :

  1. ) Defining The Needed Routes and Making Space in Footer.
  2. ) Writing the Code and Designing the Layout for the web-page.
  3. ) Writing Integration Tests for both of the pages.

I will explain each one of the processes involved in making Landing Pages for Mobile Apps in brief in this Blog-Post.

1. Defining The Needed Routes and Making Space in Footer.

To add any web-page in the Open-Event Frontend , we need to define its route in the Application Router File. The code snippet which adds them is:-

// app/router.js

router.map(function() {
  this.route('login');
  this.route('register');
  this.route('reset-password');
  this.route('attendee-app');
  this.route('organizer-app');

After successfully defining the routes, We need to create the HandleBars for the corresponding routes. To access those routes we need to create a section on EventYay Footer to show to the users.

The following code will result in showing the links to the landing page in footer-section

2. Writing the Code and Designing the Layout for the web-page.

After successfully defining the routes and links, its time we move on to writing the HandleBars for the above pages. The first design for the page would be the pointing menu offered by Semantic-UI v.2.0, which would describe the current tab.

After the designing of Navigation Menu in the AttendeeApp.hbs, Its time we design the main outlet and the body of the page. The Design of the layout page is based on the Landing Pages Created in Other Platforms Like Event.IO, EventBrite etc. A Mobile View on the right side with the Key Features of The App on the left side

3. Writing Integration Tests for both of the Pages.

Since writing test is the most important part of the Development Lifecycle. I preferred to write Acceptance Test instead of Integration Test or Unit Test, As both of the pages created were very simple in functionality. In Acceptance Test, We would be checking the URL For Both Of The Pages in every case After Login or Without Login.

Test For Attendee App :

test('visiting /landing-attendee-app without login', async function(assert) {
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

  test('visiting /landing-attendee-app with login', async function(assert) {
    await login(assert);
    await visit('/attendee-app');
    assert.equal(currentURL(), '/attendee-app');
  });

Resources

Issue: OpenEventFrontend-2929

Pull Request : OpenEventFrontend-2979

Additional Resources: Routing Guide., Templates in EmberJS.

Tags: OpenEvent, EventYay, Fossasia, Android Apps, Intern-2k19

Continue Reading

Allow Same Discount/Access Code for Multiple Events in the Open Event Server

In this Blog-Post, I will show how to allow the system to create the same Discount/Access Code for multiple events in the Open Event Server.

What was the issue:

The main problem was that the server used to identify the discount code and access code based on the discount code/access code itself, which did not allow multiple events to have the same discount/access codes.

Can you think of a better solution to this?
Yes, we should have been searching for it based on the discount/access code as well as the event they are associated with.

Changing the endpoint:

Now to do so, we want to pass the id of the event as well as discount/access code itself with the endpoint so that we can search the database based on the event_id and the code itself.  

Changes in Discount/Access Code Endpoint:

'/event/<int:discount_event_id>/discount-code/<code>'
'/event/<int:access_event_id>/access-code/<code>'

Change logic for database search:

Now when searching for discount/access code in the database, we need to pass the event_id along with the discount/access code, so that we can get the column of discount/access code associated with that event, even if we have multiple discount/access code with the same name for a different event. 

Changes in Database search logic:

access = db.session.query(AccessCode).filter_by(code=kwargs.get('code')
event_id = kwargs.get('access_event_id')).first()

discount = db.session.query(DiscountCode).filter_by(code=kwargs.get('code'),
event_id = kwargs.get('discount_event_id')).first()

Change endpoint in API docs and update Dredd hooks:

Now that we have changed the endpoint to get a discount/access code, we need to change API docs as well as Dredd hooks to accommodate the change in API docs.

Changes in API docs:

## Get Discount Code Detail using the code [/v1/event/{event_id}/discount-code/{code}]
## Get Access Code Detail using the code [/v1/event/{event_id}/access-code/{code}]

Changes in Dredd Hooks:

In discount code hook:

discount_code.event_id = 1

In access code hook:

event = EventFactoryBasic()
db.session.add(event)
db.session.commit()

Resources:

Link to Issue: fossasia/open-event-server#6027
Link to PR: fossasia/open-event-server#6208

Continue Reading

Creating Custom Widgets in Badge Magic Android

In this blog, we are going to have a look on how I created this badge preview in fossasia/badge-magic-android


What is Canvas?

Canvas is a class in Android that performs 2D drawing of different objects onto the screen. The saying “a blank canvas” is very similar to what a Canvas object is on Android. It is basically, an empty space to draw onto.

Canvas Coordinate System

The coordinate system of the Android canvas starts in the top left corner, where [0,0] represents that point. The y axis is positive downwards, and x axis positive towards the right.


Some basics of Canvas, lets see how we drew this Preview Badge.

The Badge consists of only 2 components:

  1. Rounded Rectangle ( Background )
  2. Normal Rectangles ( LED Lights )

Let’s see how we create rounded rectangles in android. 

// Draw Background
canvas.drawRoundRect(bgBounds, 25f, 25f, bgPaint)

Using drawRoundRect() we can easily create the badge background. 25f specified is the corner radius of the rectangle.

The LED Lights are just drawable resources which are used according to the current state of the LED.

private fun drawLED(condition: Boolean, canvas: Canvas, xValue: Int, yValue: Int) {
   if (condition) {
       ledEnabled.bounds = cells[xValue].list[yValue]
       ledEnabled.draw(canvas)
   } else {
       ledDisabled.bounds = cells[xValue].list[yValue]
       ledDisabled.draw(canvas)
   }
}

This function draws the LED Lights if the condition is satisfied.

When we consider a custom view, we need to consider the changes which occur according. These layout changes are to be controlled and maintained accordingly, Let’s see how we manage the positioning of the led lights for every android device. Spoiler: Simple 10th Grade Maths xD

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
   super.onLayout(changed, left, top, right, bottom)
   val offset = 30
   val singleCell = (right - left - (offset * 3)) / badgeWidth
   val offsetXToAdd: Int = ((((right - offset).toFloat() - (left + offset).toFloat()) - (singleCell * badgeWidth)) / 2).toInt() + 1

   cells = mutableListOf()
   for (i in 0 until badgeHeight) {
       cells.add(Cell())
       for (j in 0 until badgeWidth) {
           cells[i].list.add(Rect(
               (offsetXToAdd * 2) + j * singleCell,
               (offsetXToAdd * 2) + i * singleCell,
               (offsetXToAdd * 2) + j * singleCell + singleCell,
               (offsetXToAdd * 2) + i * singleCell + singleCell
           ))
       }
   }
   bgBounds = RectF((offsetXToAdd).toFloat(), (offsetXToAdd).toFloat(), ((singleCell * badgeWidth) + (offsetXToAdd * 3)).toFloat(), ((singleCell * badgeHeight) + (offsetXToAdd * 3)).toFloat())
}

We create an offset which is nothing but the gap from the screen edge to the badge itself, now we need to have gaps on both sides of the badge and we also leave half the offset inside the badge which is the difference between the badge background and the LED starting point, hence we calculate the value of single cells by: 

val singleCell = (right - left - (offset * 3)) / badgeWidth

We minus the no of pixels on the right of the display to the left, to get the width of the actual screen. Then we minus the padding from the left and right which is offset * 3 . Now we divide it by the number of cells we want in the badge which is the badgeWidth.

Once we have the number of cells, we want to calculate the left, right, top and bottom positions of all the LED. What we now do is loop into the number of LEDs and then multiply the singleLed width with the current position to get the accurate pixels which need to be escaped from the left.

cells = mutableListOf()
   for (i in 0 until badgeHeight) {
       cells.add(Cell())
       for (j in 0 until badgeWidth) {
           cells[i].list.add(Rect(
               (offsetXToAdd * 2) + j * singleCell,
               (offsetXToAdd * 2) + i * singleCell,
               (offsetXToAdd * 2) + j * singleCell + singleCell,
               (offsetXToAdd * 2) + i * singleCell + singleCell
           ))
       }
   }

Now the fun part, we save all of it in a 2D ArrayList to be able to draw it later on.

Conclusion

Working on custom views is very unique. This experience is one of a kind and drawing stuff with basic maths is fun in the first place. Simple equations led me to create a preview which simulates the complete badge in software. 

Ressources

Continue Reading

Retrofit to make API calls in SUSI.AI Android Client

In the SUSI.AI app, I found the extensive use of “Retrofit” to make API calls to the server. While working on a part of the app, I faced some difficulty regarding the implementation of “Retrofit”.  Though I learned and overcame the problems, I realized that others would face a similar problem. So, today I am writing this blog explaining how to implement “Retrofit” using the help of “SUSI.AI” android client.

Working of API Calls:

Android networking or any networking works in the following way:

  • Request— An HTTP request is made to a certain URL, with all the supplied parameters.
  • Response — The request created returns a response, usually in the JSON format.
  • Parse & Store —The JSON returned is parsed and is being used accordingly.

In Android, we use —

  • Okhttp — For creating an HTTP request with all the proper headers
  • Retrofit — For making the request
  • Moshi / GSON — For parsing the JSON data
  • Kotlin Coroutines — For making non-blocking (main thread) network requests.
  • Picasso / Glide— For downloading an image from the internet and setting it into an ImageView.

Obviously, these are just some of the popular libraries but there are others too.

How to use Retrofit?

First of all we need to import certain libraries in the app level gradle file.

Don’t forget to add the following in the manifest file.

Now, create an interface. The API link mentioned in the interface would be used to fetch data from the server.

Now create a class to parse the JSON data.  Here, we have used a Gson Converter and so the JSON response is automatically converted to the respective.

Here Session and Settings are also a data class. These data classes are framed according to the response that we receive.

Now, create a Retrofit Builder with Base URL and GsonConverterFactory. This builder will be useful to make the API calls.

Create a service for Retrofit with your service interface. Then, create a queue which will be used to de-serialize the JSON. 

Resources: 

Documentation: Retrofit

Blog: Retrofit

Tutorial: Retrofit Video Tutorial

SUSI.AI Android App: PlayStore GitHub

Tags:

SUSI.AI Android App, Kotlin, SUSI.AI, FOSSASIA, GSoC, Android, Retrofit, API calls

Continue Reading

Implementation of Planned Actions in SUSI.AI Android Client

What do you understand by “Planned Actions”? Is it something required to do something according to plan? Is it something that needs to be done at a particular time? Yes, it is. “Planned Actions” is a feature present in the SUSI.AI android app that helps to schedule tasks. Yes, you heard it right! It helps to schedule the task. We can schedule some events like “setting an alarm after one minute”, “playing music after one minute”, etc. Such events require scheduling of different events at different point of time.

SUSI.AI is a smart assistant. It answers the queries that the user asks for, play music, etc. Additionally, the project also has a smart speaker part, which is very similar to Google Home. A smart speaker often has the ability to perform certain task other than answering normal queries at a scheduled time. During my GSoC period, I got the opportunity to implement such a feature of scheduling task in the SUSI.AI android app.

What is the scheduling of task or planned action?

Scheduling of task or planned actions is nothing but performing some kind of task at a scheduled time. Let’s consider “set alarm in one minute”. This means an alarm should be set that should ring up after one minute. 

Let’s see how it has been implemented in SUSI.

First of all, we need to have a skill that would respond to the query asked. Consider, the skill “rocket launch”. The response for this skill is:

{
  “query”: “rocket launch”,
  “client_id”: “aG9zdF8xNzIuNjguMTQ2LjE5MF80YTk0OGY4OA==”,
  “query_date”: “2019-08-19T17:31:53.670Z”,
  “answers”: [{
    “data”: [
      {
        “0”: “rocket launch”,
        “token_original”: “rocket”,
        “token_canonical”: “rocket”,
        “token_categorized”: “rocket”,
        “timezoneOffset”: “-330”,
        “language”: “en”
      },
      {
        “0”: “rocket launch”,
        “token_original”: “rocket”,
        “token_canonical”: “rocket”,
        “token_categorized”: “rocket”,
        “timezoneOffset”: “-330”,
        “language”: “en”
      }
    ],
    “metadata”: {“count”: 2},
    “actions”: [
      {
        “language”: “en”,
        “type”: “answer”,
        “expression”: “starting countdown”
      },
      {
        “expression”: “twelve”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 1000,
        “plan_date”: “2019-08-19T17:31:54.679Z”
      },
      {
        “expression”: “eleven”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 2000,
        “plan_date”: “2019-08-19T17:31:55.681Z”
      },
      {
        “expression”: “ten”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 3000,
        “plan_date”: “2019-08-19T17:31:56.681Z”
      },
      {
        “expression”: “nine”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 4000,
        “plan_date”: “2019-08-19T17:31:57.682Z”
      },
      {
        “expression”: “ignition sequence starts”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 5000,
        “plan_date”: “2019-08-19T17:31:58.682Z”
      },
      {
        “expression”: “six”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 7000,
        “plan_date”: “2019-08-19T17:32:00.682Z”
      },
      {
        “expression”: “five”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 8000,
        “plan_date”: “2019-08-19T17:32:01.683Z”
      },
      {
        “expression”: “four”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 9000,
        “plan_date”: “2019-08-19T17:32:02.683Z”
      },
      {
        “expression”: “three”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 10000,
        “plan_date”: “2019-08-19T17:32:03.683Z”
      },
      {
        “expression”: “two”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 11000,
        “plan_date”: “2019-08-19T17:32:04.684Z”
      },
      {
        “expression”: “one”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 12000,
        “plan_date”: “2019-08-19T17:32:05.684Z”
      },
      {
        “expression”: “zero”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 13000,
        “plan_date”: “2019-08-19T17:32:06.684Z”
      },
      {
        “expression”: “all engines running”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 14000,
        “plan_date”: “2019-08-19T17:32:07.685Z”
      },
      {
        “expression”: “liftoff, we have a liftoff!”,
        “language”: “en”,
        “type”: “answer”,
        “plan_delay”: 16000,
        “plan_date”: “2019-08-19T17:32:09.685Z”
      }
    ],
    “skills”: [“/susi_skill_data/models/general/Novelty and Humour/en/Apollo_Countdown.txt”],
    “persona”: {}
  }],
  “answer_date”: “2019-08-19T17:31:53.685Z”,
  “answer_time”: 15,
  “language”: “en”,
  “session”: {“identity”: {
    “type”: “host”,
    “name”: “172.68.146.190_4a948f88”,
    “anonymous”: true
  }}
}

The response for the above skill can be received by querying “rocket launch” at https://api.susi.ai/  or you can get it by going directly to the following link https://api.susi.ai/susi/chat.json?timezoneOffset=-330&q=Rocket+Launch

The skill acts as a count-down timer of rocket launch. Now, we have a skill, but how are we going to implement it in android app, so that the countdown timer works properly and the output appears at the proper scheduled time. Also, we need to take care that the other processes of the app doesn’t stop or hangs up when some events are scheduled. Going through all these problems and circumstances I came up with a solution. So, let’s have a look. I made use of Handler. 

Handler in Android helps to execute some tasks at a scheduled time. The tasks are executed in a different thread other than the Main UI thread. So, the other process in the app doesn’t stops and it runs smoothly.

Let’s have a look at the code:

for (i in 0 until actionSize) {

  if (susiResponse.answers[0].actions[i].plan_delay.toString().isNullOrEmpty()) {
      planDelay = 0L
  } else {
      planDelay = susiResponse.answers[0].actions[i].plan_delay
  }
  executeTask(planDelay, susiResponse, i, date)
  chatView?.hideWaitingDots()
}

The code above, loops through all the actions mentioned in the skill one by one. After parsing it, the data is passed to a function executeTask(). This function performs the task to schedule the events (plan actions).

handler = Handler()
try {
  handler.postDelayed({
      val parseSusiHelper = ParseSusiResponseHelper()
      parseSusiHelper.parseSusiResponse(susiResponse, i, utilModel.getString(R.string.error_occurred_try_again))
      var setMessage = parseSusiHelper.answer

      if (parseSusiHelper.actionType == Constant.TABLE) {
          tableItem = parseSusiHelper.tableData
      } else if (parseSusiHelper.actionType == Constant.VIDEOPLAY || parseSusiHelper.actionType == Constant.AUDIOPLAY) {
          // Play youtube video
          identifier = parseSusiHelper.identifier
          youtubeVid.playYoutubeVid(identifier)
      } else if (parseSusiHelper.actionType == Constant.ANSWER && (PrefManager.checkSpeechOutputPref() && check || PrefManager.checkSpeechAlwaysPref())) {
          setMessage = parseSusiHelper.answer
          try {
              var speechReply = setMessage
              Handler().post {
                  textToSpeech = TextToSpeech(context, TextToSpeech.OnInitListener { status ->
                      if (status != TextToSpeech.ERROR) {
                          val locale = textToSpeech?.language
                          textToSpeech?.language = locale
                          textToSpeech?.speak(speechReply, TextToSpeech.QUEUE_FLUSH, null)
                          PrefManager.putBoolean(R.string.used_voice, true)
                      }
                  })
              }
          } catch (e: Exception) {
              Timber.e(“Error occured while trying to start text to speech engine – ” + e)
          }
      } else if (parseSusiHelper.actionType == Constant.STOP) {
          setMessage = parseSusiHelper.stop
          removeCallBacks()
          chatView?.stopMic()
      }

      if (parseSusiHelper.answer == ALARM) {
          playRingTone()
      }

      try {
          databaseRepository.updateDatabase(ChatArgs(
                  prevId = id,
                  message = setMessage,
                  date = DateTimeHelper.getDate(date),
                  timeStamp = DateTimeHelper.getTime(date),
                  actionType = parseSusiHelper.actionType,
                  mapData = parseSusiHelper.mapData,
                  isHavingLink = parseSusiHelper.isHavingLink,
                  datumList = parseSusiHelper.datumList,
                  webSearch = parseSusiHelper.webSearch,
                  tableItem = tableItem,
                  identifier = identifier,
                  skillLocation = susiResponse.answers[0].skills[0]
          ), this)
      } catch (e: Exception) {
          Timber.e(“Error occured while updating the database – ” + e)
          databaseRepository.updateDatabase(ChatArgs(
                  prevId = id,
                  message = utilModel.getString(R.string.error_internet_connectivity),
                  date = DateTimeHelper.date,
                  timeStamp = DateTimeHelper.currentTime,
                  actionType = Constant.ANSWER
          ), this)
      }
  }, planDelay)
} catch (e: java.lang.Exception) {
  Timber.e(“Error while showing data – ” + e)
}

The variable planDelay determines the time after which the task needs to be executed. By default its value is 0, i.e, it gets executed at that moment only until any delay time is supplied by the skill.

In this way, planned actions have been implemented in SUSI.AI Android app.

Resources: 

Blogs: Handler, Handler Implementation

SUSI Skill: Rocket Launch

SUSI.AI Android App: PlayStore GitHub

Tags:SUSI.AI Android App, Kotlin, SUSI.AI, FOSSASIA, GSoC, Android, Handler, Planned Actions

Continue Reading

Displaying of Images using Picasso Library in SUSI.AI Android App

Displaying images is not so easy in an Android app without using a third-party library. Picasso is one of the most popularly used third-party libraries for android that is used to display images. It is a very simple and powerful library for image downloading and caching. SUSI.AI is a smart assistant app. We need the use of Picasso a lot to answer the queries put up by the users. Suppose if the user asks to show some pictures, we need to display it. In such a case we find Picasso very handy. So let’s know how to implement it, by going through the code base present in SUSI.AI android app.

Why use Picasso or another third-party library?

You might be thinking that why we should use a 3rd party library. You can achieve your task without using a 3rd party API as well. But if you will use the core method then it would take a larger amount of code. But if we will use a 3rd party library like Picasso then we will achieve our goal in fewer lines of code.  Also, we can easily cache the images using this libraries. Caching is really an important feature to speed up the application.

Adding Picasso Library to the Gradle file

Adding Picasso android library to your project is very easy. You just need to add the following line in the dependency block of your build.gradle file and replace ${rootConfiguration.picassoVersion} with the latest version of Picasso(for example 2.71828). Now sync your Gradle file. I am assuming that you have already added NETWORK permission in your project.

The simplest way of loading image is:

Picasso.with(this)
   .load("YOUR IMAGE URL HERE")
   .into(imageView);

Here, imageView is the reference to the imageView where you want to display the image.

This is how it is being implemented in the susi app. Here imageUrl is the URL of the image to be loaded. ImageView is the place where we need to display the loaded image. Now there is a chance that the imageUrl is actually not a URL of the image, or suppose Picasso fails to load the image due to some errors. In all such cases, a dummy image would be shown in the imageView. This dummy image is added by calling the error function of Picasso class and passing the reference of the image to it. The placeholder function displays a static image present inside the app until the actual image is being loaded.

Re-sizing and Rotating

We can also resize and rotate the image very easily.

Resources: 

Documentation: Picasso, Glide vs Picasso

Tutorial: Picasso Youtube Video

Library source code: Picasso Library source code

SUSI.AI Android App: PlayStore GitHub

Tags: SUSI.AI Android App, Kotlin, SUSI.AI, FOSSASIA, GSoC, Android, Picasso, Image loader..

Continue Reading

Store data using Realm Database in SUSI.AI Android App

A large application always consists of some kinds of a database to keep a store of large chunks of data that it receives from the server. One such database is the Realm Database.

While working on SUSI.AI app (a smart assistant open-source app) I found the necessity to store room name in the database. These room names were used while configuring SUSI Smart Speaker using the app. Let me share how I implemented this in the app.

Before we proceed with the implementation, let’s go through the  prerequisites for using Realm with Android:

  • Android Studio version 1.5.1 or higher
  • JDK version 7.0 or higher
  • A recent version of the Android SDK
  • Android API Level 9 or higher (Android 2.3 and above)

First of all, you need to add the Gradle dependency at the app level.

Since we are going to do it in Kotlin, we need to add the following:

Since this is a database that is used throughout the application, we have to initialize the realm in the class that extends the Application class.

Once the realm is initialized, we set up a data model class. This model class contains the format of data to be stored. The model class should extend the RealmObject class. Also, the model class must use the keyword open. This makes it available for inheritance and allows it to be overridden. If we remove the open keyword here, we get an error stating: cannot inherit from the final model class. 

When the realm objects are initialized at the start, the default values of the variables are declared null (Concept of Function Default  Arguments in Kotlin).

Now to manipulate the database from the required files we need to create a reference to the Realm followed by getting the default instance of Realm.


To commit any operation with the database, the code must be written within beginTransaction and commitTransaction blocks. Below is an example where the room name is added to the database by using the model class mentioned above.

We use the above method to add data to the database. Now, if we can add data, we should be able to retrieve them too. Let’s go through the code snippet to retrieve data from the database.

The above snippet shows a function which extracts data from the database and stores them in an array list for displaying in a recycler view. 

Now, we all went through addition, display of data in the realm database. Is that sufficient?? No, if we can add data to the database, then there also should be a way to delete the data when needed. Clearing/removing data all at once from the database is very easy and can be simply done by :

Resources: 

Documentation: Realm

Reference: Realm Database

SUSI.AI Android App: PlayStore GitHub

Tags: SUSI.AI Android App, Kotlin, SUSI.AI, FOSSASIA, GSoC, Android, Realm, Database

Continue Reading

Handler in Android

While working on SUSI.AI app (a smart assistant app), I found the necessity of handling UI components along with background data queue. While initializing a text to speech(TTS) engine inside a fragment that already had a speech to text (stt) engine implemented, there was a necessity to run the TTS engine to run using handler and later make it interact with the main UI thread. Let’s see how I handled this situation in the SUSI.AI app.

Android usually handles all the UI operations and input events from one single thread which is known as the Main or UI thread. Android collects all events in this thread in a queue and processes this queue. If one needs to perform some tasks in parallel with the main thread, then the main thread needs to be synchronized. Each handler is associated with a thread. A handler is a way to solve the asynchronous problem in Android.

A handler is widely used for:

  • Managing and inserting messages in the queue
  • Performing a task at a scheduled time in a different thread
  • Implementing runnable 

How Handlers work?

A handler is used to send and process Message and Runnable objects associated with a thread. Each handler is associated with a different single thread. This helps to perform the task asynchronously. The task, messages or runnable associated with that handlers are executed when it comes out of the message queue.

In Android, Handler is mainly used to update the main thread from background thread or other than the main thread. There are two methods in the handler:

  • Post() − it posts a message from background thread to the main thread using looper.
  • sendmessage() − if we want to organize what we have sent to UI (message from background thread) or UI functions. we should use sendMessage().

Construction of Handler:

First of all, we need to create and reference to Handler. After Handler is being created, we create some runnable objects. These runnable objects get executed inside the Handler.

Construction of Runnables that can be used in the handler:

Here, in this example, I have used the Post method to update the main thread.

Clearly, the post method is used in the handler object to execute the task mentioned in the runnable, by using its reference. Also, we can see the use of postDelayed function. This function executes the runnable after a specific span of time(mentioned in milliseconds in the parameter along with the runnable reference).

Lastly, it is very important to note that we should clear all the references to the handlers during the destruction of the view or the activity else there might be memory leaks. 

Resources: 

Documentation: Handler

Reference: Handler in Android

SUSI.AI Android App: PlayStore GitHub

Tags:SUSI.AI Android App, Kotlin, SUSI.AI, FOSSASIA, GSoC, Android, Handler

Continue Reading
Close Menu