Creating sponsors layout in Open Event Front-end

In this blog I discuss how we implemented sponsors layout in Open Event Front-end. The sponsors are fetched from Orga Server API and is handled using Ember JS in the Front-end.

The fetched sponsor is an array of JSON objects which need to be grouped based on the type of the sponsor which is done using the lodash library. How do we implement it?

Creating sponsor-list & sponsor-item components

We create two components sponsor-list which contains all the sponsors & sponsor-item which is used to render each sponsor.

ember g component sponsor-list

ember g component sponsor-item

Grouping the sponsors by type

The API response return an array of the sponsors of the event as :

 sponsors: [
   { name: 'Sponsor 2', 
     Url: '#', 
     logoUrl: 'http://placehold.it/150x60', 
     level: 2, 
     type: 'Gold Sponsor', 
     description: '' 
   }, 
   { name: 'Sponsor 1', 
     url: '#', 
     logoUrl: 'http://placehold.it/150x60', 
     level: 1, 
     type: 'Silver Sponsor', 
     description: '' 
   }
 ]

This response is the list of all sponsors, which is not grouped by the type of the sponsor. We sort and group the array and return a JSON object in the sponsor-list component.

import Ember from 'ember';
import { orderBy, groupBy } from 'lodash';

const { Component, computed } = Ember;

export default Component.extend({

 sponsorsGrouped: computed('sponsors.[]', function() {
   return groupBy(orderBy(this.get('sponsors'), 'level'), 'type');
 })
});

We use lodash orderBy to sort the sponsors by the level and groupBy to convert the array into an JSON object of the grouped sponsors. We compute the grouped object using ember computed property.

Rendering sponsors in public event route

The sponsor array is passed to the sponsors-list component where the sponsors are sorted and grouped. We pass each sponsor from the sponsorsGrouped to the sponsor-item component which renders the logo of the sponsor.

sponsor-list.hbs

<h3 class="ui header">{{t 'Sponsors'}}</h3>
{{#each-in sponsorsGrouped as |key sponsors|}}
 <h4 class="ui header">{{key}}</h4>
 <div class="ui three column stackable grid">
   {{#each sponsors as |sponsor|}}
     {{public/sponsor-item sponsor=sponsor}}
   {{/each}}
 </div>
{{/each-in}}

sponsor-item.hbs

<a href="{{sponsor.url}}">
 <img src="{{sponsor.logoUrl}}" class="ui image  sponsor-image" alt="{{sponsor.name}}">
</a>

The outcome of this change on the Open Event Front-end now looks like this:

Thank you for reading the blog, you can check the source code for the example here.

Resources:

 

Continue ReadingCreating sponsors layout in Open Event Front-end

Data Scraping using Python with BeautifulSoup4 for Open Event samples

was creating samples for Open Event Android and Open Event Webapp when the idea of web scraping through scripts stuck me.

I chose Python for web scraping. But what’s more to using Python other than it’s user-friendliness, easy syntax and speed? It’s the wide variety of open-source libraries that come along with it!

One of them is the BeautifulSoup library for extracting data from XML and HTML tags and files. It provides with ways to search and sort through webpages, find specific elements that you need and extract them in objects of your preference.

Apart from BeautifulSoup, another module that can be easily neglected but is of great use is:- urllib2. This module is to open URLs that are fed to it.

Hence, with a combination of the above 2 Python modules, I was able to scrape off the main sample data like speakers, sessions, tracks, sponsors and so on for PyCon 2017 (sample that I created recently) in a quicker and more efficient way.

I will now talk about these modules in greater detail and their use in my web scraping scripts.

Web Scraping with BeautifulSoup

Start by installing Beautifulsoup module in your Python environment. I believe that it will be best to install it using  pip.  Run the following command to install Beautifulsoup:-

pip install beautifulsoup4

Next up, you need to import the module to your script by adding the following line:-

from bs4 import BeautifulSoup

Now you need to open a webpage using the module in the following way. For that we first have to open the URL of the page that is being scraped. We use urllib2 to do that.

import urllib2
site=urllib2.urlopen("https://us.pycon.org/2017/sponsors/")

Next we use BeautifulSoup to get the contents of the webpage in the following way:-

website=BeautifulSoup(site)

Suppose that, next I want to obtain the data inside all the <div> tags in the webpage that have their class=”sponsor-level”.

I will use BeautifulSoup in the following way along with the ‘find_all’ method to do so :-

divs=website.find_all("div",{'class':'sponsor-level'})

Here, divs will now be a special <list> type element containing all the HTML in the div tags of class=’sponsor-level’. Now I can do whatever with I want with this list. Parse it in any way, store it any kind of data type without any difficulty.

BeautifulSoup also helps in accessing the children tags or elements of different HTML or XML tags using the ‘dot’ (.) operator. Following example will make it more clear:-

tag = soup.b

Here, ‘tag’ will contain all the  <b> elements of the parent element that is stored in ‘soup’ variable.

To access attributes of HTML or XML tags, there is a special way as demonstrated below:-

To access the ‘href’ attribute of the first <a> tag in the webpage:-

soup.find_all(“a”)[0][‘href’]

A sample script…

Following is a script that I used to obtain sponsors from the PyCon 2017 site in json format.

import urllib2

site=urllib2.urlopen("https://us.pycon.org/2017/sponsors/")

from bs4 import BeautifulSoup

s=BeautifulSoup(site)

divs=s.find_all("div",{'class':'sponsor-level'})

spons=[]

for level in divs:

levelname=level.find_all("h2")[0].string

level_sponsors=level.find_all("div",{'class':'sponsor'})

for level_sponsor in level_sponsors:

anchor=level_sponsor.find_all("a")

dictionary={}

dictionary['id']=""

dictionary['name']=anchor[1].string

dictionary['level']=""

dictionary['sponsor_Type']=levelname

dictionary['url']=anchor[0]['href']

dictionary['description']=level_sponsor.find_all("p")[1].string

spons.append(dictionary)

print spons

There are still a lot of other functionalities of BeautifulSoup that are available and can make your life easier. These can be found here in the official BeautifulSoup documentation.

Continue ReadingData Scraping using Python with BeautifulSoup4 for Open Event samples

Implementing PNG Export of Schedule in Open Event Webapp

Fortunately for us, we don’t have to implement it from scratch (which would have been extremely difficult and time-consuming). Enter html2canvas library. It renders an element onto the canvas after which we can convert it into an image. I will now explain how we implemented png export in the calendar mode. You can view the whole schedule template file here

Here is a screenshot of calendar or grid view of the schedule. Currently selected date is 18th Mar, Saturday. The PNG Export button is on the top-right corner beside the ‘Calendar View’ button.

32fa6d15-59da-4521-b348-6c01f6af7825.png

Here is a little excerpt of the basic structure of the calendar mode of the sessions. I have given an overview of it in the comments.

<div class="{{slug}} calendar">
 <!-- slug represents the currently selected date -->
 <!-- This div contains all the sessions scheduled on the selected date -->
 <div class="col-md-12 paddingzero">
   <!-- Contain content related to current date and time -->
 </div>
 <div class="calendar-content">
   <div class="times">
     <!-- This div contains the list of all the session times on the current day -->
     <!-- It is the left most column of the grid view which contains all the times →
     <div class="time">
       <!-- This div contains information about the particular time -->
     </div>
   </div>
   <div class="rooms">
     <!-- This div contains all the rooms of an event -->
     <!-- Each particular room has a set of sessions associated with it on that particular date -->
     <div class="room">
       <!-- This div contains the list of session happening in a particular room -->
       <!-- Session Details -->
     </div>
   </div>
 </div>
</div>

Now, let us see how we will actually capture an image of the HTML element shown above. Here is the code related to it:

$(".export-png").click(function() {
 if (isCalendarView === true) {

   $('.calendar').each(function() {
     if ($(this).attr('class').split(' ').indexOf('hide') <= 0) {

       $timeline = $(this);
       initialWidth = $timeline.width();
       numberOfChildElements = $timeline.find('.rooms')[0].childElementCount;
       numberOfChildElements = numberOfChildElements - 1;
       widthOfChild = $timeline.find('.room').width();
       canvasWidth = numberOfChildElements * widthOfChild + 50;
       $timeline.width(canvasWidth);
     }
   });
 }

 html2canvas($timeline, {
   onrendered: function(canvas) {
     canvas.id = "generated-canvas";
     canvas.toBlob(function(blob) {
       saveAs(blob, '' + $timeline.attr('class') + '.png');
     });
   },
 });
 $timeline.width(initialWidth);
});

Note that this initial width calculated is the width which is visible to us on the screen. In reality, the element might be scrollable and its actual width might be different. If we render the element using the initial width, we would not be able to see the full contents of that element. It will not show the whole view. Hence we need to calculate the actual width.Let us see what is going on in this code. When the user clicks on the export PNG button, we check whether we are in the calendar mode or not. If yes, then we proceed further. We then see which date is currently selected and accordingly select that div. After selecting it, we then get the initial width of that element.

So, we check all the child elements inside it, get their count and width and then calculate the actual width of the parent element based on it. Temporarily, we set this actual width as the width of the session element and pass it to the html2canvas function. That in turn, renders the whole element onto a canvas. After it has been successfully rendered onto the canvas, we save it to as an image and present a download box to the user for downloading that image.

Here is the download pop-up box

bfbd12c2-f6ed-4dae-8485-bfd1638faf74.png

And this is the downloaded PNG Image. Click on it for a higher resolution!

Screenshot from 2017-07-03 00-31-56.png

Resources

Continue ReadingImplementing PNG Export of Schedule in Open Event Webapp

Creating a component for achieving an n-times running loop in Open Event Frontend

This blog article will illustrate how to to make a component in ember JS which allows you to iterate over a block of statements n-times which is being used in Open Event Frontend This is of great utility as the default ‘each’ helper of ember only allows us to iterate over objects and arrays, and not simply as a loop.So we begin by generating a component and calling it `n-times` and it is currently being used in the open event front-end project.

Even before implementing it, it is obvious to us that the content inside the loop can be any thing and the component should be able to iterate over them repeatedly. Thus this component will need to have a {{yield}} block inside it, and thus will always have to be used in block form, to allow the user to enter the content that we need to iterate over.
We begin by simply generating the boiler plate code of the component via Ember CLI.

$ ember generate component n-times

Now we proceed with the js code of the component. The only thing we need to ensure here is that no extra html tags are introduced because of our component, else it might break the flow of tags or disrupt the styling. Hence we make use of the tagName property to achieve the same.
The final code looks something like this:

import Ember from 'ember';

const { Component } = Ember;

export default Component.extend({
 tagName: ''
});

We just needed to make the tagName none as we don’t want an extra div. And this is the template code for the same component
For the template part we simply enclose the {{yield}} block inside each loop, and the value of times is expected to be passed at the time of calling the component.

{{#each (range 0 times) as |number|}}
{{yield number}}
{{/each}}

An important thing to note, we cannot have made this as a helper because the block form of helpers has been deprecated since Ember has updated hence component was the obvious choice.

This may now be simply used as

{{#n-times times=5}}

{{ui-checkbox label=(t 'Create')}}

{{ui-checkbox label=(t 'Read')}}

{{ui-checkbox label=(t 'Update')}}

{{ui-checkbox label=(t 'Delete')}}

{{/n-times}}

 

Resources

*Featured image is captured by the author of this post, it is under public domain without any restrictions.

Continue ReadingCreating a component for achieving an n-times running loop in Open Event Frontend

Using ember semantic UI radio buttons to render form elements selectively on Open Event Front End

This blog article will illustrate how ember semantic ui radio buttons have been used to render form elements selectively on Open Event Front End and in the process will learn, how to make use of the powerful binding features offered by ember semantic ui for radio buttons via the mut action.

So what do we have to begin with ?

The sample form which we want to create

A form which allows us to chose one of the modes of Paypal payments and displays corresponding fields for it.

What we want is that the radio button should allow us to make a choice and then display the corresponding  fields. Now that seems a trivial process, but there is some thought process which goes into this, to end up with the most efficient choice. So first just make the basic form where in all the fields are visible.

Now let’s learn how to make use of the mut action on the radio buttons. What it allows us to do is pass a parameter while calling it, and that parameter name is shared by all the radio buttons belonging to a particular group of radio buttons. And what that action does is, store the name of the currently selected radio button in it. So we can easily keep track of which button has been selected and use that variable in selective rendering of templates. The action is triggered whenever the radio button’s property changes and the trigger is aptly called onChange. So essentially the syntax boils down to this :

<!-- The first radio button -->
{{ui-radio label=(t 'Sandbox mode - Used during development and testing')
           name='paypal_integration_mode'
           value='sandbox'
           onChange=(action (mut selectedMode))}}
<!-- The second radio button -->
{{ui-radio label=(t 'Live mode - Used during production') 
           name='paypal_integration_mode'
           value='live'
           onChange=(action (mut selectedMode))}}

Now whichever button is selected it’s name will be stored in selectedMode in this case. And hence we can use the conditional helpers of handle bars to render elements based on the selected radio button.

The final code looks something like this:

  <.h3 class="ui header">{{t 'PayPal Credentials'}}<./h3>
    <.div class="sub header">
       {{t 'See here on how to obtain these keys.'}}
    <./div>
   <.h5 class="ui header">{{t 'PayPal Integration Mode'}}<./h5>
   <.div class="field">
     {{ui-radio label=(t 'Sandbox mode')
                name='paypal_integration_mode' 
                value='sandbox' 
                current='sandbox'
                onChange=(action (mut selectedMode))}}
   <./div>
   {{#unless (eq selectedMode 'live')}}
     <.div class="field">
       <.label>{{t 'Sandbox username'}}<./label>
       {{input type='text' name='sandbox_username'}}
     <./div>
     <.div class="field">
       <.label>{{t 'Sandbox password'}}<./label>
       {{input type='password' name='sandbox_password'}}
     <./div>
     <.div class="field">
       <.label>{t 'Sandbox signature'}}<./label>
       {{input type='text' name='sandbox_signature'}}
     <./div>
   {{/unless}}
   <.div class="field">
     {{ui-radio label=(t 'Live mode')
                name='paypal_integration_mode'
                value='live' 
                onChange=(action (mut selectedMode))}}
   <./div>
   {{#if (eq selectedMode 'live')}}
     <.div class="field">
      <.label>{{t 'Live username'}}<./label>
       {{input type='text' name='live_username'}}
     <./div>
     <.div class="field">
       <.label>{{t 'Live password'}}<./label>
       {{input type='password' name='live_password'}}
     <./div>
     <.div class="field">
       <.label>{{t 'Live signature'}}<./label>
       {{input type='text' name='live_signature'}}
     <./div>
   {{/if}}
   <.button class="ui teal button" type="submit">
   {{t 'Save'}}
   <./button>

 

Important tip

The action is triggered by the onChange action, hence the variable doesn’t have the value when the template is rendered for the very first time and hence at that instant, none of the fields will be rendered, to avoid that we have used both if and unless condition helpers instead of identical conditional helpers to cleverly avoid this situation. You can read about the ember radio buttons further more through the official documentation

Resources

Continue ReadingUsing ember semantic UI radio buttons to render form elements selectively on Open Event Front End

Making currency name and currency symbol helpers for Open Event Frontend

This blog article will illustrate how to make two helpers which will help us in getting the currency name and symbol from a dictionary, conveniently.The helpers will be used as  a part of currency form on Open Event Front End It also exemplifies the power of ember JS and why is it being used in this project via a counter example in which we try to do things the non ember way and get the required data without using those helpers.

So what do we have to begin with ?

The sample data which will be fetched from the API:

[
     {
       currency   : 'PLN',
       serviceFee : 10.5,
       maximumFee : 100.0
     },
     {
       currency   : 'NZD',
       serviceFee : 20.0,
       maximumFee : 500.0
     }
     //The list continues
]

The dictionary data format:

[
  {
    paypal : true,
    code   : 'PLN',
    symbol : 'zł',
    name   : 'Polish zloty',
    stripe : true
  },
  {
    paypal : true,
    code   : 'NZD',
    symbol : 'NZ$',
    name   : 'New Zealand dollar',
    stripe : true
  },
  {
    paypal : false,
    code   : 'INR',
    symbol : '₹',
    name   : 'Indian rupee',
    stripe : true
  }
]
// The list continues

And our primary goal is to fetch the corresponding name and symbol from the dictionary for a given currency code, easily and efficiently.

One might be tempted to get things done the easy way : via

{{get (find-by 'code' modal.name currencies) 'name'}}

and perhaps,

{{get(find-by 'code' modal.name currencies) 'symbol'}}

where currencies is the name of the imported array from the dictionary. But this might be hard to follow for a first time reader, and also in case we ever need this functionality to work in a different context, this is clearly not the most feasible choice. Hence helpers come into picture, they can be called anywhere and will have a much simpler syntax

Our goal is to make helpers such that the required functionality is achieved with a simpler syntax than the one shown previously.So we will simply generate the helpers’ boiler-plate code via ember CLI

$ ember generate helper currency-name
$ ember generate helper currency-symbol

Next we will import the currency format from the payment dictionary to match it against the name or symbol provided by the user. Now all that remains is finding the correct matching from the dictionary. We import the find function from lodash for that.

So, this is how they would look

import Ember from 'ember';
import { find } from 'lodash';
import { paymentCurrencies } from 'open-event-frontend/utils/dictionary/payment';

const { Helper } = Ember;

export function currencyName(params) {
  return find(paymentCurrencies, ['code', params[0]]).name;
}

export default Helper.helper(currencyName);

 

And for the currency symbol helper

import Ember from 'ember';
import { find } from 'lodash';
import { paymentCurrencies } from 'open-event-frontend/utils/dictionary/payment';

const { Helper } = Ember;

export function currencySymbol(params) {
  return find(paymentCurrencies, ['code', params[0]]).symbol;
}

export default Helper.helper(currencySymbol);

 

Now all we need to do use them is {{currency-name ‘USD’}} and {{currency-symbol ‘USD’}} to get the corresponding currency name and symbol. We use find from lodash here instead of the default even though it is similar in performance because it provides much better readability.

Resources

*Featured Image licensed under Creative Commons CC0 in public domain

Continue ReadingMaking currency name and currency symbol helpers for Open Event Frontend

Creating a notification dropdown in semantic UI for Open Event Frontend

Semantic UI comes packaged with highly responsive components to cater to all front end needs. The area of front-end development is so large, it is never possible to cover all the possible requirements of a developer with pre built components. Currently there is no means to display notifications on the navbar in Open Event Front-end project. In this article we are going to build a notification dropdown from scratch which will be used there to display notifications. So we begin by generating a new component via ember CLI

$ ember generate component notification-dropdown

This should generate the boiler-plate code for our component, with the template file located at: templates/components/notification-dropdown.hbs and the JS file located at components/notification-dropdown.js  It is assumed that you already have a basic ember app with at least a navbar set up. The notification drop down will be integrated with the navbar as a separate component. This allows us great flexibility in terms of location of the navbar, and also helps us  in not cluttering the code in one file.

We will use the popup component of semantic ui as the underlying structure of our dropdown. I have used some dummy data stored in a separate file, you can use any dummy data you wish, either  by directly hardcoding it or importing it from a js file stored somewhere else. It’s preferred if the mock data is called from a js file, because it helps in simulating the server response in a much more genuine way.

We will make use of the floating label of semantic UI to display the number of unread notifications. A mail outline icon should make for a good choice to use the primary icon to denote the notifications. Also, the floating label will require additional styling to make it overlap with the icon perfectly.

For the header in the dropdown we can give a ‘mark all as read’ button aligned to the right and the ‘notification’ header to the left. Also for best user experience even on small devices, we will make each notification item clickable as a whole instead of individual clickable elements in it. A selection link list of semantic UI should be perfect to display individual notifications as it gives a hovering effect and also, allows us to display a header. Moving onto individual notification items, it will have 3 sub parts

  • A header
  • Description
  • Human friendly notification time

For the header we will use the ‘header’ class predefined in semantic UI for list items.We will use ‘content’ class for description which is again a predefined semantic UI class, And finally the time can be displayed via moment-from-now helper of ember to display the time in a human friendly format.

<.i class="mail outline icon">
<./i>
<.div class="floating ui teal circular mini label">{{notifications.length}}<./div>
<.div class="ui wide notification popup bottom left transition ">
 <.div class="ui basic inverted horizontal segments">
   <.div class="ui basic left aligned segment weight-800">
     <.p>{{t 'Notifications'}}<./p>
   <./div>
   <.div class="ui basic right aligned segment weight-400">
     <.a href="#">{{t 'Mark all as Read'}}<./a>
   <./div>
 <./div>
 <.div class="ui fluid link celled selection list">
   {{#each notifications as |notification|}}
     <.div class="item">
       <.div class="header">
         {{notification.title}}
       <./div>
       <.div class="content weight-600">
         {{notification.description}}
       <./div>
       <.div class="left floated content">
         {{moment-from-now notification.createdAt}}
       <./div>
     <./div>
   {{/each}}
 <./div>
<./div>

 

Now the next challenge is to make the popup scrollable, they are not scrollable by default and may result in an error if their height exceeds that of the view port. So we apply some styling now. While applying such custom styles we have to be really careful so as to not to apply the styling in general to all of semantic UI’s components. It is very easy to overlook,  and may cause some unwanted changes. It is best to wrap it in a container class, in this case we have chosen to go ahead with notification as the class name. Also, since the notification dropdown should work consistently across all mobile devices, we need to set its maximum height not in terms of pixels but in terms of viewport height. The following styling code takes care of that as well as the icon which we are using to display the notification count.

.notification.item {
 margin: 0 !important;
 .label {
   top: 1em;
   padding: 0.2em;
   margin: 0 0 0 -3.2em !important;

 }
}

.ui.notification.popup {
 padding: 2px;
 .list {
   width: auto;
   max-height: 50vh;
   overflow: hidden;
   overflow-y: auto;
   padding: 0;
   margin: 0;
   .header {
     margin-bottom:5px;
   }
   .content {
     margin-bottom:2px;
   }
   }
 }

 

All of this takes care of the styling. Next, we need to take care of initialising the notification popup. For this we need to go to the navbar component as it is the one who calls the notification dropdown component. And add this to it:

didInsertElement() {
   this._super.call(this);
   this.$('.notification.item').popup({
     popup : '.popup',
     on    : 'click'
   });
 },

 willDestroyElement() {
   this._super.call(this);
   this.$('.notification.item').popup('destroy');
 }

 

The didInsertElement() makes sure that notification pop up is not rendered or initialised before the navbar is. On the other hand, willDestoroyElement() makes sure to clean up and destroy the pop up initialisation. Attached below are some screenshots of what the notification dropdown should look like.

On a wide screen
On mobile screens

Resources

Continue ReadingCreating a notification dropdown in semantic UI for Open Event Frontend

How to add Markers in Map Fragment of Open Event Android App

The Open Event Android project helps event organizers to generate Apps (apk format) for their events/conferences by providing API endpoint or zip generated using Open Event server. In the  Open Event Android App, we have a map for showing all locations of sessions. In this map users should be able to see different locations with multiple markers. In this post I explain how to add multiple markers in the Google map fragment and set the bound in the map so that all markers are visible on one screen with specified padding.

Create Map Fragment

The first step to do is to create a simple xml file and add a fragment element in it. Then create MapsFragment.java file and find fragment element added in the xml file using findFragmentById() method.

SupportMapFragment supportMapFragment = ((SupportMapFragment)
           getChildFragmentManager().findFragmentById(R.id.map));

1. Create fragment_map.xml file

In this file add FrameLayout as a top level element and add fragment element inside FrameLayout with the name “com.google.android.gms.maps.SupportMapFragment”.

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

       <fragment
           android:id="@+id/map"
           android:name="com.google.android.gms.maps.SupportMapFragment"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

</FrameLayout>

2. Create MapsFragment.java

MapsFragment.java extends SupportMapFragement and implements LocationListener, OnMapReadyCallback. Make instance of GoogleMap object. In onViewCreated method inflate fragment_map.xml file using inflater. Now find the fragment element added in the xml file using findFragmentById() method and assign it to SupportMapFragement instance.

public class MapsFragment extends SupportMapFragment implements LocationListener, OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    public void onViewCreated(View view, @Nullable Bundle     savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_map, container, false);
        SupportMapFragment supportMapFragment = ((SupportMapFragment)
        getChildFragmentManager().findFragmentById(R.id.map));
supportMapFragment.getMapAsync(this);

        return view;
    }

    @Override
    public void onMapReady(GoogleMap map) {  }
    ...
}

Create list of locations

Location object has three variables name, latitude & longitude. Create a list of locations for which we want to add markers to the map.

public class Location {
    private String name;
    private float latitude;
    private float longitude;
}

List<Microlocation> mLocations= new ArrayList<>();

 

Add location objects in mLocations in onViewCreated() method

mLocations.add(location);

 

You can add multiple locations using for loop or fetching from the database.

Add markers

Add following code in onMapReady(GoogleMap map) method. onMapReady(GoogleMap map) is called when map is ready to be used. setMapToolbarEnabled(true) used to show toolbar for marker. If the toolbar is enabled users will see a bar with various context-dependent actions, including ‘open this map in the Google Maps app’ and ‘find directions to the highlighted marker in the Google Maps app’.

if(map != null){
    mMap = map;
    mMap.getUiSettings().setMapToolbarEnabled(true);
}
showEventLocationOnMap();

 

Create showEventLocationOnMap() method and add following code

private void showLocationsOnMap(){

   float latitude;
   float longitude;
   Marker marker;

   //Add markers for all locations
   for (Location location : mLocations) {

       latitude = location.getLatitude();
       longitude = location.getLongitude();
       latlang = new LatLng(latitude, longitude);

       marker = mMap.addMarker(new MarkerOptions()
                .position(latlang)
                .title(location.getName()));
   }
}

 

So what we are doing here?

For each location object in mLocations list, we are creating LatLng(latitude, longitude)  object and adding it to the map using

marker = mMap.addMarker(new MarkerOptions().position(latlang).title(location.getName()));

Setting bound

We want to set the zoom level so that all markers are visible. To do this, we can use LatLngBounds.Builder. Add the position of the marker in the builder object using include method while adding the marker in the map.

LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(marker.getPosition());

 

Then make the LatLngBounds object from builder.

LatLngBounds bounds = builder.build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, dpToPx(40));
mMap.moveCamera(cameraUpdate);

private int dpToPx(int dp) {
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

 

dpTopx converts dp to px so that it set the same padding in all devices.

Conclusion

Markers in map give very nice user experience for an event or a conference because they show the relative position of places and we now offer this feature in Open Event Android.

Additional resources:

Continue ReadingHow to add Markers in Map Fragment of Open Event Android App

GlobalSearchAdapter Setup in Open Event Android App

In this blog post I describe how the GlobalSearchAdapter in Open Event Android was made which enabled users to search quickly within the app. This post also outlines how to create Recycler Views with heterogenous layouts and explains how to write ViewHolders.

Adapter Logic

A custom adapter was built for the population of views in the Recycler View in the SearchActivity.

private List<Object> filteredResultList = new ArrayList<>();
//ViewType Constants
private final int TRACK = 0;
private final int SPEAKER = 2;
private final int LOCATION = 3;
private final int DIVIDER = 4;

The DIVIDER constant was assigned to the Result Type Header View.

In a gist all the item types such as Speaker, Track, Location, Divider etc have been designated some constants.

Getting the ItemViewType

@Override
public int getItemViewType(int position) {

   if(filteredResultList.get(position) instanceof Track){
       return TRACK;
   }
   else if(filteredResultList.get(position) instanceof String){
       return DIVIDER;
   }
   ...Similarly for other ItemTypes such as Session or Location
   else{
       return 1;
   }
}

As the filteredResultList is of type Object we can insert objects of any type into the list as Object is a superclass of all classes. We would want a view which represents a TRACK if we have an object of type Track in the filteredResultList. And similarly for the other result types we could insert objects of type LOCATION, SPEAKER types in this list. getItemViewType() basically determines the type of the item that is visible to us. If the list consists of an item of type SPEAKER, in the RecyclerView.

Speaker Item Type
Track Item Type
Divider Item Type
Location Item Type

Code for onCreateViewHolder in GlobalSearchAdapter for the Recycler View

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

   RecyclerView.ViewHolder resultHolder = null;
   LayoutInflater inflater = LayoutInflater.from(parent.getContext());

   switch(viewType) {
       case TRACK:
           View track = inflater.inflate(R.layout.item_track, parent,   false);
           resultHolder = new TrackViewHolder(track,context);
           break;
       case SPEAKER:
           View speaker = inflater.inflate(R.layout.search_item_speaker, parent, false);
           resultHolder = new SpeakerViewHolder(speaker,context);
           break;
       //Similarly for other types
       default:
           break;
   }
   return resultHolder;
}

Depending upon the the viewType returned the desired layout is inflated and the desired ViewHolder is returned.

Code for onBindViewHolder in GlobalSearchAdapter for the Recycler View

@Override
 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
 
    switch (holder.getItemViewType()){
        case TRACK:
            TrackViewHolder trackSearchHolder = (TrackViewHolder)holder;
            final Track currentTrack = (Track)getItem(position);
            trackSearchHolder.setTrack(currentTrack);
            trackSearchHolder.bindHolder();
            break;
         //Similarly for all the other View Types
        default:
            break;
    }
 }

These functions are being used to bind the data to the layouts that have been inflated already in the earlier snippet of code of onCreateViewHolder.

The bindHolder functions of each ViewHolder type are being used to do the view binding i.e converting the information in the Object Track into what we see in the TrackViewHolder as seen in TrackViewFormat.

All ViewHolders have been defined as separate classes in order to enable re usability of these classes.

ViewHolder Implementation

There are 4 main ViewHolders that were made to enable such a search. I’ll be talking about the TrackViewHolder in detail.

public class TrackViewHolder extends RecyclerView.ViewHolder {
    
    @BindView(R.id.imageView)
    ImageView trackImageIcon;
    @BindView(R.id.track_title)
    TextView trackTitle;
    @BindView(R.id.track_description)
    TextView trackDescription;
 
    private Track currentTrack;
    private Context context;
    private TextDrawable.IBuilder drawableBuilder = TextDrawable.builder().round();
 
    public void setTrack(Track track) {
        this.currentTrack = track;
    }
 
    public TrackViewHolder(View itemView,Context context) {
        super(itemView);
        ButterKnife.bind(this, itemView);
        this.context = context;
    }
    public void bindHolder(){
 
        //Set all Views to their correct configurations
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context,   TrackSessionsActivity.class);
                intent.putExtra(ConstantStrings.TRACK,   currentTrack.getName());
 
                // Send Track ID to Activity to leverage color cache
                intent.putExtra(ConstantStrings.TRACK_ID,   currentTrack.getId());
                context.startActivity(intent);
            }
        });
} }

Those @BindView annotations that we can see are the result of a library called as Butterknife which is used to reduce standard boilerplate findViewById lines.

@BindView(R.id.imageView) ImageView trackImageIcon;
IS THE SAME AS THIS  
ImageView trackImageIcon = (ImageView)findViewById(R.id.imageView);

The advantage of such a ViewHolder is that it knows what kind of data it stores as compared to traditional ViewHolders which do not know the kind of data it stores.

By making ViewHolders separate from the RecyclerViewAdapter we are essentially decoupling classes and are enabling reusability of code. Also we make the ViewHolder a bit more intelligent by storing the object it binds in the ViewHolder itself. In the above example we are storing an object of Track which is bind to the ViewHolder. We also see that we do the view binding inside the viewholder itself. All this helps us to reduce code inside the adapter class.

A recent addition to the app was custom colors for all TRACKS in the app that improved the visual feel of the app. So basically, for example if a SESSION has been associated with the track of Blockchain it would be given a color such as purple. onClickListeners are also being set with some extras which are self-descriptive in nature. Similarly the other ViewHolders have been implemented.

Resources

Continue ReadingGlobalSearchAdapter Setup in Open Event Android App

Implementing Barcode Scanning in Open Event Android Orga App using RxJava

One of the principal goals of Open Event Orga App (Github Repo) is to let the event organizer to scan the barcode identifier of an attendee at the time of entry in the event, in order to quickly check in that attendee. Although there are several scanning APIs available throughout the web for Android Projects, we chose Google Vision API as it handles multitude of formats and orientations automatically with little to no configuration, integrates seamlessly with Android, and is provided by Google itself, ensuring great support in future as well. Currently, the use case of our application is:

  • Scan barcode from the camera feed
  • Detect and show barcode information on screen
  • Iterate through the attendee list to match the detected code with unique attendee identifier
  • Successfully return to the caller with scanned attendee’s information so that a particular action can be taken

There are several layers of optimisations done in the Orga Application to make the user interaction fluid and concise. I’ll be sharing a few in this blog post regarding the configuration and handling of bursty data from camera source efficiently. To see the full implementation, you can visit the Github Repository of the project using the link provided above.

Configuration

The configuration of our project was done through Dagger 2 following the dependency injection pattern, which is not the focus of this post, but it is always recommended that you follow separation of concerns in your project and create a separate module for handling independent works like barcode scanner configuration. Normally, people would create factories with scanner, camera source and processors encapsulated. This enables the caller to have control over when things are initialized.

Our configuration provides us two initialised objects, namely, CameraSource and BarcodeDetector

@Provides
BarcodeDetector providesBarCodeDetector(Context context, Detector.Processor<Barcode> processor) {
    BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context)
        .setBarcodeFormats(Barcode.QR_CODE)
        .build();

    barcodeDetector.setProcessor(processor);

    return barcodeDetector;
}

@Provides
CameraSource providesCameraSource(Context context, BarcodeDetector barcodeDetector) {
    return new CameraSource
        .Builder(context, barcodeDetector)
        .setRequestedPreviewSize(640, 480)
        .setRequestedFps(15.0f)
        .setAutoFocusEnabled(true)
        .build();
}

The fields needed to create the objects are provided as arguments to the provider functions as all the dependencies required to construct these objects. Now focusing on the Detector.Processor requirement of the BarcodeDetector is the classic example on non injectable code. This is because the processor is to be supplied by the activity or any other object which wants to receive the callback with the detected barcode data. This means we could inject it at the time of creation of the Activity or Presenter itself. We could easily overcome by adding a constructor to this dagger module containing the Barcode.Processor at the time of injection, but that would violate our existing 0 configuration based model where we just get the required component from the Application class and inject it. So, we wrapped the the processor into a PublishSubject

@Provides
@Singleton
@Named("barcodeEmitter")
PublishSubject<Notification<Barcode>> providesBarcodeEmitter() {
    return PublishSubject.create();
}

@Provides
@Singleton
Detector.Processor<Barcode> providesProcessor(@Named("barcodeEmitter") PublishSubject<Notification<Barcode>> emitter) {
    return new Detector.Processor<Barcode>() {
        @Override
        public void release() {
            // No action to be taken
        }

        @Override
        public void receiveDetections(Detector.Detections<Barcode> detections) {
            SparseArray<Barcode> barcodeSparseArray = detections.getDetectedItems();
            if (barcodeSparseArray.size() == 0)
                emitter.onNext(Notification.createOnError(new Throwable()));
            else
                emitter.onNext(Notification.createOnNext(barcodeSparseArray.valueAt(0)));
        }
    };
}

This solves 2 of our problems, not only now all these dependencies are injectable with 0 configurations, but also our stream of barcodes is now reactive.

Note that not everyone is in favour of using Singleton, but you can decrease the scope using your own annotation. We prefer not creating life cycle bound objects, those are hard to manage and can cause potential memory leaks, and the creation of an anonymous inner class object every time listener activates is not good for memory too.

Also, note that Singleton classes will cause memory leaks too if you don’t release their reference at the time of destruction of life cycle bound object

Notice how the type of PublishSubject is not just a barcode, but Notification which wraps the bar code. That’s because we want to send both the value and error streams down uninterrupted to the caller. Otherwise, the data stream would have stopped on the emission of first onError call. Here, we detect the barcodeSparseArray size and accordingly send error or first value to the PublishSubject which will be accordingly subscribed by the activity or presenter

Handling Bursty Data

barcodeEmitter.subscribe(barcodeNotification -> {
    if (barcodeNotification.isOnError()) {
        presenter.onBarcodeDetected(null);
    } else {
        presenter.onBarcodeDetected(barcodeNotification.getValue());
    }
});

Here is how we are subscribing the notification emitter and passing the appropriate value to the presenter to handle, null if it is an error and the value if it is the next emission.

Note that you must dispose the disposable returned by the subscribe method on the subject when the Activity is to be destroyed or else it will keep the reference to the anonymous inner class created with the lambda for barcodeNotification and cause a memory leak

Now, let’s see how the presenter handles this data for:

  • Hiding and showing the barcode panel when barcode is on the screen accordingly
  • Showing the data extracted from the barcode scanner

These things can be implemented in a very standard way with a few conditionals, but most developers forget the fact that the data emission rate is enormous when concerning with live feed of data. In the Open Event Orga app, we have reduced it to 15 FPS as it is more than enough to scan barcodes for our use case, but it is still huge. The continuous stream of nulls and barcode data is useless to us unless it changes.

A little explanation about nulls and values here: You must have noticed above the conditions when we pass null and value, but I’ll explain again. A value will be passed if there is a detected barcode on screen, and null will be passed if there is no barcode detected. The Google Vision API will keep sending the same value for barcode at 15 FPS and so we’ll get this redundant stream of nulls and values which we should not concern with processing as this will load the CPU unnecessarily.

There are only 2 cases where we need to process it:

  • Null changes to Value -> Show barcode panel
    Value changes to null -> Hide barcode panel
  • Value changes irrespective of nulls -> Show barcode data on UI and search through the attendee identifiers

So, here too we’ll create 2 PublishSubject objects

private PublishSubject<Boolean> detect = PublishSubject.create();
private PublishSubject<String> data = PublishSubject.create();

And we’ll configure them both in this way to receive data on each barcode emission in the presenter:

public void onBarcodeDetected(Barcode barcode) {
   detect.onNext(barcode == null);
   if (barcode != null)
       data.onNext(barcode.displayValue);
}

This will make data only receive non-null changes and detect receive a boolean notifying if the current detected barcode was null or not.

Now, we see how each of these subjects is configured to pass the emissions downstream:

data.distinctUntilChanged()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(this::processBarcode);

This one is pretty straightforward. It’ll only send data downstream if its value has changed from the previous emission, disregarding nulls. So, for a stream like this

A A A A A null null null null A A A A null B B B B B B null null null B B A A null A

It will only emit:

A                                                                         B                                                  A

Which is actually what we want, as we only need to process and show the distinct barcode data and match it with our attendee list. Now, with thousands of attendees, the first method would have triggered unnecessary and time-consuming computations over and over again on same identifiers with little gaps of time, which would have created mediocre results even in a multi-threaded environment. The second one saves us from repetitive calculations and also gives us enormous gaps between emissions, which is optimal for our use case.

The second case is not so obvious because we can’t ignore nulls here as we have to show and hide UI based on them. This means that unlike our previous stream if we just use distinctUntilChanged, it will look like this:

A A A A A null null null null A A A A null B B B B B B null null null B B A A null A

f                 t                               f               t      f                    t                        f             t       f

This is because, if you remember, we were sending down emissions of barcode == null on each emission for this Subject. So, in this case, as you may see, some of the values are so close enough that it will not be discernable in UI and also annoy users who’ll see the panel pop up for milliseconds before vanishing or vice-versa. The perfect operation for this case will be debounce

detect.distinctUntilChanged()
    .debounce(150, TimeUnit.MILLISECONDS)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(receiving -> scanQRView.showBarcodePanel(!receiving));

This operator will drop any emission in the window of 150ms succession and only pick up those emissions which are 150ms apart from each other. Now, 150 ms is not a magic number, it is picked through hit and trial and what works best for your case. Lower the value and you will pick up more changes downstream, increase the value and you might miss the required events.

This makes our stream somewhat like this, cleaning out the cluttered events

f                 t                               f                                           t                        f

This is the screenshot of the implementation:  

 

And an animated gif of the scanning process:

This is all for this blog, you may use many other operators from the arsenal of RxJava, whatever fits your use case. As I have presumed the knowledge about Subjects, MVP and a little bit of Dagger in this post, I’ll link some of the resources where you can find more information about these:

http://reactivex.io/documentation/subject.html

https://github.com/ReactiveX/RxJava/wiki/Subject

http://reactivex.io/documentation/operators/distinct.html

http://www.vogella.com/tutorials/Dagger/article.html

https://antonioleiva.com/mvp-android/

Continue ReadingImplementing Barcode Scanning in Open Event Android Orga App using RxJava