Mapping Events to Load from Database

Mapping Events to Load from Database

In Open Event Android whenever App is started events are fetched for the location given by the user, since we have locally added isFavorite extra field for every event it is necessary to be updated for all the events returned in the API response before inserting it into our database otherwise all the favorite related information would be lost. This blog post will guide you on how its done in Open Event Android.

The sequence of steps followed

  • Take the IDs of events being saved into the database
  • Use DAO method which does “SELECT id from Event where favorite = 1 AND id in :eventIds and pass API returned eventIds to this function
  • Set the old favorited on new event objects
  • Save them in database

Let’s see how all of these steps are performed in greater details. Whenever user gives in a location following function is called

fun getEventsByLocation(locationName: String): Single<List<Event>> {
       return eventApi.searchEvents(“name”, locationName).flatMap { apiList ->
           val eventIds = apiList.map { it.id }.toList()
           eventDao.getFavoriteEventWithinIds(eventIds).flatMap { favIds ->
               updateFavorites(apiList, favIds)
           }
       }
   }

Here we are extracting all the Ids of events returned in the API response and then calling getFavoriteEventWithinIds on it. The latter takes the list of eventIds and return the Ids of events which are favorite out of them. This is then passed to the function updateFavorite along with the API returned Events. Following is the implementation of updateFavorite method.

fun updateFavorites(apiEvents: List<Event>, favEventIds: List<Long>): Single<List<Event>> {
       apiEvents.map { if (favEventIds.contains(it.id)) it.favorite = true }
       eventDao.insertEvents(apiEvents)
       val eventIds = apiEvents.map { it.id }.toList()
       return eventDao.getEventWithIds(eventIds)
   }

updateFavorite checks for all events in the list of events whether if its Id is present in the favorite event ids it sets favorite field of that event true. After the favorites for the list of events are updated they are inserted into the database using DAO method insertEvents. The last task is to take the fetch these events again from the database, to do this first we extract the Ids of the events we just inserted into the database and call the DAO method getEventsWithIds passing the extracted eventIds, getEventsWithids simply returns the Events with given Ids.

Given below are the implementations of the functions getEventWithIds and getFavoriteEventWithinIds

@Query(“SELECT * from Event WHERE id in (:ids)”)
   fun getEventWithIds(ids: List<Long>): Single<List<Event>>

@Query(“SELECT id from Event WHERE favorite = 1 AND id in (:ids)”)
   fun getFavoriteEventWithinIds(ids : List<Long>): Single<List<Long>>

getEventWithIds simply makes a select query and checks for events whose ids lies in the ids passed to the method

getFavoriteEventWithinids returns the Ids of the favorite event out of the list of event id passed to the method.

Resources

Continue ReadingMapping Events to Load from Database

Displaying Charts on Lightbox

Susper displayed charts which shows graphical representation of Result Frequency and Protocol Distribution(http or https). But since results from these charts added little value it was decided to display charts on a click of a button on a lightbox. (Issue: https://github.com/fossasia/susper.com/issues/1066 ). In this blog,I will describe how I have implemented charts on lightbox.

Uniting the graphs(Line and Bar Graph) in a same class:

Before beginning the implementation of lightbox, we should unite all the components that will be displayed on lightbox in a single class.

Here I have put the different types of charts in a class named graph.

<div class="graph large"> <div class="linegraph"><canvas baseChart
           [datasets]="lineChartData"
           [labels]="lineChartLabels"
           [options]="lineChartOptions"
           [chartType]="lineChartType"
           [colors]="lineChartColors"
></canvas></div><div class="bargraph"><canvas baseChart
           [datasets]="barChartData"
           [labels]="barChartLabels"
           [options]="barChartOptions"
           [chartType]="barChartType"
           [colors]="lineChartColors"
   ></canvas></div></div>

Implementing button to toggle display of light box:

To show and hide lightbox along with charts we must have a button.

Clicking on which should trigger a function which will internally execute the logic in typescript file to show and hide the lightbox.

<button class="btn" id="toggle-chart-button" (click)="BoxToggle()">
           {{analyticsStatus}} Analytics
 </button>

Creating a translucent black background:

Now to have a black overlay for lightbox we need a div element and inside it we need a closing button.

Clicking on the black overlay should toggle status of light box therefore we have binded click event with BoxToggle() method.

<div id="fade" class="black_overlay" (click)='BoxToggle()'><a class="closeX" id="closeX">&#10006;</a></div>

Creating a white block to display charts:

Now after creating a black overlay in background we need a white foreground to display charts.

Therefore I have implemented a div element and put all the chart elements inside the div element so that it will be displayed on a white foreground on a translucent black background.

<div id="light" class="white_content">
<div class="graph large"><div class="linegraph"><canvas baseChart
           [datasets]="lineChartData"
           [labels]="lineChartLabels"
           [options]="lineChartOptions"
           [chartType]="lineChartType"
           [colors]="lineChartColors"
   ></canvas></div>
<div class="bargraph"><canvas baseChart
           [datasets]="barChartData"
           [labels]="barChartLabels"
           [options]="barChartOptions"
           [chartType]="barChartType"
           [colors]="lineChartColors"
   ></canvas></div></div></div>

CSS for all above elements:

All the CSS which is written for charts, toggle button, white block and black overlay

To make it look attractive and similar to market leader can be found here.

Implementing the logic in typescript file:

Now we have created all the elements in frontend to display the lightbox but we need a little logic to show and hide the lightbox on clicking the button. For this I have used a variable analyticsStatus which is a string and is initialised with value ‘Show Chart’. Its value is displayed on the toggle button. I have used this variable with BoxToggle() function for implementing the logic for showing and hiding the lightbox.

This function checks the status of message and toggles it along with toggling lightbox with some javascript code. The code for the function is here.

BoxToggle() { if (this.analyticsStatus === 'Show Chart') {
     this.analyticsStatus = 'Hide Chart';
     document.getElementById('light').style.display = 'block';
     document.getElementById('fade').style.display = 'block';
   } else {
     this.analyticsStatus = 'Show Chart';
     document.getElementById('light').style.display = 'none';
     document.getElementById('fade').style.display = 'none'; }}

Resources

1.Creating a lightbox: https://www.emanueleferonato.com/2007/08/22/create-a-lightbox-effect-only-with-css-no-javascript-needed/

2.W3School lightbox: https://www.w3schools.com/howto/howto_js_lightbox.asp

3.Angular 2 Lightbox: https://lokeshdhakar.com/projects/lightbox2/

Continue ReadingDisplaying Charts on Lightbox

Creating multiple Attendee Details sections in Open Event Android

Whenever a User picks the quantities of tickets he/she wants to buy for an event, that no of ticket details are required to be provided by the user. For this multiple attendee details section is displayed so that user can type in the information of all the Attendee. This blog post will help you understand how this is implemented in Open Event Android

The idea is to create a Recycler View for attendee objects and add empty attendee objects to the recycler based on what total quantity of tickets are selected by the User. For this first, we will have to create a Recycler View for attendee objects.

RecyclerView for Attendee Objects

Just like any other recycler view, we have to follow the following steps to build the Recycler View

  • Create the layout for single Attendee item.
  • Create a RecyclerAdapter recycler view
  • Create ViewHolder for recycler view

The layout for single attendee item contains editable fields such as First name, Last name, Email, Country and a view which can hold custom forms (if required for that Event Ticket). The Recycler Adapter for attendee has an ArrayList of Attendee which is what is rendered on the fragment, it also has a list of tickets such that attendee at a certain position in ArrayList is for the ticket at the corresponding position in ticket list. We also have some regular recycler adapter functions to insert values into these class variables. We pass a link of the recycler adapter object to the view holder so that these variables can be accessed from the view holder too.

override fun onBindViewHolder(holder: AttendeeViewHolder, position: Int) {
       holder.bind(this, position)
   }

We send the position to the view holder as to know which attendee and ticket from the list of attendees and tickets should the view be created for.

The view holder is responsible for creating views for a single recycler view item and how it should behave when any action is performed on that view.

Since we are dealing with editable text view for getting attendee details from the user, we have set up a Text watcher to listen for text change in any of these view objects. Whenever text is changed in any of these views the corresponding attendee in attendee recycler is automatically updated.  The following is the implementation of text watcher.

 textWatcher = object : TextWatcher {
           override fun afterTextChanged(p0: Editable?) {
               val id = attendeeRecyclerAdapter.attendeeList[position].id
               attendeeRecyclerAdapter.attendeeList.removeAt(position)
               val attendee = Attendee(id, firstname = itemView.attendeeItemFirstName.text.toString(),
                       lastname = itemView.attendeeItemLastName.text.toString(),
                       email = itemView.attendeeItemEmail.text.toString(),
                       city = getAttendeeField(“city”),
                       address = getAttendeeField(“address”),
                       state = getAttendeeField(“state”),
                       country = itemView.attendeeItemCountry.text.toString(),
                       ticket = TicketId(attendeeRecyclerAdapter.ticketList[position].id.toLong()),
                       event = attendeeRecyclerAdapter.eventId)
               attendeeRecyclerAdapter.attendeeList.add(position, attendee)
           }

             }

Now that our recycler adapter is ready we just have to wire the adapter with the recycler view in the Attendee Fragment.

<android.support.v7.widget.RecyclerView
               android:id=“@+id/attendeeRecycler”
               android:layout_width=“match_parent”
               android:layout_height=“wrap_content” />

Adding the above in attendee fragment will create a recycler view. Next step is too wire everything and insert attendee and ticket according to the user.

Inserting Attendees and Tickets in the Recycler

We have set up an observer on the tickets variable of Attendee View model which means that any update in the tickets will automatically call it with latest values. We are inserting all the tickets into the ticket list of the recycler adapter and also for every ticket we are generating a no of attendees according to the value of ticket’s quantity.

attendeeFragmentViewModel.tickets.observe(this, Observer {
               it?.let {
                   ticketsRecyclerAdapter.addAll(it)
                   ticketsRecyclerAdapter.notifyDataSetChanged()
                   it.forEach {
                    val pos = ticketIdAndQty?.map { it.first }?.indexOf(it.id)
                   val iterations = pos?.let { it1 ->       ticketIdAndQty?.get(it1)?.second } ?: 0
                  for (i in 0 until iterations)
                               attendeeRecyclerAdapter.add(Attendee(attendeeFragmentViewModel.getId()), it)
                  attendeeRecyclerAdapter.notifyDataSetChanged()
                     }
    }

In the code snippet shown above, pos = ticketIdAndQty?.map { it.first }?.indexOf(it.id) helps in getting the position of ticket id and its quantity pair from the list of pairs whenever we find a ticket. Using the position value we take can get the second variable in a pair which is the quantity of that ticket. Now that we know ticket details and the quantity of same to be inserted into the recycler we can create empty attendee objects and add them to the adapter with there ticket id as current ticket id and this goes till the quantity limit for the current ticket is reached. These attendees can now be edited by the user and when the user is done providing details the order will take the details of different attendees and create the order.

References

Continue ReadingCreating multiple Attendee Details sections in Open Event Android

Extending the News Feature to Show results from multiple organisations

News Tab in Susper was earlier implemented to show results only from a single organisation using  site: modifier for query facet. In this blog I will discuss about how I have modified the current News Tab to show results from various News organisation like BBC, Al Jazeera, The Guardian etc.

Implementation:

Step 1:

Creating a JSON file to store organisations detail:

We need to decide from which organisations  we will fetch results using YaCy Server and then display it in Susper. We have provided a JSON file where user can add or delete News sources easily. The results will only limited to the organisations which are present in the JSON file.

Step 2:

Creating a service to fetch details from JSON file:

Now after creating the the JSON file we need a service which will fetch results from the JSON file according to our need. This service will be a simple  Angular Service having a class GetJsonService and it will access the newsFile.json and map the results in JSON format. Here is the service which does this task for us.

Step 3:

Creating a service to fetch news accordingly:

Now after fetching the JSON result we need a service to fetch the News Results from YaCy Server. I have created a separate service to do this task where I have fetched results using site: modifier from each organisation and returned the results.The code for the news.service.ts is below.

export class NewsService {
 constructor(private jsonp: Jsonp) { }
 getSearchResults(searchquery, org) {
   let searchURL = 'https://yacy.searchlab.eu/solr/select?query=';
   searchURL += searchquery.query + ' site:' + org;
   let params = new URLSearchParams();
   for (let key in searchquery) {
     if (searchquery.hasOwnProperty(key)) {
       params.set(key, searchquery[key]); } }
  //Set other parameters
   return this.jsonp
     .get(searchURL, {search: params}).map(res =>
       res.json()[0]
     ).catch(this.handleError); }

 

Step 4:

Updating the results section:

Now we have a service that gives results from a single organisation and a JSON list of organisations. Now in results.component.ts we can simply subscribe to getJsonService and in a loop we will call getNewsService by changing the organisation in every iteration. We will then check that whether we are getting the valid results or not (undefined). The results which are not valid can cause errors when we will try to read any field of an undefined variable. Then, We will simply append the 2 result items from each organisation in an empty array and later use this array to show results.

this.getJsonService.getJSON().subscribe(res => { this.newsResponse = [];
for (let i = 0; i < res.newsOrgs.length; i++) { this.getNewsService.getSearchResults(querydata,res.newsOrgs[i].provider).subscribe( response => {
    if (response.channels[0].items[0] !== undefined) {
    this.newsResponse.push(response.channels[0].items[0]); }
    if (response.channels[0].items[1] !== undefined) {
    this.newsResponse.push(response.channels[0].items[1]); } } ); }
    });

 

The newsClick() function is activated on clicking News Tab and it updates the query and its details in store.

Step 5

Displaying the results:

Now we will modify the results.component.html to show results from new newsResponse array which have 2 results each from 5 organisations.

For this we will use each item of newsResponse using *ngFor and display its title,link and description in html template. We will also use [style.color] property of our element and set the color according to theme.

<div *ngFor="let item of newsResponse" class="result"> <div class="title">
<a class="title-pointer" href="{{item.link}}" [style.color]="themeService.titleColor">{{item.title}}</a>
</div> <div class="link">
<p  [style.color]="themeService.linkColor">{{item.link}}</p>
 </div> </div>

 

Here is the view of Susper’s News Tab where we are getting results from 5 different organisations.

Resources

  1. YaCy Modifiers: http://www.yacy-websuche.de/wiki/index.php/En:SearchParameters
  1. Angular Services: https://angular.io/tutorial/toh-pt4
  2. Reading JSON data in Angular: https://stackoverflow.com/questions/43275995/angular4-how-do-access-local-json

 

Continue ReadingExtending the News Feature to Show results from multiple organisations

Setting an Event Favorite in Open Event Android

The favorite events feature in Open Event Android allows any user to favorite an event and those events can are available in the favorite events fragment which is easily accessible from the bottom navigation bar. This blog article will walk you through on how favorite works in Open Event Android. There are a couple of different ways to do it, in Open Event Android we are keeping track of favorite events using a favorite Boolean stored along with the information of the event ie we have added a favorite boolean field inside the event entity class. However, this method has its own pros and cons. The big plus point of this method is the fact that we can simply check the favorite field of the event object to check if that particular event is favorite or not. But since we have set onConflict strategy as replace in the event insert DAO method (shown below)

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

This leads to a big problem when the same event is fetched or is present in the response while inserting the favorite field will be set to default value (false) and the favorite information would be lost. Hence extra care needs to be taken while updating events.

Given following is the event model class for serializing / deserializing JSON responses. Note the extra field favorite added here which helps us to provide user favorite feature locally.

@Type(“event”)
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class)
@Entity
data class Event(
      @Id(LongIdHandler::class)
      @PrimaryKey
      val id: Long,
      val name: String,
      val identifier: String,
      val isMapShown: Boolean = false,
      val favorite: Boolean = false
)

Since we added a new field to the Event model class, to access the favorite events we will have to create DAO methods. To fetch all the favorite events we will have to create a Query method which returns all the events wherever favorite property is set. This can be done using a simple select statement which looks for events with favorite boolean set ie. 1, the method implementation is shown below.

@Query(“SELECT * from Event WHERE favorite = 1”)
   fun getFavoriteEvents(): Flowable<List<Event>>

To set an event favorite we will have to add one more method to the EventDao. We can create a generalized method which sets according to the boolean passed as a parameter. The function implementation is shown below, setFavorite takes in EventId, Id of the event which has to be updated and the boolean favorite which is what the new value for the favorite field is. setFavorite makes an SQL update query and matches for EventId and sets favorite for that event.

@Query(“UPDATE Event SET favorite = :favorite WHERE id = :eventId”)
   fun setFavorite(eventId: Long, favorite: Boolean)

In Open Event Android we use the service class which can be used to expose the DAO methods to the rest of the project. The idea behind creating service layer is the separation of concerns this service method is then called from the view model function (following the MVVM approach). Given following is the implementation of the get and set favorite methods.

fun getFavoriteEvents(): Flowable<List<Event>> {
       return eventDao.getFavoriteEvents()
   }
fun setFavorite(eventId: Long, favourite: Boolean): Completable {
       return Completable.fromAction {
           eventDao.setFavorite(eventId, favourite)
       }
   }

The method getFavoriteEvents calls the DAO method to fetch the favorite evens and returns a flowable list of favorite events and setFavorite method generates a completable from the DAO action to favorite any event.

EventViewModel uses these methods and specifies where the observables should be observedOn and subscribedOn also defines what should happen when some error occurs. Given below is implementation for the two methods.

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”
               }))
   }

For every event, we have a floating action bar to toggle the favorite state of the same whenever user clicks on the FAB it checks the current state of the event and calls the view model function setFavorite passing the Event Id of the clicked event and the negation of current event.favorite state.

val favouriteFabClickListener = object : FavoriteFabListener {
           override fun onClick(eventId: Long, isFavourite: Boolean) {
               eventsViewModel.setFavorite(eventId, !isFavourite)
           }
       }

What we discussed in this blog post works well unless we are not inserting the events with the same event id, this will cause replace on the stored events and current favorite information will be lost. To overcome this limitation we follow the following procedure

  • Fetch event id’s of all the events returned by the API response
  • Use the DAO method to find out the id’s of favorite events out of these
  • Set the favorite in the new events where IDs are in the ids returned in the second point
  • Insert the events into the database
  • Return the events from the database with ID’s as of point one

References

Continue ReadingSetting an Event Favorite in Open Event Android

Join Codeheat Coding Contest 2018/19

Codeheat is a coding contest for developers interested in contributing to Open Source software and hardware projects at FOSSASIA.  Join development of real world software applications, build up your developer profile, learn new new coding skills, collaborate with the community and make new friends from around the world! Sign up for #CodeHeat here now and follow Codeheat on Twitter.

The contest runs until 1st February 2019. Different FOSSASIA projects take part in the Codeheat contest including:

Grand prize winners will be invited to present their work at the FOSSASIA OpenTechSummit in Singapore in March 2019 and will get 600 SGD in travel funding to attend, plus a free speaker ticket and beautiful Swag.

Our jury will choose three winners from the top 10 contributors according to code quality and relevance of commits for the project. The jury also takes other contributions like submitted weekly scrum reports and monthly technical blog posts into account, but of course awesome code is the most important item on the list.

Other participants will have the chance to win Tshirts, Swag and vouchers to attend Open Tech events in the region and will get certificates of participation.

codeheat-logo

Team mentors and jury members from 10 different countries support participants of the contest.

Participants should take the time to read through the contest FAQ and familiarize themselves with the introductory information and Readme.md of each project before starting to work on an issue.

Developers interested in the contest can also contact mentors through project channels on the FOSSASIA Gitter Chat.

Links

Website: codeheat.org

Codeheat Twitter: twitter.com/codeheat_

Codeheat Facebook: facebook.com/codeheat.org

Participating Projects Code Repositories: github.com/fossasia

Continue ReadingJoin Codeheat Coding Contest 2018/19

Implementing Multimeter in PSLab Android App

The Pocket Science Lab Android app being on the verge of development have various new features adding up per day. One of the new things added up recently is the splitting of the control section in three different instruments and implementing the control read section into a multimeter. This blog will be discussing about how the multimeter is implemented.

The different instruments are power section, multimeter and wave generator. While in the previous implementation of control section it was divided into three parts namely control main, control read and control advanced as shown in figure (1). The control is the power source, read is the multimeter and advanced section is the wave generator.

Figure  (1): Screenshot of control section

Figure (1) shows the previous implementation of a multimeter i.e the read section but as we know this is way different than the actual implementation of a multimeter and thus from here comes the task of implementing a new multimeter.

What is a Multimeter, how does it looks?

A multimeter basically is an instrument designed to measure electric current, voltage, and usually resistance, typically over several ranges of value.

          Figure (2): Showing a real multimeter instrument and its different sections [2]

Figure(2) clearly shows how an actual multimeter looks. It basically has three important components i.e the display the buttons and the rotary knob or the dial and thus the task was to implement the same in PSLab android.

Implementation in PSLab

Figure (3) :  Screenshot of new implementation of multimeter

The implementation of multimeter is thus inspired from its original look i.e it has got basic buttons, a rotary knob and a display. Figure (3) shows the implementation of multimeter in the android-app

Back-end of Multimeter

A separate multimeter activity was implemented for the multimeter. The main back-end part of getting the resistance, capacitance, frequency and count pulse were taken from the communication related classes such as the ScienceLab class and PacketHandler class. For example to get the voltage calculation we use the getRawableVoltage function

private double getRawAverageVoltage(String channelName) {
  try {
      int chosa = this.calcCHOSA(channelName);
      mPacketHandler.sendByte(mCommandsProto.ADC);
      mPacketHandler.sendByte(mCommandsProto.GET_VOLTAGE_SUMMED);
      mPacketHandler.sendByte(chosa);
      int vSum = mPacketHandler.getVoltageSummation();
      mPacketHandler.getAcknowledgement();
      return vSum / 16.0;
  } catch (IOException | NullPointerException e) {
      e.printStackTrace();
      Log.e(TAG, "Error in getRawAverageVoltage");
  }
  return 0;
}

The above function shows the pure backend of PSLab and how data is taken from the hardware using the packet handler class, after which the data is processed in various other functions after we getting the final result. Similarly the function to get count pulse is

public int readPulseCount() {
 try {
  mPacketHandler.sendByte(mCommandsProto.COMMON);
  mPacketHandler.sendByte(mCommandsProto.FETCH_COUNT);
  int count = mPacketHandler.getVoltageSummation();
  mPacketHandler.getAcknowledgement();
  return 10 * count;
 } catch (IOException e) {
  e.printStackTrace();
 }
 return -1;
}

As we see that the data is being taken through a similar manner in the above function i.e using the packetHandler class(by sending and receiving bytes). Thus in all the other functions for capacitance, frequency similar communication model can be found.

Similarly all the functions are implemented in the ScienceLab class and thus all the functions are directly called from the ScienceLab class in the Multimeter activity. For more knowledge on these one can directly have a look at the PSLab android app codes available in Github.

Implementation of the Rotary Knob

The rotary knob is implemented using the BeppiMenozzi Knob library. More information regarding the same can be found in my previous blog on implementing the rotary knob.

Resources:

Continue ReadingImplementing Multimeter in PSLab Android App

Plot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Graphs and charts provide a visual representation of the data. They provide a clearer and quicker understanding of the impact of certain statistics. Thus, SUSI.AI Android app makes use of bar charts to display statistics related to user ratings for SUSI skills. This blog guides through the steps to create a Horizontal Bar Chart, using MPAndroidChart library, that has been used in the SUSI.AI Android app skill details page to display the five star skill rating by the users.

On vertical axis : Labels of the rating shown
On horizontal axis : Percentage of total number
of users who rated the skill with the corresponding
number of stars on the vertical axis

Step – 1 : Add the required dependencies to your build.gradle.

(a) Project level build.gradle

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

(b) App level build.gradle

dependencies {
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}

 

Step – 2 : Create an XML layout.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
   android:layout_height="match_parent">

    <!-- Add a Horizontal Bar Chart using MPAndroidChart library -->
    <com.github.mikephil.charting.charts.HorizontalBarChart
       android:id="@+id/skill_rating_chart"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

 

Step – 3 : Create an Activity and initialize the Horizontal Bar Chart.

class MainActivity : Activity {

   lateinit var skillRatingChart : HorizontalBarChart

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.chart)

       setSkillGraph( )

   }
}

 

Step – 4 : Create a method in your MainActivity to set up the basic properties and the axes.

/**
* Set up the axes along with other necessary details for the horizontal bar chart.
*/
fun setSkillGraph(){
   skillRatingChart = skill_rating_chart              //skill_rating_chart is the id of the XML layout

   skillRatingChart.setDrawBarShadow(false)
   val description = Description()
   description.text = ""
   skillRatingChart.description = description
   skillRatingChart.legend.setEnabled(false)
   skillRatingChart.setPinchZoom(false)
   skillRatingChart.setDrawValueAboveBar(false)

   //Display the axis on the left (contains the labels 1*, 2* and so on)
   val xAxis = skillRatingChart.getXAxis()
   xAxis.setDrawGridLines(false)
   xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
   xAxis.setEnabled(true)
   xAxis.setDrawAxisLine(false)


   val yLeft = skillRatingChart.axisLeft

//Set the minimum and maximum bar lengths as per the values that they represent
   yLeft.axisMaximum = 100f
   yLeft.axisMinimum = 0f
   yLeft.isEnabled = false

   //Set label count to 5 as we are displaying 5 star rating
   xAxis.setLabelCount(5)

//Now add the labels to be added on the vertical axis
   val values = arrayOf("1 *", "2 *", "3 *", "4 *", "5 *")
   xAxis.valueFormatter = XAxisValueFormatter(values)        

   val yRight = skillRatingChart.axisRight
   yRight.setDrawAxisLine(true)
   yRight.setDrawGridLines(false)
   yRight.isEnabled = false

   //Set bar entries and add necessary formatting
   setGraphData()

   //Add animation to the graph
   skillRatingChart.animateY(2000)
}


Here is the XAxisValueFormatter class that is used to add the custom labels to the vertical axis :

public class XAxisValueFormatter implements IAxisValueFormatter {

   private String[] values;

   public XAxisValueFormatter(String[] values) {
       this.values = values;
   }

   @Override
   public String getFormattedValue(float value, AxisBase axis) {
       // "value" represents the position of the label on the axis (x or y)
       return this.values[(int) value];
   }

}

 

Step – 5 : Set the bar entries.

/**
* Set the bar entries i.e. the percentage of users who rated the skill with
* a certain number of stars.
*
* Set the colors for different bars and the bar width of the bars.
*/
private fun setGraphData() {

   //Add a list of bar entries
   val entries = ArrayList<BarEntry>()
   entries.add(BarEntry(0f, 27f))
   entries.add(BarEntry(1f, 45f))
   entries.add(BarEntry(2f, 65f))
   entries.add(BarEntry(3f, 77f))
   entries.add(BarEntry(4f, 93f))

  //Note : These entries can be replaced by real-time data, say, from an API

  ......

}

 

Step – 6 : Now create a BarDataSet.

To display the data in a bar chart, you need to initialize a
BarDataSet instance. BarDataSet is the Subclass of DataSet class. Now, initialize the BarDataSet and pass the argument as an ArrayList of BarEntry object.

val barDataSet = BarDataSet(entries, "Bar Data Set")

 

Step – 7 : Assign different colors to the bars (as required).

private fun setGraphData() {
    .....

   //Set the colors for bars with first color for 1*, second for 2* and so on
      barDataSet.setColors(
              ContextCompat.getColor(skillRatingChart.context, R.color.md_red_500),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_deep_orange_400),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_yellow_A700),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_green_700),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_indigo_700)

   .....
)


Step – 8 : Populate data into Bar Chart.

To load the data into Bar Chart, you need to initialize a
BarData object  with bardataset. This BarData object is then passed into setData() method to load Bar Chart with data.

//Set bar shadows
   skillRatingChart.setDrawBarShadow(true)
   barDataSet.barShadowColor = Color.argb(40, 150, 150, 150)
   val data = BarData(barDataSet)

   //Set the bar width
   //Note : To increase the spacing between the bars set the value of barWidth to < 1f
   data.barWidth = 0.9f

   //Finally set the data and refresh the graph
   skillRatingChart.data = data
   skillRatingChart.invalidate()
}


Your Horizontal Bar Chart is now ready.
Note: You can format the labels as per your need and requirement with the help of XAxisValueFormatter.

Resources

Continue ReadingPlot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Use Timber for Logging in SUSI.AI Android App

As per the official GitHub repository of Timber : “Timber is a logger with a small, extensible API which provides utility on top of Android’s normal Log class”. It is a flexible logging library for Android which makes logging a lot more convenient. In this blog you will learn how to use Timber in SUSI.AI Android app.

To begin, add Timber to your build.gradle and refresh your gradle dependencies.

implementation 'com.jakewharton.timber:timber:4.7.0’


Two easy steps to use Timber:

  1. Install any Tree instances that you want in the onCreate() of your application class.
  2. Call Timber’s static methods everywhere throughout the app.

You can add the following code to your application class :

@Override
   public void onCreate() {
       super.onCreate();

       …..
        
       if (BuildConfig.DEBUG) {
           Timber.plant(new Timber.DebugTree() {
               //Add the line number to the tag
               @Override
               protected String createStackElementTag(StackTraceElement element) {
                   return super.createStackElementTag(element) + ": " + element.getLineNumber();
               }
           });
       } else {
           //Release mode
           Timber.plant(new ReleaseLogTree());
       }
   }

   private static class ReleaseLogTree extends Timber.Tree {

       @Override
       protected void log(int priority, String tag, @NonNull String message,  
                                     Throwable throwable) {
           if (priority == Log.DEBUG || priority == Log.VERBOSE || priority == Log.INFO) {                           
               return;
           }

           if (priority == Log.ERROR) {
               if (throwable == null) {
                   Timber.e(message);
               } else {
                   Timber.e(throwable, message);
               }
           }
       }
   }


Timber ships with a ‘Debug Tree’ that provides all the basic facilities that you are very used to in the common Android.log framework. Timber has all the logging levels that are used in the normal Android logging framework which are as follows:

        • Log.e: Use this tag in places like inside a catch statement where you are aware that an error has occurred and therefore you’re logging an error.
        • Log.w: Use this to log stuff you didn’t expect to happen but isn’t necessarily an error.
        • Log.i: Use this to post useful information to the log. For instance, a message that you have successfully connected to a server.
        • Log.d: Use this for debugging purposes. For instance, if you want to print out a bunch of messages so that you can log the exact flow of your program or if you want to keep a log of variable values.
        • Log.v: Use this if, for some reason, you need to log every little thing in a particular part of your app.
        • Log.wtf: Use this when you encounter a terrible failure.

       

    • You can use Timber.e, Timber.w, Timber.i, Timber.d and Timber.v respectively for the logging levels mentioned above. However, there is a small difference. In Android logging framework the exception is passed as the last parameter. But, when using Timber, you need to provide the exception as the first parameter. Interestingly, while using Timber you don’t have to provide a TAG in logging calls because it automatically does that for you. It uses the file name, where you are logging from, as the TAG. To log an exception you can simply write:
    • Timber.e(exception, "Message");
      
    • Or a null message if you want:
    • Timber.e(exception, null);
      
    • A simple message can be logged sent via Crashlytics too:
    • Timber.e("An error message");
      

Did you notice? There is no TAG parameter. It is automatically assigned as the caller class’ name. Now, you can use Timber for logging in SUSI Android app.

Resources

 

Continue ReadingUse Timber for Logging in SUSI.AI Android App