Hiding Multiple Attendee Recycler for Single Tickets in Open Event Android

Multiple attendee recycler allows the user to easily write in attendee details for all tickets he/she is buying. The UI for attendee details fragment can be divided into two segments, one contains the details of the person creating the whole order and followed by multiple details section for the attendee of every ticket. This is a good way to take details when multiple ticket or tickets with greater than 1 quantity is involved. However, if there is only one attendee detail to be taken instead of asking the user for order creator’s details and then first attendee details it is better to add some views in the creator segment and not ask for the same details again from the user. This blog post will help you in understanding how it’s done in Open Event Android.

Keeping a track of orders with single attendee

We keep a boolean to track whether or not the sum of quantities of tickets selected by a user is one since one attendee is to be generated per ticket quantity. And incase it is boolean variable singleTicket is set to True which is false by default initially. Following is how it can be done

singleTicket = ticketIdAndQty?.map { it.second }?.sum() == 1

The above statements basically iterate over the list of pairs of tickets and their quantity and then sums over the second element of all pairs that is the quantities, if the summation leads to 1 single ticket is set to True else false.

Adding elements to recycler adapter

The next step is to update the logic as to when blank attendees are added to recycler adapter. The idea is to check the boolean singleTicket and if it is false dont insert any attendee.

attendeeFragmentViewModel.tickets.observe(this, Observer {
               it?.let {

                   if (!singleTicket)
                       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()
                       }
               }
           })

If a single ticket is false regular procedure to add blank attendee is carried out. The quantity of ticket is found and the same quantity of attendees are added to recycler adapter’s attendee’s list similary ticket information is also added to the recycler adapter. The id for blank attendees is taken from the attendeeViewModel’s getId function which in turn simply returns id using the authHolder’s getId function.

Modify the register method to check for the single attendee

So far what we have discussed helps us display the right no of details section on the UI. We will also have to modify the register method to take into account the above changes. Following is the implementation of what happens when the user selects register.

rootView.register.setOnClickListener {
               if (selectedPaymentOption == “Stripe”)
                   sendToken()

               val attendees = ArrayList<Attendee>()
               if (singleTicket) {
                   val pos = ticketIdAndQty?.map { it.second }?.indexOf(1)
                   val ticket = pos?.let { it1 -> ticketIdAndQty?.get(it1)?.first?.toLong() } ?: –1
                   val attendee = Attendee(id = attendeeFragmentViewModel.getId(),
                           firstname = firstName.text.toString(),
                           lastname = lastName.text.toString(),
                           city = getAttendeeField(“city”),
                           address = getAttendeeField(“address”),
                           state = getAttendeeField(“state”),
                           email = email.text.toString(),
                           ticket = TicketId(ticket),
                           event = eventId)
                   attendees.add(attendee)
               } else {
                   attendees.addAll(attendeeRecyclerAdapter.attendeeList)
               }
               val country = if (country.text.isEmpty()) country.text.toString() else null
               attendeeFragmentViewModel.createAttendees(attendees, country, selectedPaymentOption)
           }

For create attendee object and then add it to the attendees ArrayList first we need to find the ticket it is to be created for as the attendee object accepts Ticket Id as one of the parameters. The logic to get the ticket is fairly straightforward if singleTicket is true first we find out the ticket for which quantity is 1 and then we get the ticket object for the same. Finally, we take the id field of the ticket and add it to our attendee constructor. Other fields for attendee such as first name, last name, email, and country are taken from the EditText View’s rendered on the screen. Else if the single ticket is false which means there are multiple attendee details section on the fragment we simply add the recycler adapter’s attendee list into attendees ArrayList which is later provided to the order function for creating an order of it.

References

Continue ReadingHiding Multiple Attendee Recycler for Single Tickets in Open Event Android

Hiding Payment Options for Free Tickets in Open Event Android

Hiding Payment Options for Free Tickets in Open Event Android

Payment Options spinner allows the user to pick any convenient payment method for an order however, Payment Options should not be shown if the total worth of order is zero or the event is free. This blog post will guide you on how its implemented in Open Event AndroidFollowing are the sequence of steps that are followed for this

  • Allow the user to select tickets and their quantity
  • Use DAO method to get the prices of the tickets selected by the user
  • Iterate through the List and keep storing the prices
  • Display Payment Selector for order with the total amount greater than zero

Given below is DAO method to get the list of prices for different tickets. The method makes a simple select statement and returns price attribute of tickets with id in passed ids. A single list of float is returned by the function.

 @Query(“SELECT price from Ticket WHERE id in (:ids)”)
   fun getTicketPriceWithIds(ids : List<Int>): Single<List<Float>>

This DAO method is then exposed to other parts (ViewModels, Fragments) using service layer class method getTicketPriceWithIds which just makes calls to DAO method and return the result provided by it. Service classes are used for separation of concerns.

fun getTicketPriceWithIds(ids: List<Int>): Single<List<Float>> {
       return ticketsDao.getTicketPriceWithIds(ids)
   }

When a list of tickets and their quantity is specified by the user the prices of the ticket are fetched using the above-explained methods. These are then stored it in a List of pair with the first element being ticket id and second its quantity.

fun updatePaymentSelectorVisibility(ticketIdAndQty: List<Pair<Int, Int>>?) {
       val ticketIds = ArrayList<Int>()
       ticketIdAndQty?.forEach { if (it.second > 0) ticketIds.add(it.first) }
       compositeDisposable.add(ticketService.getTicketPriceWithIds(ticketIds)
               .subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe({
                   var total = 0.toFloat()
                   it?.forEach {
                       if (it.toFloat() > 0) total += it.toFloat()
                   }
                   paymentSelectorVisibility.value = total != 0.toFloat()
               }, {
                   Timber.e(it, “Error Loading tickets!”)
               }))
   }

The above method controls the visibility boolean for payment selector, it essentially iterates over the List<Pair<Int, Int>> and add all the ticket id to a temporary list if quantity for that ticket is greater than zero after this prices for the tickets in temporary list is fetched and sum total is calculated. If this total is greater than zero the visibility boolean is set else reset.

Finally an observable is set on this boolean which automatically updates the UI whenever boolean changes accordingly.

attendeeFragmentViewModel.paymentSelectorVisibility.observe(this, Observer {
           if (it !=null && it) {
               rootView.paymentSelector.visibility = View.VISIBLE
           } else {
               rootView.paymentSelector.visibility = View.GONE
           }
        })

Resources

Continue ReadingHiding Payment Options for Free Tickets in Open Event Android

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

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

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

Implementing Settings for Lux Meter Instrument in PSLab Android App

In PSLab android app, we have included sensor instruments which use either inbuilt sensor of smartphone or external sensor to record sensor data. For example, Lux Meter uses the light sensor to record lux data but the problem is that these instruments doesn’t contain settings option to configure the sensor record-setting like which sensor to use, change the update period etc.

Therefore, we need to create a settings page for the Lux Meter instrument which allows the user to modify the properties of the sensor instrument. For that I will use Android Preference APIs to build a settings interface that is similar to setting activity in any other android app.

The main building block of the settings activity is the Preference object. Each preference appears as a single setting item in an activity and it corresponds to key-value pair which stores the settings in default Shared Preferences file. Every time any setting is changed by the user the Android will store the updated value of the setting in the default shared preferences which we can read in any other activity across the app.

In the following steps I will describe instruction on how to create the setting interface in Android app: 

Step1 Include the dependency

First, we need to include the dependency for v7 Preference Support Library by including the following code:

dependencies { 
compile fileTree(dir: 'libs', include: ['*.jar']) 
compile 'com.android.support:appcompat-v7:23.1.1' 
compile 'com.android.support:design:23.1.1' 
compile 'com.android.support:preference-v7:23.1.1' 
}

 Step 2 Creating the preferences screen in XML

For this step, I have created a preference screen which will be inflated when the settings fragment is being created.

I created a file named  “lux_meter_settings.xml” and place it in the res/xml/ directory.

The root node for the XML file must be a <PreferenceScreen> element. We have to add each Preference within this element. Each child I added within the <PreferenceScreen> element appears as a single item in the list of settings.

The preference which I have used are:

<EdittextPreference> This preference opens up a dialog box with edit text and stores whatever value is written by the user in the edit text. I have used this preference for inputting update period and high limit of data during recording.

<CheckboxPreference> shows an item with a checkbox for a setting that is either enabled or disabled. The saved value is a boolean (true if it’s checked). I have used this preference for enabling or disabling location data with the recorded data.

<ListPreference> opens a dialog with a list of radio buttons. The saved value can be any one of the supported value types. I have used this preference to allow the user to choose between multiple sensor types.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
       <EditTextPreference
           android:key="setting_lux_update_period"
           android:title="@string/update_period"
           android:dialogTitle="@string/update_period"
           android:defaultValue="1000"
           android:dialogMessage="Please provide time interval(in ms) at which data will be updated"
           android:summary="Update period is 900ms"/>

       <EditTextPreference
           android:key="setting_lux_high_limit"
           android:title="High Limit"
           android:dialogTitle="High Limit"
           android:defaultValue="2000"
           android:dialogMessage="Please provide maximum limit of LUX value to be recorded"
           android:summary="High Limit is 2000 Lux"/>

       <CheckBoxPreference
           android:defaultValue="false"
           android:key="include_location_sensor_data"
           android:summary="Include the location data in the logged file"
           android:title="Include Location Data" />
</PreferenceScreen>

The above XML file will produce a layout as shown in Figure 1 below:

Figure 1 shows a preview of settings layout in Android Studio

 

Step 3 Implementing the backend of setting interface

As android Documentation clearly states:

As your app’s settings UI is built using Preference objects instead of View objects, you need to use a specialized Activity or Fragment subclass to display the list settings:

  • If your app supports versions of Android older than 3.0 (API level 10 and lower), you must build the activity as an extension of the PreferenceActivity class.
  • On Android 3.0 and later, you should instead use a traditional Activity that hosts a PreferenceFragment that displays your app settings.

Therefore, I first created an Activity named “SettingsActivity” which will act as a host for a Fragment. This gist contains code for SettingsActivity which I defined in the PSLab android app which will show the Setting Fragment.

Now, for setting fragment I have created a new fragment and name it “LuxMeterSettingsFragment” and make that fragment class extends the PreferenceFragmentCompat class and for which I needed to add this import statement.

import android.support.v7.preference.PreferenceFragmentCompat;

And then inside the SettingsFragment, I overridden the following method like this:

@Override
public void onCreatePreferences(Bundle savedInstanceState,
                                String rootKey) {
    setPreferencesFromResource(R.xml.lux_meter_settings, rootKey);
}

This method is called when the Android is creating the Preferences that is when we need to call the method ‘setPreferencesFromResource()’ and pass the resource file in which we have defined our preferences along with the root key which comes as a parameter. Here, the Android will create preferences referred by the key which we have provided and initialize it to the default value in the default SharedPreferences file.

Step 4 Providing setting option to navigate to setting activity

First I included a setting option in the menu file which is getting inflated in the Lux Meter Activity toolbar as shown in below code.

<?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">
  
   ...

   <item
       android:id="@+id/settings"
       android:title="@string/lux_meter_settings"
       app:showAsAction="never" />
</menu>

Then, heading over to  Lux Meter Activity in the onOptionItemSelected() method I added below code in which I created intent to open Setting Activity when the setting option is selected.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
case R.id.settings:
   Intent settingIntent = new Intent(this, SettingsActivity.class);
   settingIntent.putExtra("title", "Lux Meter Settings");
   startActivity(settingIntent);
   break;
}

After this step, we can see the settings option in Lux Meter Activity as shown in Figure 2

Figure 2 shows the setting option in the overflow menu

 

To see if its working opens the app -> open Lux Meter Instrument-> Open Overflow Menu -> click on Lux Meter Setting Option to open settings.

Here we can the Lux Meter Setting as shown by the Figure 3.

Figure 3 shows the screenshot of the Lux Meter Setting activity on the actual device

 

Step 5 Implementing listener for change in Preferences item

Now we can also implement a listener by making SettingsFragment class implement SharedPreference.OnSharedPreferenceChangeListener interface and then overriding the onSharedPreferenceChanged() method.

Now, to register this listener with the Preference Screen which we will do it in the onResume() method of the fragment and deregister the listener in the onPause() method to correspond with the lifecycle of the fragment.

@Override
public void onResume() {
   super.onResume();
      
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
   super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}

Thus we have successfully implemented settings for the Lux Meter Instrument.

Resources

  1. Adding Settings to an App – Google Developer Fundamental Course Article on how to add settings.
  2. Gist – Setting Activity implementation – The gist used for setting activity in the app
Continue ReadingImplementing Settings for Lux Meter Instrument in PSLab Android App

Using DialogFragment to show sales data in Open Event Organizer App’s Events List

In the Open Event Organizer Android App The events list shows the list of events, but organizers often need to compare sales of different events fast. Until now they had to select each and every item and go to the dashboard to see the sales. This is about to change in the Pull Request #1063. It provides an intuitive way to show ticket sales by showing a dialog box upon long pressing event list items.

To implement it, we needed a BaseDialogFragment class which would implement Injectable and handle the presenter life cycle for us.

public class BaseDialogFragment<P extends BasePresenter> extends DialogFragment implements Injectable {

BaseDialogFragment class is very similar to the BaseFragment class, except that it extends DialogFragment instead of Fragment, And the new class SalesSummaryFragment is also similar to any other fragment class we are using.

When an item in the events list is clicked, the long click listener for the events’ list adapter opens the SalesSummaryFragment for the corresponding event, and when the data completes loading it calls ItemResult#showResult and binds the event with the data.

@Override
public void showResult(Event event) {
    binding.setEvent(event);
    binding.executePendingBindings();
}

The  SalesSummaryPresenter  is the almost the same as the TicketsPresenter, since it is loading ticket sales. In its onStart() method, it calls the method  loadDetails()  which on completion calls  analyseSoldTickets()  on  ticketAnalyser, which basically updates an event’s analytics after calculations.

public class SalesSummaryPresenter extends AbstractDetailPresenter<Long, SalesSummaryView> {
    …

    public void loadDetails(boolean forceReload) {
        …
        getEventSource(forceReload)
            …
            .subscribe(attendees -> {
                this.attendees = attendees;
                ticketAnalyser.analyseSoldTickets(event, attendees);
            }, Logger::logError);
    }

   …

    @Override
    public void showResult(Event event) {
        binding.setEvent(event);
        binding.executePendingBindings();
    }
}

For  SalesSummaryFragment , we are using the layout  fragment_sales_summary.xml. And this layout mainly makes use of the layout ticket_analytics_item.xml which is a layout designed to produce the three circular views to show the data.

<include
    layout="@layout/ticket_analytics_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    bind:color='@{"red"}'
    bind:completed="@{event.analytics.soldDonationTickets}"
    bind:ticketName="@{@string/ticket_donation}"
    bind:total="@{event.analytics.donationTickets}" />

This is how the result looks like:

References:
Open Event Orga Android App: Pull Request #1063:
https://github.com/fossasia/open-event-orga-app/pull/1063

Codepath guides: Using Dialog Fragment
https://github.com/codepath/android_guides/wiki/Using-DialogFragment

Continue ReadingUsing DialogFragment to show sales data in Open Event Organizer App’s Events List