Making Open Event Organizer Android App Reactive

FOSSASIA’s Open Event Organizer is an Android Application for Event Management. The core feature it provides is two way attendee check in, directly by searching name in the list of attendees or just by scanning a QR code from ticket. So as attendees of an event can be really large in number like 1000+ or more than that, it should not alter the performance of the App. Just imagine a big event with lot of attendees (lets say 1000+) and if check in feature of the app is slow what will be the mess at entrance where each attendee is waiting for his ticket to be scanned and verified. For example, a check in via QR code scan. A complete process is somewhat like this:

  1. QR scanner scans the ticket code and parse it into the ticket identifier string.
  2. Identifier string is parsed to get an attendee id from it.
  3. Using the attendee id, attendee is searched in the complete attendees list of the event.
  4. On match, attendee’s check in status is toggled by making required call to the server.
  5. On successful toggling the attendee is updated in database accordingly.
  6. And check in success message is shown on the screen.

From the above tasks 1st and 6th steps only are UI related. Remaining all are just background tasks which can be run on non-UI thread instead of carrying them on the same UI thread which is mainly responsible for all the user interaction with the app. ReactiveX, an API for asynchronous programming enables us to do this. Just for clarification asynchronous programming is not multithreading. It just means the tasks are independent and hence can be executed at same time. This will be another big topic to talk about. Here we have used ReactiveX just for running these tasks in background at the same time UI thread is running. Here is our code of barcode processing:

private void processBarcode(String barcode) {
  Observable.fromIterable(attendees)
      .filter(attendee -> attendee.getOrder() != null)
      .filter(attendee -> (attendee.getOrder().getIdentifier() + "-" + attendee.getId()).equals(barcode))
      .subscribeOn(Schedulers.computation())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(attendee -> {
          // here we get the attendee and
          // further processing can be called here
          scanQRView.onScannedAttendee(attendee);
      });
  }

In the above code you will see the actual creation of an Observable. Observable class has a method fromIterable which takes list of items and create an Observable which emits these items. So hence we need to search the attendee in the attendees list we have already stored in database. Filter operator filters the items emitted using the function provided. Last two lines are important here which actually sets how our subscriber is going to work. You will need to apply this thread management setting to your observable while working on android. You don’t actually have to worry about it. Just remember subscribeOn sets the thread on which actually the background tasks will run and on item emission subscriber handle it on main thread which is set by observeOn method. Subscribe operator provides the function what actually we need to run on main thread after emitted item is caught. Once we find the attendee from barcode, network call is made to toggle check in status of the attendee. Here is the code of check in method:

public void toggleCheckIn() { eventRepository.toggleAttendeeCheckStatus(attendee.getEventId(), attendeeId)
      .subscribe(completed -> {
          ...
          String status = attendee.isCheckedIn() ? "Checked In" : "Checked Out";
          attendeeCheckInView.onSuccess(status);
      }, throwable -> {
          throwable.printStackTrace();
          ...
      });
}

In the above code toggleAttendeeCheckStatus method returns an Observable. As name suggests the Observable is to be observed and it emits signals (objects) which are caught by a Subscriber. So here observer is Subscriber. So in the above code toggleAttendeeCheckStatus is creating an Obseravable. Lets look into toggleAttendeeCheckStatus code:

public Observable<Attendee> toggleAttendeeCheckStatus(long eventId, long attendeeId) {
  return eventService.toggleAttendeeCheckStatus(eventId, attendeeId, getAuthorization())
      .map(attendee -> {
          ...
          // updating database logic
          ...
          return attendee;
      })
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread());
}

We have used Retrofit+Okhttp for network calls. In the above code eventService.toggleAttendeeCheckStatus returns an Observable which emits updated Attendee object on server response. Here we have used Map operator provided by ReactiveX which applies function defined inside it on each item emitted by the observable and returns a new observable with these items. So here we have use it to make the related updates in the database. Now with ReactiveX support the complete check in process is:

(Tasks running in background in bold style)

  1. QR scanner scans the ticket code and parse it into the ticket identifier string.
  2. Using the identifier, attendee is searched in the complete attendees list of the event.
  3. On match, attendee’s check in status is toggled by making required call to the server.
  4. On successful toggling the attendee is updated in database accordingly.
  5. And check in success message is shown on the screen.

So now main thread runs only tasks related to the UI. And time consuming tasks are run in background and UI is updated on their completion accordingly. Hence check in process becomes smooth irrespective of size of the attendees. And app never crashes.

Continue ReadingMaking Open Event Organizer Android App Reactive

Displaying Web Search and Map Preview using Glide in SUSI Android App

SUSI.AI has many skills. Two of which are displaying web search of a certain query and displaying a map of certain position. This post will cover how these things are implemented in Susi Android App and how a preview is displayed using Bumptech’s free and open source Glide library. So, what is glide? According to Glide docs, Glide is a fast and efficient open source media management and image loading framework for Android that wraps media       decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.

Now, lets describe how this framework is used in Susi App. So, let’s cover it’s two uses one by one :

Map Preview :

    

Whenever a user queries something like “Where is Singapore” or “Where am I”, the response from server is a map with latitude, longitude and zoom level. See the “type” which is map. It indicates android app that it needs to display a map with provided values.

“actions”: [
     {
       “type”: “answer”,
       “expression”: “Singapore is a place with a population of 3547809. Here is a map: https://www.openstreetmap.org/#map=13/1.2896698812440377/103.85006683126556”
     },
     {
       “type”: “anchor”,
       “link”: “https://www.openstreetmap.org/#map=13/1.2896698812440377/103.85006683126556”,
       “text”: “Link to Openstreetmap: Singapore”
     },
     {
       “type”: “map”,
       “latitude”: “1.2896698812440377”,
       “longitude”: “103.85006683126556”,
       “zoom”: “13”
     }
   ]
 }]

So, these values are then plugged into the below mentioned url where length x width is size of image to be retrieved. This url links to an image of the map with center and size as defined.

http://staticmap.openstreetmap.de/staticmap.php?center=latitude,longitude&zoom=zoom&size=lengthxwidth

So, now as we have a link to the image to be displayed. We just need something to get the image from that link and display it in the app. That’s where Glide comes to rescue. It loads the image from the link to an ImageView.

Glide.with(currContext).load(mapHelper.getMapURL()).listener(new RequestListener<String, GlideDrawable>() {

@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}

@Override
public boolean onResourceReady(GlideDrawable resource, String model,
Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource){
mapViewHolder.pointer.setVisibility(View.VISIBLE);
return false;
}

}).into(mapViewHolder.mapImage);

So, what the above code does is that it displays image from url mapHelper.getMapURL() to mapViewHolder.mapImage.

So, by this way, we are able to display a preview of the map in the chat itself. The user can then click on the image to load the         complete map.

Web Search Preview :

When a user enters queries like “search for dog”, the response from server is a websearch with the query to be searched,         something like this.

“actions”: [
     {
       “type”: “answer”,
       “expression”: “Here is a web search result:”
     },
     {
       “type”: “websearch”,
       “query”: “dog”
     }
   ]

So, now we know the query to be searched, we can use any search api to get results about that query ad display it in the app. In Susi android app, we have used DuckDuckGo open source api to do that task. We call this url

https://api.duckduckgo.com/?format=json&pretty=1&q=query&ia=meanings

which gives a json response with results. We then use the json     results which contains the link to result, image to be displayed and a short abstract of the result.

{
“Result” : “<a href=\”https://duckduckgo.com/Dog\”>Dog</a>A member of genus Canis that forms part of the wolf-like canids, and is the most widely abundant…”,
“Icon” : {
“URL” : “https://duckduckgo.com/i/16101b42.jpg”,
“Height” : “”,
“Width” : “”
},
“FirstURL” : “https://duckduckgo.com/Dog”,
“Text” : “Dog A member of genus Canis that forms part of the wolf-like canids, and is the most widely abundant…”
}

There are three things we are displaying in the websearch preview, which are Title, text and an image. We display the query as the title, text in json response as text and use glide to get the image from the url provided in the the json response.

glide.load(url)
.crossFade()
.into(websearchholder.previewImageView);

Using the above code, we load the image from the image url to websearchholder.previewImageView().

So, by this way, we are able to display a preview of the search     result. The user can then touch on this preview which opens the     detailed page of the result.

Happy Coding!

Continue ReadingDisplaying Web Search and Map Preview using Glide in SUSI Android App

Using ButterKnife in PSLab Android App

ButterKnife is an Android Library which is used for View injection and binding. Unlike Dagger, ButterKnife is limited to views whereas Dagger has a much broader utility and can be used for injection of anything like views, fragments etc. Being limited to views makes it much more simpler to use compared to dagger.

The need for using ButterKnife in our project PSLab Android was felt due to the fact binding views would be much more simpler in case of layouts like that of Oscilloscope Menu which has multiple views in the form of Textboxes, Checkboxes, Seekbars, Popups etc. Also, ButterKnife makes it possible to access the views outside the file in which they were declared. In this blog, the use of ButterKnife is limited to activities and fragments.

ButterKnife can used anywhere we would have otherwise used findViewById(). It helps in preventing code repetition while instantiating views in the layout. The ButterKnife blog has neatly listed all the possible uses of the library.

The added advantage of using Butterknife are-

  • The hassle of using Boilerplate code is not needed and the code volume is reduced significantly in some cases.
  • Setting up ButterKnife is quite easy as all it takes is adding one dependency to your gradle file.
  • It also has other features like Resource Binding (i.e binding String, Color, Drawable etc.).
  • Other uses like simplification of code while using buttons. For example, there is no need of using findViewById and setOnClickListener, simple annotation of the button ID with @OnClick does the task.

Using butterknife was essential for PSLab Android since for the views shown below which has too many elements, writing boilerplate code can be a very tedious task.

Using ButterKnife in activities

The PSLab App defines several activities like MainActivity, SplashActivity, ControlActivity etc. each of which consists of views that can be injected with ButterKnife.

After setting up ButterKnife by adding dependencies in gradle files, import these modules in every activity.

import butterknife.BindView;
import butterknife.ButterKnife;

Traditionally, views in Android are defined as follows using the ID defined in a layout file. For this findViewById is used to retrieve the widgets.

private NavigationView navigationView;
private DrawerLayout drawer;
private Toolbar toolbar;

navigationView = (NavigationView) findViewById(org.fossasia.pslab.R.id.nav_view);
drawer = (DrawerLayout) findViewById(org.fossasia.pslab.R.id.drawer_layout);
toolbar = (Toolbar) findViewById(org.fossasia.pslab.R.id.toolbar);

However, with the use of Butterknife, fields are annotated with @BindView and a View ID for finding and casting the view automatically in the layout files.
After the annotation, finding and casting of views ButterKnife.bind(this) is called to bind the views in the corresponding activity.

@BindView(R.id.nav_view) NavigationView navigationView;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.toolbar) Toolbar toolbar;
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

Using ButterKnife in fragments

The PSLab Android App implements ApplicationFragment, DesignExperimentsFragment, HomeFragment etc., so ButterKnife for fragments is also used.

Using ButterKnife is fragments is slightly different from using it in activities as the binded views need to be destroyed on leaving the fragment as the fragments have different life cycle( Read about it here). For this an Unbinder needs to be defined to unbind the views before they are destroyed.

Quoting from the official documentation

When binding a fragment in OnCreateView, set the views to null in OnDestroyView. Butter Knife returns an Unbinder instance when you call bind to do this for you. Call its unbind method in the appropriate lifecycle callback.

So, an additional module of ButterKnife is imported

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;

Similar as that of activities, the code below can be replaced by the corresponding ButterKnife code.

TextView tvDeviceStatus = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_status);
TextView tvVersion = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_version);
ImageView imgViewDeviceStatus = (ImageView)view.findViewById(org.fossasia.pslab.R.id.img_device_status);
@BindView(R.id.tv_device_status) TextView tvDeviceStatus;
@BindView(R.id.tv_device_version) TextView tvVersion;
@BindView(R.id.img_device_status) ImageView imgViewDeviceStatus;
private Unbinder unbinder;

Additionally, the unbinder object is used to store the returned Unbinder instance when ButterKnife.bind is called for Fragments.

unbinder = ButterKnife.bind(this,view);

Finally, the unbind method of unbinder is called while view is destroyed.

@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
Continue ReadingUsing ButterKnife in PSLab Android App

Adding Susper as standard search engine in Firefox and Chrome

Have you ever seen on the sites like Google and DuckDuckGo, when a user visits their website they provide an option to add their search engine as default search provider. Similarly, we raised up an issue regarding the implementation of this feature.

Currently, we have implemented this feature but facing some issues. Here is a screenshot:

Code for implementing this feature:

  $(document).ready(function() {
    var isFirefox = typeof InstallTrigger!=='undefined';

     if (isFirefox === false) {
       $("#set-susper-default").remove();
       $(".input-group-btn").addClass("align-search-btn");
       $("#navbar-search").addClass("align-navsearch-btn");
     }

     if (window.external && window.external.IsSearchProviderInstalled) {
       var isInstalled = window.external.IsSearchProviderInstalled("http://susper.com");

     if (!isInstalled) {
       $("#set-susper-default").show();
     }
    }

    $("#install-susper").on("click", function() {
      window.external.AddSearchProvider("http://susper.com/susper.xml");
    });

    $("#cancel-installation").on("click", function() {
      $("#set-susper-default").remove();
    });
  });


You can see, right side a box to add search provider. But this feature was not working for Chrome since it has some different feature to add search providers because of AddSearchProvider() and window.external.IsSearchProviderInstalled() works only with some versions of Firefox and earlier they worked with Chrome as well but now they don’t. So to remove this feature from Chrome browser, we simply commented out the lines which are highlighted in the code as bold.

We are still having an issue like, if a user adds the search provider in Firefox by installing it and then he/she visits the site next time, that option still appears to be there. It should not appear again if Susper has been already added as default search provider. We’re working on it though.

  • What else we did for other browsers compatibility?
    We picked up the idea of adding an OpenSearch feature. It auto-discovers the search providers. What we did simply:
  1. Create an XML file as  yoursitename.xml and configure it like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
    <ShortName>[Name of your website]</ShortName>
    <Description>[Description of your website]</Description>
    <Url type="text/html" template="http://www.yourwebsite.com/search?q=site:[Site host] {searchTerms}"/>
    </OpenSearchDescription>
  2. Add an auto-discovery link in your main index.html file:
<link rel="search" href="//susper.com/susper.xml" type="application/opensearchdescription+xml" title="susper.com"/>

We’re done! To see it in action, refresh the browser. The browser will auto-detect and tell you that a custom search engine was discovered and you can customise it according to your choice.

Check our susper.xml here:
https://github.com/fossasia/susper.com/blob/master/susper.xml

Continue ReadingAdding Susper as standard search engine in Firefox and Chrome

Conversion of CSS styles into React styles in SUSI Web Chat App

Earlier this week we had an issue where the text in our search box of the SUSI web app was not white even after having all the required styles. After careful inspection it was found that there is a attribute named -webkit-text-fill-color which was set to black.

Now I faced this issue as adding such attribute to our reactJs code will cause lint errors. So after careful searching stackoverflow, i found a way to add css attribute to our react code by using different case. I decided to write a blog on this for future reference and it might come handy to other developers as well.

If you want to write css in javascript, you have to turn dashed-key-words into camelCaseKeys

For example:

background-color => backgroundColor
border-radius => borderRadius
but vendor prefix starts with capital letter (except ms)
-webkit-box-shadow => WebkitBoxShadow (capital W)
-ms-transition => msTransition ('ms' is the only lowercase vendor prefix)

const containerStyle = {
  WebkitBoxShadow: '0 0 0 1000px white inset'
};

So in our case:-

-webkit-text-fill-color became WebkitTextFillColor

The final code of styles looked like: –

const searchstyle = {
      WebkitTextFillColor: 'white',
      color: 'white'
    }

Now, because inline styles gets attached on tags directly instead of using selectors, we have to put this style on the <input> tag itself, not the container.

See the react doc #inline-styles section for more details.

Continue ReadingConversion of CSS styles into React styles in SUSI Web Chat App

Prototyping PSLab Android App using Invision

Often, while designing apps, we need planning and proper designing before actually building the apps. This is where mock-up tools and prototyping is useful. While designing user interfaces, the first step is usually creating mockups. Mockups give quite a good idea about the appearance of various layouts of the app. However, mockups are just still images and they don’t give a clue about the user experience of the app. This is where prototyping tools are useful. Prototyping helps to get an idea about the user experience without actually building the app.

Invision is an online prototyping service which was used for initial testing of the PSLab Android app. Some of pictures below are the screenshots of our prototype taken in Invision. Since, it supports collaboration among developers, it proves to be a very useful tool. 

  • Using Invision is quite simple. Visit the Invision website and sign up for an account.Before using invision for prototyping, the mockups of the UI layouts must be ready since Invision is simply meant for prototyping and not creating mockups. There are a lot of mock-up tools available online which are quite easy to use.
  • Create a new project on Invision. Select the project type – Prototype in this case followed by selecting the platform of the project i.e. Android, iOS etc.
  • Collaborators can be added to a project for working together.
    • After project creation and adding collaborators is done with, the mock-up screens can be uploaded to the project directory
    • Select any mock-up screen, the window below appears, there are a few modes available in the bottom navbar – Preview mode, Build mode, Comment mode, Inspect Mode and History Mode.
        • Preview Mode – View your screen and test the particular screen prototype.
        • Build Mode – Assign functionality to buttons, navbars, seek bars, check boxes etc. and other features like transitions.
        • Comment Mode – Leave comments/suggestions regarding performance/improvement for other collaborators to read.
        • Inspect mode – Check for any unforeseen errors while building.
        • History Mode – Check the history of changes on the screen.
  • Switch to the build mode, it would now prompt to click & drag to create boxes around buttons, check boxes, seek bars etc,(shown above). Once a box ( called as “hotspot” in Invision ), a dialog box pops up asking to assign functionalities.
  • The hotspot/box which was selected must link to another menu/layout or result in some action like app closing. This functionality is provided by the “Link To:” option.
  • Then the desired gesture for activating the hotspot is selected which can be tap for buttons & check boxes, slide for navbars & seek bars etc from the “Gesture:” option.
  • Lastly, the transition resulting due to moving from the hotspot to the assigned window in “Link To:” is selected from the “Transition:” menu.

This process can be repeated for all the screens in the project. Finally for testing and previewing the final build, the screen which appears when the app starts is selected and further navigation, gestures etc. are tested there. So, building prototypes is quite an interesting and easy task.

Additional Resources

Continue ReadingPrototyping PSLab Android App using Invision

Choosing Semantic UI over Bootstrap for the Open Event Front-end

There are many design frameworks. In this blogpost, I am going to list out some pros of Semantic UI and why it is better suited for the Open Event project than Bootstrap or other frameworks.

Components:

The very main reason why we chose Semantic UI in Open Event frontend is the number of components it offers, such as step, segment, rails, cards, lists, headers, dimmers, dropdown, and form validation. We can use many of these components right away, which helps us to speed up development.

Installation:

Semantic UI also provides very good integrations with the majority of technologies available out there. Bootstrap, however, can be integrated anywhere with CDN’s.

Following are the technologies that Semantic UI can be integrated with:

  • As a NPM package
  • React
  • Angular
  • Ember JS

Also Semantic UI has CDN’s. Thus Semantic UI has easeful installation. The documentation on the official site provides a good view of installation.

Documentation:  

In regards to the documentation too, Semantic UI is more suitable for us. The official site has a neat and clean documentation for each component. In the case of Bootstrap, the documentation is also good but personally, for me, the Semantic UI’s documentation is easier to read understand.

Designed with completely em:

em is a unit in CSS which ensures that components are scaled according to the different device sizes.Every component is made by using em and rem so that the webpage doesn’t get distorted whenever screen size changes.

Concise and Expressive:  

In English, it’s much easier to say “There are three tall men” than “There is a tall man, a tall man, and a tall man”. This means that in Bootstrap, to achieve a thing (say a navbar), we need to have 2-3 nested classes of “navbar”, ”navbar-default”, etc. whereas with Semantic UI, it can be done with single one.

Code appearance:

In the case of Semantic UI, the code looks much cleaner since the classes created are of a short length and cause less confusion. Following code indicates how clean things can be done with Semantic UI.

class="ui secondary menu"> class="active item"> Home class="item"> Messages class="item"> Friends
class="right menu">
class="item">
class="ui icon input"> type="text" placeholder="Search..."> class="search link icon">
</div> <a class="ui item"> Logout </a> </div> </div>

                    Fig. Semantic UI

<nav class="navbar navbar-default">
class="container-fluid">
class="navbar-header"> class="navbar-brand" href="#">Brand
<!-- Collect the nav links, forms, and other content for toggling -->
class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
class="navbar-form navbar-left">
class="form-group"> type="text" class="form-control" placeholder="Search">
<button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav>

                      Fig. Bootstrap

Looks:

In terms of looks, it is well said that “User makes user experience” i.e the user experience depends more on the user viewing the website rather than the developer. Still, according to me, every Bootstrap site looks similar whereas a site built with Semantic provides a different, crisp look. Unlike other sites, a site built with Semantic looks beautiful.

Redesigning infinitely:

Creating a site in Semantic means you never have to rewrite your codebase from scratch. Redesigning means retooling your UI toolkit, adjusting UI definitions, not creating entirely new HTML layouts.

Conclusion:

There are many reasons why Semantic UI is a bit more suitable, powerful than Bootstrap for the Open Event project. Still, the choice of the framework depends on the purpose of your website or what you want to build. The official documentation of Semantic-UI can be found out at https://semantic-ui.com.

Continue ReadingChoosing Semantic UI over Bootstrap for the Open Event Front-end

How to add a new attribute in an action type for SUSI.AI

In this blog, I’ll be telling on how to add more attributes to already implemented action types so that it becomes easy to filter out the JSON results that we are getting by calling various APIs.

What are actions?

Actions are a set of defined functionality that how Susi works for special JSON return type from some API endpoint. These include table, pie chart, rss, web search etc. These action types are implemented and defined at the backend(on server). The definition contains several attributes and their data types.

Current action types and their attributes:

  • table : {columns, length}
  • piechart : {total, key, value, unit}
  • rss : (title, description, link)
  • websearch : {query}
  • map : {latitude, longitude, zoom}

* Please see that these are as of the date of publish of this blog. These are subject to change. Also keep in mind that these are optional attributes.

Need for new attributes:

There are millions of open APIs out there and hence their JSON response structure also varies. Some return a JSON Array and some return a single JSON object. Some might return JSON Array inside a JSONObject. They all varies. So to give proper filters, we may need new action types.

In this blog, we’ll take up the example of how “length” attribute was added. Similarly you can add more attributes to any of the action types.

Some APIs return few as 5 elements in a JSON Array and some give out 100s of elements. So the power of limiting the rows in a particular skill is given to Susi-skill developers only with addition of length parameter.

First things first.. Identify the action type in which you want to add parameters. Browse to SusiMind.java and SusiAction.java files. These are the main files where Susi looks for attributes while evaluating  a dream or a skill.

In SusiAction.java file, find the corresponding public action method which is returning a JSONObject. Currently the method looks like this:

public static JSONObject tableAction(JSONObject cols) {
        JSONObject json = new JSONObject(true)
            .put("type", RenderType.table.name())
            .put("columns", cols);
        return json;
    }

Add a parameter of corresponding data type (attribute you are adding) in argument list. Following this, put that variable in in JSONObject variable (that is json) which also being returned at the end. After changes, the method will look like this:

public static JSONObject tableAction(JSONObject cols, int length) {
        JSONObject json = new JSONObject(true)
            .put("type", RenderType.table.name())
            .put("columns", cols)
            .put("length", length);
        return json;
    }

In SusiMind.java file, find the if condition where other attributes of the corresponding action type are being checked. (sticking to length attribute in table action type, following is what you have to do).

if (type.equals(SusiAction.RenderType.table.toString()) && boa.has("columns")) {                                                                     actions.put(SusiAction.tableAction(boa.getJSONObject("columns"));
}

Now depending on the need, add the conditions in the code, make the changes. The demand of this action type is : if columns attribute is present, then the user may or may not put the length parameter, but if the columns are absent, length will have no value. So now the code gets modified and looks like this :

if (type.equals(SusiAction.RenderType.table.toString()) && boa.has("columns")) {                                                                                                      actions.put(SusiAction.tableAction(boa.getJSONObject("columns"),
         		boa.has("length") ?boa.getInt("length") : -1));
}

But wait.. To see these changes up on Susi, open an issue on the server repo and on all the client repos as well.

Server | ios client | android client | web chat client

Once you are sure things are working as expected, send a pull request to server after making the changes(follow the pull request practices followed at FOSSASIA).

 

Continue ReadingHow to add a new attribute in an action type for SUSI.AI

Android App Debugging over WiFi for PSLab

Why do WiFi debugging when you have USB cable? PSLab is an Open Source Hardware Device which provides a lot of functionality that is required to perform a general experiment, but like many other devices it  only provides an Android mini usb port. This means developers can’t connect another USB cable as the  mini port is already busy powering and communicating with the USB device that is connected.

How can developers debug our Android App over WiFi? Please follow these steps:

  • Connect your Android Device to PC through USB cable and make sure USB-Debugging is enabled in Developers Option.
  • Turn on Wifi of Android Device if its off and make sure its connected to router because that’s going to act as bridge between your Android device and PC for communication.
  • Open your terminal and type adb tcpip 5555
  • Now see the IP of your Android Device by About Phone -> Status -> IP or adb shell netcfg
  • Then type adb connect <DEVICE_IP_ADDRESS>:5555
  • Almost Done! Disconnect USB and start with wireless debugging.

Now you would see your device coming up in the prompt when clicked run from Android Studio. All the logs can be seen in Android Monitor. Similarly as you see during debugging through USB cable.

To get in touch with us or ask any question about the project, just drop a message at https://gitter.im/fossasia/pslab

Also if this project interest you, feel free to contribute or raise any issue. PSLab-Android.

Continue ReadingAndroid App Debugging over WiFi for PSLab

Youtube videos in the SUSI iOS Client

The iOS and android client already have the functionality to play videos based on the queries. In order to implement this feature of playing videos in the iOS client, we use the Youtube Data API v3. The task here was to create an UI/UX for the playing of videos within the app. An API call is made initially to fetch the youtube videos based on the query and they video ID of the first object is extracted and used to play the video.

The API endpoint for youtube data API looks like:

https://www.googleapis.com/youtube/v3/search?part=snippet&q={query}&key={your_api_key}

Using this we get the following result: ( I am adding only the first item which is required since the response is too long )

Path: $.items[0]

{

 "kind": "youtube#searchResult",

 "etag": "\"m2yskBQFythfE4irbTIeOgYYfBU/oR-eA572vNoma1XIhrbsFTotfTY\"",

 "id": {

"kind": "youtube#channel",

"channelId": "UCQprMsG-raCIMlBudm20iLQ"

 },

 "snippet": {

"publishedAt": "2015-01-01T11:06:00.000Z",

"channelId": "UCQprMsG-raCIMlBudm20iLQ",

"title": "FOSSASIA",

"description": "FOSSASIA is supporting the development of Free and Open Source technologies for social change in Asia. The annual FOSSASIA Summit brings together ...",

"thumbnails": {

"default": {

"url": "https://yt3.ggpht.com/-CP18cWbo34A/AAAAAAAAAAI/AAAAAAAAAAA/kEmIgO8OjCk/s88-c-k-no-mo-rj-c0xffffff/photo.jpg"

},

"medium": {

"url": "https://yt3.ggpht.com/-CP18cWbo34A/AAAAAAAAAAI/AAAAAAAAAAA/kEmIgO8OjCk/s240-c-k-no-mo-rj-c0xffffff/photo.jpg"

},

"high": {

"url": "https://yt3.ggpht.com/-CP18cWbo34A/AAAAAAAAAAI/AAAAAAAAAAA/kEmIgO8OjCk/s240-c-k-no-mo-rj-c0xffffff/photo.jpg"

}

},

"channelTitle": "FOSSASIA",

"liveBroadcastContent": "upcoming"

 }

}

We parse the above object to grab the videoID, based on the query,  we will use code below:

if let itemsObject = response[Client.YoutubeResponseKeys.Items] as? [[String : AnyObject]] {
    if let items = itemsObject[0][Client.YoutubeResponseKeys.ID] as? [String : AnyObject] {
         let videoID = items[Client.YoutubeResponseKeys.VideoID] as? String
         completion(videoID, true, nil)
    }
}

This videoID is returned to the Controller where this method was called.

Now, we begin with designing the UI for the same. First of all, we need a view on which the youtube video will be played and this view would help dismiss the video by clicking on it.

First, we add the blackView to the entire screen.

// declaration
let blackView = UIView()

// Add backgroundView
func addBackgroundView() {

   If let window = UIApplication.shared.keyWindow {

           self.view.addSubview(blackView) 

           // Cover the entire screen
           blackView.frame = window.frame

           blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
           blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))

   }

}

func handleDismiss() {
   UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
       self.blackView.removeFromSuperview()
   }, completion: nil)
}

Next, we add the YoutubePlayerView. For this we use the Pod `YoutubePlayer`. Since, it had a few warnings showing as well as some videos not being played I had to make fixes to the original pod and use my own customized version ( available here ).

// Youtube Player
lazy var youtubePlayer: YouTubePlayerView = {
    let frame = CGRect(x: 0, y: 0, width: self.view.frame.width - 16, height: self.view.frame.height * 1 / 3)
    let player = YouTubePlayerView(frame: frame)
    return player
}()

// Shows Youtube Player

func addYotubePlayer(_ videoID: String) {
    if let window = UIApplication.shared.keyWindow {

       // Add YoutubePlayer view on top of blackView
        self.blackView.addSubview(self.youtubePlayer)
        // Calculate and set frame

       let centerX = UIScreen.main.bounds.size.width / 2
        let centerY = UIScreen.main.bounds.size.height / 3
        self.youtubePlayer.center = CGPoint(x: centerX, y: centerY)

       // Load Player using the Video ID 
        self.youtubePlayer.loadVideoID(videoID)

        blackView.alpha = 0
        youtubePlayer.alpha = 0

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
            self.blackView.alpha = 1
            self.youtubePlayer.alpha = 1
       }, completion: nil)
    }
}

We are set with the UI and the only thing we are left with is to actually call the API in the client and after getting the `videoID` from that we call the above method passing this `videoID`. Before calling we need to check whether our query contains the play action or not and if it does we make the API call and add the player.

if let text = inputTextField.text {
    if text.contains("play") || text.contains("Play") {
        let query = text.replacingOccurrences(of: "play", with: "").replacingOccurrences(of: "Play", with: "")
        Client.sharedInstance.searchYotubeVideos(query) { (videoID, _, _) in
            DispatchQueue.main.async {
                if let videoID = videoID {
                    self.addYotubePlayer(videoID)
                 }
             }
          }
    }

}

We are all set now!Below is the output for the Youtube Player:

Continue ReadingYoutube videos in the SUSI iOS Client