Adding Multiple Attendees inside an Order in Open Event Android

Orders endpoint allows multiple attendees to be passed in the body of post request which essentially means that and an order may contain tickets for different attendees.

This blog post will guide you on how multiple attendees under an Order is handled and implemented in Open Event Android.

An attendee as the name implies gives us information about how many people are attending any particular event. Let’s say if a User selects two tickets with 3 and 7 quantity total 10 attendees will be generated and can be put inside one Order request.

Whenever multiple tickets are selected by the user following function is called to create Attendee

fun createAttendees(attendees: List<Attendee>, country: String?, paymentOption: String) {
       this.country = country
       this.paymentOption = paymentOption
       this.attendees?.clear()
       attendees.forEach {
           createAttendee(it, attendees.size)
       }
   }

 

Create attendees accepts a list of attendees and calls the createAttendees for every attendee in the list. Actual attendee creation is handled in createAttendee. The above function also set up the country and payment option as provided by the user.

fun createAttendee(attendee: Attendee, totalAttendee: Int) {        compositeDisposable.add(attendeeService.postAttendee(attendee)
               .subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .doOnSubscribe {
                   progress.value = true
               }.doFinally {
                   progress.value = false
               }.subscribe({
                   attendees?.add(it)
                   if (attendees != null && attendees?.size == totalAttendee) {
                       loadTicketsAndCreateOrder()
                   }
                   message.value = “Attendee created successfully!”
                   Timber.d(“Success! %s”, attendees?.toList().toString())
               }, {
                   message.value = “Unable to create Attendee!”
                   Timber.d(it, “Failed”)
               }))
   }

Create an Attendee on the other hand deals with creating individual attendees on the server. It calls the service function attendeeService.postAttendee with attendee as the parameter if the post request made is succeeds the attendee returned from the API is added to the list of attendees, when the size of list of attendees become equal to the list of attendees that had to be generated loadTicketsAndCreateOrder is called which takes care of getting charges for user and creating an order. If the post request for the attendee is failed a message is displayed and a log is added to console with error details.

fun loadTicketsAndCreateOrder() {
       if (this.tickets.value == null) {
           this.tickets.value = ArrayList()
       }
       this.tickets.value?.clear()
       attendees?.forEach {
           loadTicket(it.ticket?.id)
       }
   }

Load tickets and create order also works in a similar way as of creating attendees as there are two functions in here also one of which takes a list of attendees and calls the other function which gets data for individual ticket items passes as the parameter. Here ticket details for every attendee are fetched using the loadTicket function which takes ticket id corresponding to an attendee (using it.tickets?.id) and returns the Ticket object. Given below is the implementation for loadTicket method.

fun loadTicket(ticketId: Long?) {        compositeDisposable.add(ticketService.getTicketDetails(ticketId)
               .subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe({
                   tickets.value?.add(it)
                   Timber.d(“Loaded tickets! %s”, tickets.value?.toList().toString())
                   if (tickets.value?.size == attendees?.size) {
                       createOrder()
                   }
               }, {
                   Timber.d(it, “Error loading Ticket!”)
               }))
   }

Load ticket uses the service method to fetch the ticket details using the passed ticket id and the returned ticket object is added to the list of ticket. When the size of the ticket becomes equal to the size of total no of attendees which means that ticket details of each attendee are fetched createOrder is called which is responsible for creating order. If anything fails in fetching tickets log is statement is printed along with error information.

fun createOrder() {

val order = Order(getId(), paymentMode, country, “pending”, amount, attendees = attendeeList, event = EventId(eventId))


           compositeDisposable.add(orderService.placeOrder(order)
                   .subscribeOn(Schedulers.io())
                   .observeOn(AndroidSchedulers.mainThread())
                   .doOnSubscribe {
                       progress.value = true
                   }.doFinally {
                       progress.value = false
                   }.subscribe({
                       orderIdentifier = it.identifier.toString()
                       message.value = “Order created successfully!”
                       Timber.d(“Success placing order!”)
                       if (it.paymentMode == “free”)
                           paymentCompleted.value = true
                   }, {
                       message.value = “Unable to create Order!”
                       Timber.d(it, “Failed creating Order”)
                   }))
       } else {
           message.value = “Unable to create Order!”
       }
   }

Create order function is the final step in the process it takes the list of attendees, payment modes, amount and other information related to order and create an order object out of it which is then passed to the service layer method which makes a post request to generate an order on the server. In case of an error or success whole message is logged and toast is displayed. This method is also involved with showing progress bar once this is called and closing the same when the procedure is completed.

Finally, in case of free events the user is taken to order completed screen and in case of paid events the user is taken to the payment screen where the user has to provide payment related information

Resources

Continue ReadingAdding Multiple Attendees inside an Order 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

Speaker details in the Open Event Orga App

The Open Event Organiser Android App is currently released in the Alpha phase on the Google Play Store here. This blog post explains how the speaker details feature has been implemented in the app.

Model

The model for Speaker is pretty straightforward. It includes the personal details of the speaker such as name, biography, country, social media profiles, designation etc. Apart from these details, every instance of speaker is associated with a single event. A speaker will also have multiple instances of sessions. Full implementation of the speaker’s model can be found here.

Network Call

We use Retrofit in order to make the network call and Jackson Factory to deserialize the data received from the call into an instance of the speaker model. The following endpoint provides us with the required information:

https://open-event-api-dev.herokuapp.com/speakers/{speaker_id}

Repository

In any typical android application using both network calls and data persistence, there is a need of a repository class to handle them. Speaker Repository handles the network call to the API in order to fetch the speaker details. It then saves the data returned by the api into the database asynchronously. It also ensures that we send the latest data that we have stored in the database to the view model. Given below is the full implementation for reference:

@Override
    public Observable<Speaker> getSpeaker(long speakerId, boolean reload) {
        Observable<Speaker> diskObservable = Observable.defer(() ->
            repository
                .getItems(Speaker.class, Speaker_Table.id.eq(speakerId)).take(1)
        );

        Observable<Speaker> networkObservable = Observable.defer(() ->
            speakerApi.getSpeaker(speakerId)
                .doOnNext(speaker -> repository
                    .save(Speaker.class, speaker)
                    .subscribe()));

        return repository
            .observableOf(Speaker.class)
            .reload(reload)
            .withDiskObservable(diskObservable)
            .withNetworkObservable(networkObservable)
            .build();
    }

ViewModel

The View Model is responsible for fetching the necessary details from the repository and displaying it in the view. It handles all the view binding logic. The most important method in the SpeakerDetailsViewModel is the getSpeakers method. It accepts a speaker id from the fragment, queries the repository for the details of the speaker and returns it back to the fragment in the form of a LiveData. Below is the full implementation of the getSpeakers method:

protected LiveData<Speaker> getSpeaker(long speakerId, boolean reload) {
        if (speakerLiveData.getValue() != null && !reload)
            return speakerLiveData;

        compositeDisposable.add(speakerRepository.getSpeaker(speakerId, reload)
            .compose(dispose(compositeDisposable))
            .doOnSubscribe(disposable -> progress.setValue(true))
            .doFinally(() -> progress.setValue(false))
            .doOnNext(speaker -> speakerLiveData.setValue(speaker))
            .flatMap(speaker -> sessionRepository.getSessionsUnderSpeaker(speakerId, reload))
            .toList()
            .subscribe(sessionList -> sessionLiveData.setValue(sessionList),
                throwable -> error.setValue(ErrorUtils.getMessage(throwable))));

        return speakerLiveData;
    }

We add the disposable to a composite disposable and dispose it in the onCleared method of the View Model. The full implementation of the View Model can be found here.

Fragment

The SpeakerDetailsFragment acts as the view and is responsible for everything the user sees on the screen. It accepts the id of the speaker whose details are to be displayed in the constructor. When an instance of the fragment is created it sets up it’s view model and inflates it’s layout using the Data binding framework.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
   context = getContext();
   binding = DataBindingUtil.inflate(inflater, R.layout.speaker_details_fragment, container, false);
   speakerDetailsViewModel = ViewModelProviders.of(this, viewModelFactory).get(SpeakerDetailsViewModel.class);
   speakerId = getArguments().getLong(SPEAKER_ID);

   AppCompatActivity activity = ((AppCompatActivity) getActivity());
   activity.setSupportActionBar(binding.toolbar);

   ActionBar actionBar = activity.getSupportActionBar();
   if (actionBar != null) {
       actionBar.setHomeButtonEnabled(true);
       actionBar.setDisplayHomeAsUpEnabled(true);
   }

   return binding.getRoot();
}

In the onStart method of the fragment we load the data by calling the getSpeaker method in the view model. Then we set up the RecyclerView for the sessions associated with the speaker. Lastly we also set up the refresh listener which can be used by the user to refresh the data.

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

   speakerDetailsViewModel.getProgress().observe(this,this::showProgress);
   speakerDetailsViewModel.getError().observe(this, this::showError);
   loadSpeaker(false);

   setupRecyclerView();
   setupRefreshListener();
}

Once the data is returned we simply set it on the layout by calling setSpeaker on the binding.

@Override
public void showResult(Speaker item) {
   binding.setSpeaker(item);
}

The full implementation of the SpeakerDetailsFragment can be found here.

Sessions Adapter

The SessionsAdapter is responsible for handling the RecyclerView of sessions associated with the speaker. The most important method in the adapter is the setSessions method. It accepts a list of sessions and shows it as the contents of the recycler view. It uses the DiffUtil.calculateDiff method to create a DiffResult which will be used by the adapter to figure out where to insert items.

protected void setSessions(final List<Session> newSessions) {
        if (sessions == null) {
            sessions = newSessions;
            notifyItemRangeInserted(0, newSessions.size());
        } else {
            DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                @Override
                public int getOldListSize() {
                    return sessions.size();
                }

                @Override
                public int getNewListSize() {
                    return newSessions.size();
                }

                @Override
                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                    return sessions.get(oldItemPosition).getId()
                        .equals(newSessions.get(newItemPosition).getId());
                }

                @Override
                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                    return sessions.get(oldItemPosition).equals(newSessions.get(newItemPosition));
                }
            });
            sessions = newSessions;
            result.dispatchUpdatesTo(this);
        }
    }

The full implementation of the Adapter can be found here.

References

Continue ReadingSpeaker details in the Open Event Orga App

Ticket Quantity Spinner in Open Event Android

Spinners are basically drop down menu which provides an easy way to select an item from a set of items. Spinner is used in Open Event Android to allow user select quantity of tickets as shown in the image above. This blog post will guide you on how its implemented in Open Event Android.

Add Spinner to the XML file

<Spinner
           android:id=“@+id/orderRange”
           android:layout_width=“30dp”
           android:layout_height=“30dp”
           android:spinnerMode=“dialog”
           android:textSize=“@dimen/text_size_small” />

The above code spinnet will create a spinner type view with 30dp height and width, spinnerMode can be dialog or dropDown following are example spinner for both of the modes left being dialog type and right dropDown type.

                

(Image Source: Stack Overflow)

Set Up Adapter and Populate Data on Spinner

To show the list of acceptable Quantities for a Ticket, create an ArrayList of String and add all values from ticket.minOrder to ticket.maxOrder along with a zero option. Since ArrayList is of String and the values are integer they need to be converted to String using Integer.toString(i) method. Following code snippet will give you more understanding about it.

val spinnerList = ArrayList<String>()
           spinnerList.add(“0”)
           for (i in ticket.minOrder..ticket.maxOrder) {
               spinnerList.add(Integer.toString(i))
           }

We will also need to set up an onItemSelectedListener to listen for Item Selections and override onItemSelected and onNothingSelected functions in it. Whenever an item is selected onItemSelected is called with arguments such as view, position of the selected item, id etc, these can be used to find the selected Quantity from the ArrayList of acceptable Quantities.      

    itemView.orderRange.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
               override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
                   itemView.order.text = spinnerList[pos]
                   selectedListener?.onSelected(ticket.id, spinnerList[pos].toInt())
               }

               override fun onNothingSelected(parent: AdapterView<*>) {
               }
           }

Since Spinner is an Adapter Based view we need to create a SpinnerAdapter and attach it with the Spinner. If you want to create custom Spinners you would have to create a custom adapter for it along with a layout for its row elements or else one can use library-provided layout. Given below is the implementation for library-provided spinner row type. There are different options available for row type we are using R.layout.select_dialog_singlechoice because we need single selectable Quantity spinner along with Radio button for every ticket.

     itemView.orderRange.adapter = ArrayAdapter(itemView.context, R.layout.select_dialog_singlechoice, spinnerList)

Resources

Continue ReadingTicket Quantity Spinner in Open Event Android

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

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

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

Retrofit2 Rxjava2 Error Response Handling in Open Event Organizer App

In the Open Event Organizer Android app the challenge is to provide user, a readable error description for the input requests. The Organizer App was showing a coded message which was understandable only to a programmer making it unfriendly to the common user. The blog describes how we tackled this problem and implemented a mechanism to provide user friendly error messages for user requests (if any).

Let’s consider the scenario when an organizer want to create an Event or maybe a Ticket so what he has to do is fill out a form containing some user input fields and click on submit button to send the data to the server which in turn sends a response for the request. If the information is valid the server sends a HTTP 201 Response and user gets feedback that the Event/Ticket is created. But if the information is not valid the user gets feedback that there is HTTP 422 error in your request. There is no readable description of error provided to the user.

To handle this problem we will be using Retrofit 2.3.0 to make Network Requests, which is a REST client for Android and Java by Square Inc. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based Web Service. Further, we will be using other awesome libraries like RxJava 2.1.10 (by ReactiveX) to handle tasks asynchronously, Jackson, Jasminb-Json-Api in an MVP architecture. Let’s move on to the code.

Retrofit to the rescue

Now we will see how we can extract the details about the error response and provide user a better feedback rather than just throwing the error code.

Firstly let’s make the Request to our REST API Server using Retrofit2.

@POST(“faqs”)
Observable<Faq> postFaq(@Body Faq faq);

Here we are making a POST request and expecting a Observable<Faq> Response from the server.The  @Body annotation indicates the Request Body is an Faq object.

Please note that Observable used here is ReactiveX Observable so don’t confuse it with java.util Observable.

public class Faq {
@Id(LongIdHandler.class)
public Long id;  @Relationship(“event”)
@ForeignKey(stubbedRelationship = true,

onDelete = ForeignKeyAction.CASCADE)
public Event event;

public String question;
public String answer;
}

Let’s say the API declares both question and answer as mandatory fields  for creation of an Faq. We supply the following input to the app.

question = “Do I need to buy a Ticket to get In ?”;
answer = null

We used RxJava to make an asynchronous request to server. In case of error we will get a Retrofit throwable and we will pass that on to ErrorUtils.java which will do all the processing and return a readable error message.

faqRepository
.createFaq(faq)
.compose(dispose(getDisposable()))
.compose(progressive(getView()))  .doOnError(throwable ->

getView().showError(ErrorUtils.getMessage(throwable)))
.subscribe(createdFaq -> {
getView().onSuccess(“Faq Created”);
getView().dismiss();
}, Logger::logError);

Now we will extract the ResponseBody from the Retrofit throwable.

ResponseBody responseBody = ((HttpException) throwable)

.response().errorBody();
return getErrorDetails(responseBody);

The ResponseBody is a JSON containing all the information about the error.

{
“errors”: [
{
“status”: “422”,
“source”: {
“pointer”: “/data/attributes/answer”
},
“detail”: “Missing data for required field.”,
“title”: “Validation error”
}
],
“jsonapi”: {
“version”: “1.0”
}
}

In order to provide better feedback to user regarding the error we have to parse the response JSON and extract the pointed field (which in this case is – “answer”) and then combine it with the value corresponding to the “detail” key. Following is the relevant section from ErrorUtils.java (for viewing the complete ErrorUtils.java please refer here).

public static String getErrorDetails(ResponseBody responseBody) {
try {
JSONObject jsonObject = new JSONObject(responseBody.string());
JSONObject jsonArray = new    JSONObject(jsonObject.getJSONArray(“errors”).get(0).toString());
JSONObject errorSource =

new JSONObject(jsonArray.get(“source”).toString());

try {
String pointedField =

getPointedField(errorSource.getString(“pointer”));
if (pointedField == null)
return jsonArray.get(“detail”).toString();
else
return jsonArray.get(“detail”).toString()

.replace(“.”, “”) + “: ” + pointedField;
} catch (Exception e) {
return jsonArray.get(“detail”).toString();
}

} catch (Exception e) {
return null;
}
}

public static String getPointedField(String pointerString) {
if (pointerString == null || Utils.isEmpty(pointerString))
return null;
else {
String[] path = pointerString.split(“/”);
if (path.length > 3)
return path[path.length – 1];
else
return null;
}
}

The final error message returned by ErrorUtils in case of HTTP 422 –

Missing data for required field: answer

Similar approach can be followed for extracting the error “title” or “status” .

References

  1. Official documentation of Retrofit 2.x http://square.github.io/retrofit/
  2. Official documentation for RxJava 2.x https://github.com/ReactiveX/RxJava
  3. Codebase for Open Event Organizer App on Github https://github.com/fossasia/open-event-orga-app
Continue ReadingRetrofit2 Rxjava2 Error Response Handling in Open Event Organizer App

Producing Waveforms using Wave Generator module in the PSLab Android App

This blog will demonstrate how to produce different waveforms using the Wave Generator module in the PSLab android app and view them on the Oscilloscope.

The Wave Generator in PSLab android app is simple to use and has a UI which is similar to physical commodity wave generators. It is capable of producing different waveforms like sine, sawtooth and square wave.

Apparatus Required

Before getting started with the wave generator we require the following items:

  1. PSLab device
  2. An android phone with PSLab app installed in it.
  3. USB cable (Mini B)
  4. OTG(On the Go) wire
  5. Some connecting wires having pins at both ends

Understanding the Wave Generator Pins

Figure 1 shows the pin diagram of the PSLab device

Let me briefly explain the use of the pins that are going to be used in the Wave generator module:

S1 and S2 pins

The PSLab device contains two pins (S1, S2) which are capable of producing two independent analog waveforms (sine,  sawtooth) having different frequencies and phase offset. The frequency range is from 10Hz to 5Khz.

SQR1, SQR2, SQR3 and SQR4 pin

The SQR1 pin is used for producing the square waveform and all the SQ pins can be used together to produce four different PWM signal having the same frequency. These PWM signal can have a different duty cycle and phase.

CH1, CH2 and CH3 pin

The CH pins are used by the oscilloscope in the  PSLab android app to monitor waveform signals produced by the wave generator pins. They can be used together to simultaneously monitor multiple waveforms.

Setting up the Device

We need to connect the PSLab device with the mobile phone as shown in Figure 2 which can be done by following steps:

  1. Connect a micro USB(Mini B) to the PSLab device.
  2. Connect the other end of the micro USB cable to the OTG.
  3. Connect the OTG to the phone.
Figure 2 shows the connection of the PSLab device with the smartphone

Producing Waveforms

Now, once the device has been properly connected to the device (which is shown at the top right corner of the app), then in the instruments page scroll down to the Wave Generator card and click on it to open the WaveGenerator activity.

Figure 3 shows the instruments containing card view to all the instruments and icon to show device status

Here you will see a screen like shown in Figure 4 containing two monitors and a controlling panel with lots of buttons. Here the Waveform panel is used to control the S1 and S2 pins whose properties are shown on the left monitor screen and the Digital panel is used to control the SQR pins whose properties are shown on the right monitor screen.

Figure 4 shows the UI of the Wave Generator Activity

For sine/sawtooth wave:

Connect the S1 pin to the CH1 pin using a connecting wire, then in the Waveform panel select the Wave1 button, choose the type of waveform(either sine or sawtooth), then click on the Freq button to change the frequency of the wave, then use the Seek bar or the up/down arrow buttons to change the value of frequency and then press the set button to set the frequency for the S1 pin as shown below:

Figure 5 The GIF shows the setting of the properties of the W1 pin in the UI

Now, click the view button at bottom right corner, this will directly open the Oscilloscope provided by the PSLab android app .

Once the oscilloscope is open, check the CH1 pin from the panel in the bottom and we can see the sine wave in the monitor shown by the screen in Figure 6 and Figure 7

Figure 6 shows the screenshot of oscilloscope showing the sine wave
Figure 7 shows the screenshot of the oscilloscope showing sawtooth wave

Similarly, if you want to see two sine waves connect the S1 pin to the CH1 and connect the S2 pin to the CH2 channel , choose the wave-type for both pin, set the frequencies for both of the waves, here you can also set the phase difference between the two waves, for setting phase difference first click on Wave2 button it will enable the phase button, then click on the Phase button and set the value of phase with the help of the Seek bar.

For Square Wave

Connect the CH1 pin to the SQ1 pin, after making the connection head over to the Digital panel in the Wave Generator, ensure that the mode is selected to square, now click on the Freq button in the digital panel and set the frequency of the square wave with the help of Seek bar, then click on the Duty button and set the value of duty cycle for the square wave as shown below:

Figure 8 The GIF shows the setting of properties for producing square wave from SQ1 pin

Now, once the square wave has been set click on the view button, the oscilloscope will open then select the CH1 pin and you can see the square wave on the monitor as shown by the screen in Figure 9.

Figure 9 shows the screenshot of the square wave as shown in the oscilloscope

Thus we have produced different waveforms using PSLab wave generator module.

Resources

PSLab device pin diagram  – https://github.com/fossasia/pslab-artwork/blob/master/Sticker/pslabdesign.png

Youtube Video Screencast for Wave Generator – https://www.youtube.com/watch?v=NC2T5kElWbE&t=1s

Continue ReadingProducing Waveforms using Wave Generator module in the PSLab Android App