File Upload Validations on Open Event Frontend

In Open Event Frontend we have used semantics ui’s form validations to validate different fields of a form. There are certain instances in our app where the user has to upload a file and it is to be validated against the suggested format before uploading it to the server. Here we will discuss how to perform the validation.

Semantics ui allows us to validate by facilitating pass of an object along with rules for its validation. For fields like email and contact number we can pass type as email and number respectively but for validation of file we have to pass a regular expression with allowed extension.The following walks one through the process.

fields : {
  file: {
    identifier : 'file',
    rules      : [
      {
        type   : 'empty',
        prompt : this.l10n.t('Please upload a file')
      },
      {
        type   : 'regExp',
        value  : '/^(.*.((zip|xml|ical|ics|xcal)$))?[^.]*$/i',
        prompt : this.l10n.t('Please upload a file in suggested format')
      }
    ]
  }
}

Here we have passed file element (which is to be validated) inside our fields object identifier, which for this field is ‘file’, and can be identified by its id, name or data-validate property of the field element. After that we have passed an array of rules against which the field element is validated. First rule gives an error message in the prompt field in case of an empty field.

The next rule checks for allowed file extensions for the file. The type of the rule will be regExp as we are passing a regular expression which is as follows-

/^(.*.((zip|xml|ical|ics|xcal)$))?[^.]*$/i

It is little complex to explain it from the beginning so let us breakdown it from end-

 

$ Matches end of the string
[^.]* Negated set. Match any character not in this set. * represents 0 or more preceding token
( … )? Represents if there is something before (the last ?)
.*.((zip|xml|ical|ics|xcal)$) This is first capturing group ,it contains tocken which are combined to create a capture group ( zip|xml|ical|ics|xcal ) to extract a substring
^ the beginning of the string

Above regular expression filters all the files with zip/xml/ical/xcal extensions which are the allowed format for the event source file.

References

  • Ivaylo Gerchev blog on form validation in semantic ui
  • Drmsite blog on semantic ui form validation
  • Semantic ui form validation docs
  • Stackoverflow regex for file extension

Using Picasso to Show Images in SUSI Android

Important skills of SUSI.AI are to display web search queries, maps of any location and provide a list of relevant information of a topic. This blog post will cover why Glide is replaced by Picasso to show images related to these action types and how it is implemented in SUSI AndroidPicasso is a powerful image downloading and caching open source library developed by Square.

Why Glide is replaced by Picasso to show images in SUSI Android?

Previously we used Glide library to show preview in SUSI Android but we replace it because it was creating an error continuously.

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity at com.bumptech.glide.manager.RequestManagerRetriever  at Com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102) at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:87)at com.bumptech.glide.Glide.with(Glide.java:629)
atnorg.fossasia.susi.ai.adapters.recycleradapters.WebSearchAdapter.onBindViewHolder(WebSearchAdapter.java:74)

Reason for this error is when activity destroyed and again recreated the context used by glide is old one and  that activity already destroyed .

Glide.with(context).load(imageList.get(0))

One solution of this error is to use context.getApplicationContext()  but it is a bad idea. Another solution is to replace glide by picasso and later one is good because picasso is also a very good image downloading and caching library.

To use Picasso in your project you have to add dependency in build.gradle(Module) file.

dependencies {
  
  compile “com.squareup.picasso:picasso:2.4.0”
  
}

How Picasso is used in different actiontype

Map

“actions”: [
     {
       “type”: “map”,
       “latitude”: “1.2896698812440377”,
       “longitude”: “103.85006683126556”,
       “zoom”: “13”
     }
   ]

Link we used to retrieve image url is

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

Picasso will load image from this url and show image in the imageview. Here mapImage is the imageview in which map image is shown.

Picasso.with(currContext).load(mapHelper.getMapURL())
                       .into(mapImage, new  com.squareup.picasso.Callback() {    
                           @Override
                           public void onSuccess() {
                               pointer.setVisibility(View.VISIBLE);
                           }
                           @Override
                           public void onError() {
                               Log.d(“Error”, “map image can’t loaded”);
                           }
                       });

WebSearch

When we query like “Search for fog” we get ‘query’ in reply from server

“query”: “fog”

Now we use this query to retrieve image url which we used in Picasso to show images.Picasso load this image into previewImageView imageview. Image url is retrieved using  DuckDuckGo api. We are using url

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

It gives a json response which contains image url

Picasso.with(context).load(iconUrl)
      .into(holder.previewImageView, new  com.squareup.picasso.Callback() {
                  @Override
                   public void onSuccess() {
                         Log.d(“Sucess”,“image loaded successfully”);
                   }
                   @Override
                   public void onError() {
                       holder.previewImageView.setVisibility(View.GONE);
                     }
                });     

Here also com.squareup.picasso.Callback is use to find that image is loaded successfully or not.

RSS

When we query any like “dhoni” we get ‘link’ in reply from server

“title”: “Dhoni”,

“description”: “”,
“link”: “http://de.wikipedia.org/wiki/Dhoni”

We use this link in android-link-preview library to retrieve relevant image url and then Picasso use this url to load image into imageview previewImageView.

Picasso.with(currContext).load(imageList.get(0))
      .fit().centerCrop()
      .into(previewImageView);

Reference

Sorting Photos in Phimpme Android

The Phimpme Android application features a fully fledged gallery interface with an option to switch to all photos mode, albums mode and to sort photos according to various sort actions. Sorting photos via various options helps the user to get to the desired photo immediately without having to scroll down till the end in case it is the last photo in the list generated automatically by scanning the phone for images using the Android’s mediaStore class. In this tutorial, I will be discussing how we achieved the sorting option in the Phimpme application with the help of some code snippets.

To sort the all photos list, first of all we need a list of all the photos by scanning the phone using the media scanner class via the code snippet provided below:

uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
      String[] projection = {MediaStore.MediaColumns.DATA};
      cursor = activity.getContentResolver().query(uri, projection, null, null, null);

In the above code we are using a cursor to point to each photos and then we are extracting the path of the images and storing it in a list using a while loop. After we generate the list of path of all the images, we have to convert the into a list of media using the file path using the code below:

for (String path : listOfAllImages) {
          list.add(new Media(new File(path)));
      }
      return list;
  }

After generating the list of all images we can sort the photos using the Android’s collection class. In Phimpme Android we provide the option to sort photos in different categories namely:

  1. Name Sort action
  2. Date Sort action
  3. Size Sort action
  4. Numeric Sort action

As sorting is somewhat heavy task so doing this in the main thread will result in freezing UI of the application so we have to put this into an AsyncTask with a progress dialog to sort the photos. After putting the above four options in the menu options. We can define an Asynctask to load the images and in the onPreExecute method of the AsyncTask, we are displaying the progress dialog to the user to depict that the sorting process is going on as depicted in the code snippet below

AlertDialog.Builder progressDialog = new AlertDialog.Builder(LFMainActivity.this, getDialogStyle());
dialog = AlertDialogsHelper.getProgressDialog(LFMainActivity.this, progressDialog,
      getString(R.string.loading_numeric), all_photos ? getString(R.string.loading_numeric_all) : getAlbum().getName());
dialog.show();

In the doInBackgroundMethod of the AsyncTask, we are sorting the list of all photos using the Android’s collection class and using the static sort method defined in that class which takes the list of all the media files as a parameter and the MediaComparator which takes the sorting mode as the first parameter and the sorting order as the second. The sorting order decides whether to arrange the list in ascending or in descending order.

getAlbum().setDefaultSortingMode(getApplicationContext(), NUMERIC);
Collections.sort(listAll, MediaComparators.getComparator(getAlbum().settings.getSortingMode(), getAlbum().settings.getSortingOrder()));

After sorting, we have to update the data set to reflect the changes of the list in the UI. This we are doing in the onPostExecute method of the AsyncTask after dismissing the progress Dialog to avoid the window leaks in the application. You can read more about the window leaks in Android due to progressdialog here.

dialog.dismiss();
mediaAdapter.swapDataSet(listAll);

To get the full source code, you can refer the Phimpme Android repository listed in the resources below.

Resources

  1. Android developer guide to mediastore class: https://developer.android.com/reference/android/provider/MediaStore.html
  2. GitHub LeafPic repository: https://github.com/HoraApps/LeafPic
  3. Stackoverflow – Preventing window leaks in Android: https://stackoverflow.com/questions/6614692/progressdialog-how-to-prevent-leaked-window
  4. Blog – Sorting lists in Android: http://www.worldbestlearningcenter.com/tips/Android-sort-ListView.htm

Implementing Text-to-Speech (TTS) in SUSI Android

Mobile assistants are designed to perform tasks that the user “commands” through by chat UI or speech. The Android OS already provides Text to speech (TTS) and Speech to text (STT) features. This feature is available from Android version 1.6 onward. In this blog post I will show how tts is implemented in SUSI Android and how I fix the issue ‘delay in speech response’.

TextToSpeech class controls the tts engine. To use TextToSpeech class import it in the activity where you want to use text to speech feature.

import android.speech.tts.TextToSpeech;

After you import TextToSpeech class now we need to initialize TextToSpeech

TextToSpeech tts = new TextToSpeech(this,this);

Here first parameter is the Context and the other one is the listener. The listener is  use  to  inform our app that the engine is ready to use. In order to be notified we have to  implement  TextToSpeech.OnInitListener.

TextToSpeech.OnInitListener listener = new  TextToSpeech.OnInitListener {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS)
tts.setLanguage(Locale.UK/* set the default language*/);
}
}

Hence the engine can be initialized asIf status is success then, it means that TTS is initialized successfully and now we can use it. Otherwise, we can’t use it. setLanguage method is used to set language in which we want reply.

TextToSpeech tts = new TextToSpeech(getApplicationContext,listener)

When you use TTS one thing you have to remember that TTS run  on main thread so sometimes it may cause delays in text to speech conversion or it may block UI for a while. It is better to wrap it like below code.

new Handler().post(new Runnable() {
      @Override
      public void run() {
         tts = new TextToSpeech(getApplicationContext(), listener);
        }
    });

Now our engine is ready to speak, we need simply pass the string we want to read.

tts.speak(text to read,TextToSpeech.QUEUE_FLUSH, null, null);

But before tts.speak, it is important to check for the audio focus change request. It is important because only one audio source can have focus at a time. You can check it using below code.

private AudioManager.OnAudioFocusChangeListener afChangeListener =
           new AudioManager.OnAudioFocusChangeListener() {
                 public void onAudioFocusChange(int focusChange) {
                                                        //check for focus
                                                   }
                                           };

OnAudioFocusChangeListener is called when audio focus of the system is changed and according to value of focusChange either we stop TTS or keep using it.

AudioManager audiofocus = (AudioManager)                                    getSystemService(Context.AUDIO_SERVICE);

audiofocus is instance of AudioManager class. We need it to call requestAudioFocus method of AudioManager class. requestAudioFocus method returns the status of request for audio focus change. This method requires three parameter  instance of AudioManager.OnAudioFocusChangeListener, stream type and duration hint. If request is granted only then we can we can use tts.speak .

int result = audiofocus.requestAudioFocus(afChangeListener,AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {

tts.speak(text to read,TextToSpeech.QUEUE_FLUSH, null, null);

}

We were continuously facing issue ‘delay in speech response’ because voiceReply method implementation was wrong. We were initializing TextToSpeech on each call of voiceReply method and since onInit method runs on main thread causing delay in voice response. So I removed it and instead of initializing tts each time I used the tts instance already initialized when activity create.

 String spoken = reply;

textToSpeech.speak(spoken, TextToSpeech.QUEUE_FLUSHnull);

You can also control how the engine read text. Like we can modify pitch and speech rate.

tts.setPitch((float)pitch);

tts.setSpeechRate((float)speed);

Resource

Integration of Susi AI to Gitter

This blog post discusses the development of Susi Messenger bot on Gitter. It replies instantly to the messages sent to it, using the Susi API. The Streaming API notifies us when a user messages to the SUSI chat room. The REST API helps to message back with a reply from SUSI API, to the SUSI chat room.

Feel free to message to the already made SUSI AI account on Gitter and have a chat with it.

Prerequisites

  1. Basic knowledge about calling API’s and fetching data or posting data to the API.
  2. Node.js language.
  3. Github
  4. Heroku

Figure – Architecture for running SUSI AI on different messaging services.

This blog post will walk you through each of the steps required to integrate SUSI AI to Gitter:

Setup SUSI AI Bot on Gitter

  1. Create a Github or twitter account with a username having ‘Susi’ as its substring because this is the name that will be shown with the reply string we will get from Susi AI.
  2. Now you need to sign in to Gitter with a twitter or Github account from here.
  3. Create your community by visiting this page. After writing your community name press next, invite the people you want to be in this room and press next. You will be redirected to your communities lobby. This lobby is the chat room to which we will deploy our SUSI AI.
  4. Now visit the Gitter developer page, press sign in on the top right. You will be redirected to your apps page. Copy the personal access token written there as shown in this image: (The area colored black will have your access token).

5. On a new tab, in your browser visit   https://api.gitter.im/v1/rooms?access_token=YOUR_ACCESS_TOKEN, with YOUR_ACCESS_TOKEN replaced by the token we just copied.

A JSON object will be shown on our browser screen. You will see the value of ‘name’ key as YOUR_COMMUNITY_NAME/Lobby. Copy the id of this chat room, as we will need it later. You can refer to the image below, you will have your chat room id in the area colored black.

  1. Create a new heroku app here.

This app will accept the requests from Gitter and Susi api.

  1. Set the config variables for this heroku app in the setting tab of your account. Set ROOM_ID to the id of the chat room and TOKEN to the personal access token, we copied in steps 4 and 5.

These were the formalities to be done to have our chat bot account on Gitter.

  1. Let’s jump to the code part of how this integration will be done:

To use the two config variables set in Heroku, we need these two lines in our Node js code:

var roomId = process.env.ROOM_ID;
var token = process.env.TOKEN;

We need to set up an options variable with our access token and room id in it:

// Setting the options variable to use it in the https request block
var options = {
    hostname: 'stream.gitter.im',
    port:     443,
    path:     '/v1/rooms/' + roomId + '/chatMessages',
    method:   'GET',
    headers:  {'Authorization': 'Bearer ' + token}
};

We will send this options variable when making a request so that Gitter lets our request through and notifies us when a client messages to our SUSI chat room.

The res.on(‘data’) accepts a function which is called when a client messages to our SUSI chat room:

// making a request to gitter stream API
var req = https.request(options, function(res) {
 res.on('data', function(chunk) {
    // do stuff
 }
}

req.on('error', function(e) {
 console.log('Hey something went wrong: ' + e.message);
});

req.end();

According to the docs of REST API in Gitter, the JSON data that we receive when a client sends a message to a chat room is like this:

To get this response set in a variable, we can use this code snippet:

res.on('data', function(chunk) {
   var msg = chunk.toString();
   if(msg != " \n"){              // If message is not an empty message
     var jsonMsg = JSON.parse(msg);

Now we have this json response as shown in the above figure in the jsonMsg variable. To extract the client’s message from this json object:

var clientMsg = jsonMsg.text;

As we now have the user query in clientMsg variable. Let’s call Susi API and fetch an answer from it for a query.

As an example, let’s first take the query as ‘hi’ and visit http://api.asksusi.com/susi/chat.json?q=hi from the browser. We will get a JSON object as follows:

{
        "query": "hi",
        "count": 1,
        "client_id": "aG9zdF8xMDMuMjkuMjIyLjE4MA==",
        "query_date": "2017-07-17T02:29:44.171Z",
        "answers": [{
            "data": [{
                "0": "hi",
                "token_original": "hi",
                "token_canonical": "hi",
                "token_categorized": "hi",
                "timezoneOffset": "-330",
                "language": "en"
            }],
            "metadata": {"count": 1},
            "actions": [{
                    "type": "answer",
                    "language": "de",
                    "expression": "Hallo!"
            }],
   "skills": ["/susi_skill_data/models/general/smalltalk/de/German-Standalone-aiml2susi.txt"]
        }],
        "answer_date": "2017-07-17T02:29:44.179Z",
        "answer_time": 8,
        "language": "en",
        "session": {"identity": {
            "type": "host",
            "name": "103.29.222.180",
            "anonymous": true
        }}
    }

The answer can be found as the value of the key named expression. In this case, it is “Hallo!”.

To fetch the answer through coding for our client message, we can use this code snippet in Node js:

// including request module
var request = require(‘request’);

// setting options to make a successful call to Susi API.
var susiOptions = {
            method: 'GET',
            url: 'http://api.asksusi.com/susi/chat.json',
            qs:
            {
                timezoneOffset: '-330',
                q: clientMsg  //the client message sent to SUSI chat room.
            }
        };

// A request to the Susi bot
request(susiOptions, function (error, response, body) {
    if (error)
        throw new Error(error);
    // answer fetched from susi
    ans = (JSON.parse(body)).answers[0].actions[0].expression;
}

The properties required for the call are set up through a JSON object (i.e. susiOptions). Pass the susiOptions object to our request function as its 1st parameter. The response from the API will be stored in ‘body’ variable. We need to parse this body, to be able to access the properties of that body object. Hence, fetching the answer from Susi API.

As we now have the answer, let’s call the API of Gitter to show our answer back to the user. Let’s code the request for that:

// To send a reply by Susi AI to client's message back to Gitter
         var gitterOptions = {
                               method: 'POST',
                               url: 'https://api.gitter.im/v1/rooms/'+roomId+'/chatMessages',
                               headers:
                               {
                                 'authorization': 'Bearer '+ token ,
                                 'content-type': 'application/json',
                                 'accept': 'application/json'
                               },
                               body:
                               {
                                 text: ans
                               },
                               json: true
                             };

         // making the request to Gitter API
         request(gitterOptions, function (error, response, body) {
           if(error)
             throw new Error(error);
           console.log(body);
         });

Hence, we have made the basic chat work!

The streaming API of Gitter notifies us for every message sent to our chat room. We will also be notified about the reply message sent by ourselves. To not fall into an infinite loop of answers and questions by SUSI itself, we must include this line in our code:

res.on('data', function(chunk) {
   var msg = chunk.toString();
   if(msg != ” \n”){              // If message is not an empty message
     var jsonMsg = JSON.parse(msg);
     if(jsonMsg.fromUser.displayName != 'SusiAI'){ // If it’s not our own answer
        // do stuff 
     }
   }
}

req.on('error', function(e) {
 console.log('Hey something went wrong: ' + e.message);
});

req.end();

The display name in my case is ‘SusiAI’, but it may be different in your case according to the Github or Twitter id made by you.

  1. Upload this code to Github.
  2. Connect the Heroku app to the Github repository, which has your code.

  1. Deploy on the development branch. If you intend to contribute, it is recommended to Enable Automatic Deploys.

Branch Deployment.

Successful Deployment.

  1. Go to your Gitter room created and enjoy chatting with Susi.Resources

Permission Manager in Open Event API Server

Open Event API Server uses different decorators to control permissions for different access levels as discussed here. Next challenging thing for permissions was reducing redundancy and ensuring permission decorators are independent of different API views. They should not look to the view for which they are checking the permission or some different logic for different views.

In API Server, we have different endpoints that leads to same Resource this way we maintain relationships between different entities but this leads to a problem where permission decorators has to work on different API endpoints that points to different or same resource and but to check a permission some attributes are required and one or more endpoints may not provide all attributes required to check a permission.

For instance, PATCH /session/id` request requires permissions of a Co-Organizer and permission decorator for this requires two things, user detail and event details. It is easy to fetch user_id from logged in user while it was challenging to get “event_id”. Therefore to solve this purpose I worked on a module named “permission_manager.py” situated at “app/api/helpers/permission_manager.py” in the codebase

Basic Idea of Permission Manager

Permission manager basically works to serve the required attributes/view_kwargs to permission decorators so that these decorators do not break

Its logic can be described as:

    1. It first sits in the middle of a request and permission decorator
    2. Evaluates the arguments passed to it and ensure the current method of the request (POST, GET, etc ) is the part of permission check or not.
    3. Uses two important things, fetch and fetch_as
      fetch => value of this argument is the URL parameter key which will be fetched from URL or the database ( if not present in URL )
      fetch_as => the value received from fetch will be sent to permission decorator by the name as the value of this option.
    4. If the fetch key is not there in URL, It uses third parameter model which is Model if the table from where this key can be fetched and then passes it to permission decorator
    5. Returns the requested view on passing access level and Forbidden error if fails

This way it ensures that if looks for the only specific type of requests allowing us to set different rules for different methods.

if 'methods' in kwargs:
        methods = kwargs['methods']

    if request.method not in methods:
        return view(*view_args, **view_kwargs)

Implementing Permission Manager

Implementing it was a simple thing,

  1. Firstly, registration of JSON API app is shifted from app/api/__init__.py to app/api/bootstrap.py so that this module can be imported anywhere
  2. Added permission manager to the app_v1 module
  3. Created permission_manager.py in app/api/helpers
  4. Added it’s usage in different APIs

An example Usage:

decorators = (api.has_permission('is_coorganizer', fetch='event_id', fetch_as="event_id", methods="POST",
                                     check=lambda a: a.get('event_id') or a.get('event_identifier')),)

Here we are checking if the request has the permission of a Co-Organizer and for this, we need to fetch event_id  from request URI. Since no model is provided here so it is required for event_id in URL this also ensures no other endpoints can leak the resource. Also here we are checking for only POST requests thus it will pass the GET requests as it is no checking.

What’s next in permission manager?

Permission has various scopes for improving, I’m still working on a module as part of permission manager which can be used directly in the middle of views and resources so that we can check for permission for specific requests in the middle of any process.

The ability to add logic so that we can leave the check on the basis of some logic may be adding some lambda attributes will work.

Resources

Uploading images to Dropbox from the Phimpme App

The Phimpme Android application along with the camera, gallery and image editing features offers the option to upload images to many social media and cloud storages without having to install several other applications. As we can see from the screenshot below, Phimpme application contains a user-friendly accounts screen to connect to the accounts using a username and password so that we can upload photos from the share screen to that particular account later on.

One such famous cloud storage is the Dropbox and in this tutorial, I am explaining how I implemented the account authentication and image uploading feature to Dropbox in the Phimpme Android application.

Step 1

The first thing we need to do is to create an application in the Dropbox application console and to get the app key and the API secret key which we will require later on for the authentication purposes. To create an application on the Dropbox application console page,

  1. Go to this link. It will open a page as depicted in the screenshot below:
  2. Now click on the Dropbox API option.
  3. Click on App folder – access to a single folder created specifically for your app.
  4. Write the name of your application and press the create app button.

After this, we will be redirected to the page which will contain all the keys required to authenticate and upload photos.

Step 2

After getting the keys, the next thing we need to do is install the Dropbox SDK. To do this:

  1. Download the Android SDK from this link and extract it.
  2. Copy the dropbox-android-sdk-1.6.3.jar and json_simple-1.1.jar file to the libs folder.
  3. Click on the add as library button by right clicking on the jar files added.
  4. Copy the below-mentioned code in the AndroidManifest.xml file which defines the dropbox login activity within a new activity tag.
android:name="com.dropbox.client2.android.AuthActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="orientation|keyboard">
<intent-filter>
     <!-- Change this to be db- followed by your app key -->
         <data android:scheme="db-app_key" />
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.BROWSABLE"/>
         <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

In the 7th line of the code snippet, replace the app_key with the key you received from following the step 1.

Step 3

After setting up everything, we need to extract the access token for the user to upload the photos in that particular account. To do this, we can make use of the below code snippet, which uses the dropbox SDK we installed in step 2 to create an object named mDBApi and initialises it to authenticate the user.

private DropboxAPI<AndroidAuthSession> mDBApi;AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
AndroidAuthSession session = new AndroidAuthSession(appKeys);
mDBApi = new DropboxAPI<AndroidAuthSession>(session);

After initialisation in the onCreate method of the activity, we can authenticate the user using the following line of code.

mDBApi.getSession().startOAuth2Authentication(MyActivity.this);

This will open up a window where the user will be prompted to login to their dropbox account. After the login is finished, we will be taken back to the activity which made the authentication call, so in the onResume method, we need to get the access token of the user which will be used later on to upload the images using the following code snippet provided below:

mDBApi.getSession().finishAuthentication();
String accessToken = mDBApi.getSession().getOAuth2AccessToken();

After we have stored the access token, we can upload the selected image to the Dropbox using the following line of code.

File file = new File("working-draft.jpg");
FileInputStream inputStream = new FileInputStream(file);
Entry response = mDBApi.putFile("/magnum-opus.jpg", inputStream,
                              file.length(), null, null);

For more information on uploading and retrieving data from the Dropbox account, you can go to the dropbox developer guide and for working example refer to the Phimpme Android repository in the resources below.

Resources

  1. Phimpme Repo : Phimpme Android github repository.
  2. Dropbox official documentation : https://www.dropbox.com/developers-v1/core/start/android
  3. Dropbox application console : https://www.dropbox.com/developers/apps
  4. Stackoverflow example to upload image on Dropbox : https://stackoverflow.com/questions/10827371/upload-photo-to-dropbox-by-android

Face detection in Phimpme Android’s Camera

The Phimpme Android application comes with a well-featured camera application which offers almost all the functionality an advanced camera user searches for. It comes with a wide range of options to apply different scene modes in the camera and also to detect the faces using the front or the back camera of the device. In this tutorial, I will be discussing how we achieved the face detection functionality in Phimpme.

In the Phimpme application, we have the option in the settings to enable the face detection just as depicted in the screenshot below. After enabling it the Camera starts detecting the faces and draws rectangular boxes on the number of faces detected by the camera.

I will be explaining step by step to achieve this using some code snippets.

Step 1

First, we have to check whether our device supports the face detection functionality to avoid unnecessary application crashes using the Android’s Camera.Parameters class.

After the check we have to create a new class named My FaceDetectionListener which will be implementing the Android’s Camera.FaceDetectionListener. The face detection class overrides the function onFaceDetection and passes the array of Faces detected and the camera as the parameter to this function.

class MyFaceDetectionListener implements CameraController.FaceDetectionListener {
  @Override  
  public void onFaceDetection(CameraController.Face[] faces) { 
    faces_detected = new CameraController.Face[faces.length];     System.arraycopy(faces, 0, faces_detected, 0, faces.length);
  }
 }

Step 2

After creating this class, we need to start the camera of the application to set the face detection listener to it. This can be done by the code snippet provided below

camera = Camera.open(cameraId);

We can open the front camera and the back camera by simply changing the cameraId. If we want to open the front camera, then we need to set the camera Id value as 1 and if we want the back camera to open up we can set the camera Id to be 0.
After this, we can set the face detection listener in the camera. This can be done using the below code snippet.

mCamera.setFaceDetectionListener(fDetectionListener);
   mCamera.startFaceDetection();

The set face detection listener function takes in the object of the class we created in step 1 as the parameter and calls the Android’s pre defined function to start the face detection. The object of the class we created in step 1 can be created and initialised with the help of code snippet below.

MyFaceDetectionListener fDListener = new MyFaceDetectionListener();

After we have set the detection listener in the camera, as soon as it detects the face, it will call the overridden function onFaceDetection but how do the user know if the face has been detected or not. For this we have to create a rectangular box of size approximately that of the face detected. This can be done with the following code snippet.

int l = faces[i].rect.left;
               int r = faces[i].rect.right;
               int t = faces[i].rect.top;
               int b = faces[i].rect.bottom;
               Rect uRect = new Rect(l0, t0, r0, b0);

To get the full source code, please check out the Phimpme Android github repository.

Resources

  1. Phimpme Android Github repository
  2. Complete tutorial on face detection in Android
  3. Leafpic github repository
  4. Android Camera API Google developer page

Multiple Color Effects in Phimpme Camera

The Phimpme Android’s camera comes with an option to switch between various color effects along with various other functionalities. To select different color modes, we have added a toggle button at the top right corner of the camera interface and which switches from the range of color effect available and on long clicking the toggle button, it resets the effect to normal. To show the functionality of the toggle button we have made use of the Showcase view in the application which displays all the functionality of the toggle button on the first run of the application.

In this tutorial, I will be discussing how we implemented the color effects feature in the Phimpme Android application with the help of some code snippets.

Step 1

Firstly we have to create a toggle button in the camera interface and have to set the onclicklistener on it to change the various color effects on the button click. This can be done with the following code snippet.

toggle.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
       //Actions here
}

Similarly, we have to set the long click listener on the toggle button which will handle the reset color effects function in the application.

Step 2

The next thing we need is to extract all the color modes supported by the device and to create an Arraylist of it so that we can call the respective values by just increasing the index on toggle button click. This can be done with the help of the following code snippet.

Now we have all the supported color modes along with the normal mode stored in the values list. For instance,

  1. Mono
  2. Negative
  3. Solarize
  4. Sepia
  5. Neon

Hence on button click, we have to get the color values using the list index and we have to set the value to the camera parameter from where we extracted the supported color effects.

For this, we can make use of a static variable named colorNum and initialise it with 0 and on button click we can just increment this variable by 1 and can set the color effect using the code snippet provided below

final String color = colorEffect.get(colorNum);
CameraController.SupportedValues supported_values = camera_controller.setColorEffect(color);
if (supported_values != null) {
    color_effects = supported_values.values;
   applicationInterface.setColorEffectPref(supported_values.selected_value);
}

And on the long click listener method of the camera, we can set the value of the variable to be 0 and can set the color values accordingly.

To get the full source code on changing the color effects in the camera and to know about adding the showcase view which we have used in this to show the functionality. Please refer to the Phimpme Android repository.

Resources

  1. Open camera Github repository
  2. Color effects in Android camera
  3. Camera API developer page
  4. Amlcurran Showcaseview

 

Acceptance Testing of a Feature in Open Event Frontend

In Open Event Frontend, we have integration tests for ember components which are used throughout the project. But even after those tests, user interaction could pose some errors. We perform acceptance tests to alleviate such scenarios.

Acceptance tests interact with application as the user does and ensures proper functionality of a feature or to determine whether or not the software system has met the requirement specifications. They are quite helpful for ensuring that our core features work properly.

Let us write an acceptance test for register feature in Open Event Frontend.

import { test } from 'qunit';
import moduleForAcceptance from 'open-event-frontend/tests/helpers/module-for-acceptance';

moduleForAcceptance('Acceptance | register');

In the first line we import test from ‘ember-qunit’ (default unit testing helper suite for Ember) which contains all the required test functions. For example, here we are using test function to check the rendering of our component. We can use test function multiple times to check multiple components.

Next, we import moduleForAcceptance from ‘open-event-frontend/tests/helpers/module-for-acceptance’ which deals with application setup and teardown.

test('visiting /register', function(assert) {
  visit('/register');

  andThen(function() {
    assert.equal(currentURL(), '/register');
  });
});

Inside our test function, we simulate visiting  /register route and then check for the current route to be /register.

test('visiting /register and registering with existing user', function(assert) {
  visit('/register');
  andThen(function() {
    assert.equal(currentURL(), '/register');
    fillIn('input[name=email]', 'opev_test_user@nada.email');
    fillIn('input[name=password]', 'opev_test_user');
    fillIn('input[name=password_repeat]', 'opev_test_user');
    click('button[type=submit]');
    andThen(function() {
      assert.equal(currentURL(), '/register');
      // const errorMessageDiv = findWithAssert('.ui.negative.message');
      // assert.equal(errorMessageDiv[0].textContent.trim(), 'An unexpected error occurred.');
    });
  });
});

Then we simulate visiting /register route and register a dummy user. For this, we first go to /register route and then check for the current route to be register route. We fill the register form with appropriate data and hit submit.

test('visiting /register after login', function(assert) {
  login(assert);
  andThen(function() {
    visit('/register');
    andThen(function() {
      assert.equal(currentURL(), '/');
    });
  });
});

The third test is to simulate visiting /register route with user logged in and this is very simple. We just visit /register route and then check if we are are at / route or not because a user redirects to / route when he tries to visit /register after login.

And since we checked for all the possible combinations, to run the test we simply use the following command-

ember test --server

But there is a little demerit to acceptance tests. They boot up the whole EmberJS application and start us at the application.index route. We then have to navigate to the page that contains the feature being tested. Writing acceptance tests for each and every feature would be a big waste of time and CPU cycles. For this reason, only core features are tested for acceptance.

Resources