Implementing the Feedback section on Skills CMS

In this blog post, we are going to implement the Skill feedback system on the Skills CMS. The features that are added by this implementation are displaying all the comments/feedbacks of the user, ability to add new feedback and also option to edit a previous feedback that was added.

The UI interacts with the back-end server via two APIs –

Detailed explanation of the implementation

  • The first task was to create a separate component for the feedback section – SkillFeedbackCard.js, along with the CSS file SkillFeedbackCard.css
  • Create ES6 function to get all the Feedbacks of a skill,namely getFeedback(), on the parent component, i.e, SkillListing.js
getFeedback = () => {
    let getFeedbackUrl = `${urls.API_URL}/cms/getSkillFeedback.json`;
    let modelValue = 'general';
    this.groupValue = this.props.location.pathname.split('/')[1];
    this.languageValue = this.props.location.pathname.split('/')[3];
    getFeedbackUrl = getFeedbackUrl + '?model=' + modelValue + '&group=' + this.groupValue + '&language=' + this.languageValue + '&skill=' + this.name;

    let self = this;
    // Get skill feedback of the visited skill
    $.ajax({
        url: getFeedbackUrl,
        dataType: 'jsonp',
       crossDomain: true,
        jsonp: 'callback',
        success: function (data) {
            self.saveSkillFeedback(data.feedback);
        },
        error: function(e) {
            console.log(e);
        }
    });
};

saveSkillFeedback = (feedback = []) => {
    this.setState({
        skill_feedback: feedback
    })
}

 

  • This above code contains the function getFeedback(), that makes an API call to the server for getting all the feedbacks. On successfully getting the response, the feedback array of the response is then passed to a function, saveSkillFeedback(), which in turn updates the skill_feedback state, which was declared in the constructor. This re-renders the components and displays the feedback in the UI.
{
  "feedback": [
    {
      "feedback": "Awesome skill!",
      "email": "[email protected]",
      "timestamp": "2018-06-12 19:28:39.297"
    },
    {
      "feedback": "Awesome skill!",
      "email": "[email protected]",
      "timestamp": "2018-06-12 21:35:53.048"
    }
  ],
  "session": {"identity": {
    "type": "host",
    "name": "141.101.98.18_a7ab9c4d",
    "anonymous": true
  }},
  "skill_name": "aboutsusi",
  "accepted": true,
  "message": "Skill feedback fetched"
}

 

  • Then, we go ahead and create the function that is responsible for posting new feedback and editing them as well.
postFeedback = (newFeedback) => {

    let baseUrl = urls.API_URL + '/cms/feedbackSkill.json';
    let modelValue = 'general';
    this.groupValue = this.props.location.pathname.split('/')[1];
    this.languageValue = this.props.location.pathname.split('/')[3];
    let postFeedbackUrl = baseUrl + '?model=' + modelValue + '&group=' + this.groupValue + '&language=' + this.languageValue + '&skill=' + this.name + '&feedback=' + newFeedback + '&access_token='+cookies.get('loggedIn');

    let self = this;
    $.ajax({
        url: postFeedbackUrl,
        dataType: 'jsonp',
        jsonp: 'callback',
        crossDomain: true,
        success: function (data) {
            self.getFeedback()
        },
        error: function(e) {
            console.log(e);
        }
    });
};

 

  • This above code snippet contains the function postFeedback(newFeedback), that takes the user feedback and make an API call to update it on the server.
  • All the required functions are ready. Now we add the SkillFeedbackCard.js component on the SkillListing.js component and pass useful data in the props.
<SkillFeedbackCard
    skill_name={this.state.skill_name}
    skill_feedback={this.state.skill_feedback}
    postFeedback={this.postFeedback}
/>
  • The next step is creating the UI for the SkillFeedbackCard.js component. We have used standard Material-UI components for creating the UI, that includes List, ListItem, Divider, IconButton, etc.
  • Code snippet for the Feedback ListItem  –
<ListItem
    key={index}
    leftAvatar={<CircleImage name={data.email.toUpperCase()} size='40' />}
    primaryText={data.email}
    secondaryText={<p> {data.feedback} </p>}
/>

 

  • The next part of the UI implementation creating option to edit and post feedback.
  • Code snippet for the Post feedback section  –
className=“feedback-textbox”> id=“post-feedback” hintText=“Skill Feedback” defaultValue=“” errorText={this.state.errorText} multiLine={true} fullWidth={true} /> label=“Post” primary={true} backgroundColor={‘#4285f4’} style={{ margin: 10 }} onClick={this.postFeedback} />

 


 

  • For the edit section, I have used a Dialog box for it. Code snippet for the Edit feedback section  –
<Dialog
    title="Edit Feedback"
    actions={actions}
    modal={false}
    open={this.state.openDialog}
    onRequestClose={this.handleClose}
>
    <TextField
        id="edit-feedback"
        hintText="Skill Feedback"
        defaultValue={userFeedback}
        errorText={this.state.errorText}
        multiLine={true}
        fullWidth={true}
    />
</Dialog>

 

This was the implementation for the Skill Feedback System on the Skills CMS and I hope, you found the blog helpful in making the understanding of the implementation better.

Resources

 

Continue Reading

Displaying all the images from storage at once in Phimpme Android Application

In the Phimpme Android application, the images are displayed in the albums in which they are indexed in the device’s storage. However, there is also an “All photos” section in the application where all the images present in the device’s storage are displayed at once irrespective of the folder they’re indexed in. So in this post, I will be discussing how we achieved the “All Photos”  functionality.

Android framework provides developers with a media content provider class called MediaStore. It contains metadata for all available media on both internal and external storage devices. With the help of particular methods we can obtain metadata for all the images stored in the device’s storage.

Step 1

So First we need to get the Uri from MediaStore content provider pointing to the media(here media refers to the photos stored on the device). This can be done by the following snippet of code.

Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

Step 2

Now retrieving a cursor object containing the data column for the image media is required to be performed. The data column will contain the path to the particular image files on the disk. This can be done by querying the MediaColumns table of the MediaStore class, which can be performed by the use of the content resolver query method. The mentioned functionality can be achieved by the following lines of code.

String[] projection = {MediaStore.MediaColumns.DATA};
Cursor cursor = activity.getContentResolver().query(uri, projection, null, null, null);

Step 3

In the final step, we would retrieve the path of all the images by iterating through the cursor object obtained in the previous step and keep adding those paths to an ArrayList<String>. Creating Media objects passing in the image path and concurrently adding those Media objects to an ArrayList<Media> to be done thereafter. Finally, the dataset(ArrayList<Media> in this case) containing Media objects for all the images is required to be passed to the Media Adapter in order to display all the photos in the UI. The code snippets used for the final steps are provided below.

while (cursor.moveToNext()) {
  absolutePathOfImage = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
  listOfAllImages.add(absolutePathOfImage);
}
ArrayList<Media> list = new ArrayList<>();

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

This is how we achieved the functionality to display all the images from the storage on a single screen(at once) in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

The screenshot for displaying the “All photos” section is provided below.

Resources

1.Android Developer Guide – https://developer.android.com/reference/android/provider/MediaStore

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Displaying all the images from gallery in android – https://deepshikhapuri.wordpress.com/2017/03/20/get-all-images-from-gallery-in-android-programmatically/

Continue Reading

Adding option to Unfavourite image in Phimp.Me

This blog is in accordance with the P.R #1900. Here I have implemented the option to unfavourite image in the Phimp.Me android application.

Implementation

In the Phimp.Me app there are the following modes present:

1. all_photos:

All the photos are displayed in this mode irrespective of where they are saved.

2. fav_photos:

The photos which are added to favourites are displayed in the fav_photos.

3. Albums_mode:

All the albums which are present in the app are displayed in the albums_mode.

The main idea here is to find whether the selected image is already FAVOURITE or not. If it is already FAVORITED then it can be removed from that mode by removing its path form the Realm Database. If it isn’t already FAVORITED then the image is ignored and the next image is taken into consideration.

The process of removing the images from favourites can be an expensive one as the user can select myriad images which would ultimately block the Main UI. So it is better handled asynchronously and is implemented using the AsyncTask.

Whenever the user adds an image to the FAVOURITES, it gets added to the Realm Database where the model class being used is the FavouriteImagesModel.The selected media in the all_photos and fav_photos mode can be accessed via by selectedMedia.size()  and the number of selected media in the albums_mode can be accessed by getAlbum().getSelectedCount().

So in the execute method of doInBackground() a condition check is initially made and then 2 separate loops are run depending upon the mode in which the selected images exist.

Initially it is checked whether the selected image is already a FAVOURITE one or not by the following code. If it belongs to the favourite mode then the size of the favouriteImageModels would become 1.

RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where
                                               (FavouriteImagesModel.class).equalTo(“path”, selectedMedias.get(i).getPath( )).findAll( );

If ( favouriteImagesModels.size( ) == 1) {
            favouriteImagePresent = true;
             imagesUnfavourited++;
   }

Now as the image belongs to the favourite mode we ultimately use the following code to remove the image from FAVOURITES.

 favouriteImagesModels.deleteAllFromRealm();

The full code which handle the option to unfavourite an image is shown below.

@Override
                   protected Boolean doInBackground(String arg0) {
                    

        if ( all_photos || fav_photos )   {


                           realm = Realm.getDefaultInstance();
                           realm.executeTransaction ( new Realm.Transaction( ) {
                               

                               @Override
                               public void execute (Realm realm)  {
                                   for (int i = 0 ;  i < selectedMedias.size( ) ;  i++) {
                                       RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where
                                               (FavouriteImagesModel.class).equalTo(“path”, selectedMedias.get(i).getPath( )).findAll( );
                                       If ( favouriteImagesModels.size( ) == 1) {
                                           favouriteImagePresent = true;
                                           imagesUnfavourited++;
                                       }


                                       favouriteImagesModels.deleteAllFromRealm();
                                   }
                               }
                           });
                       }

         else if ( !fav_photos && !albumsMode ) {
                           realm = Realm.getDefaultInstance();
                           realm.executeTransaction(new Realm.Transaction() {
                           

                              @Override
                               public void execute(Realm realm) {
                                   for (int i = 0;  i < getAlbum().getSelectedCount();  i++) {
                                       RealmResults<FavouriteImagesModel> favouriteImagesModels = realm.where
                                               (FavouriteImagesModel.class).equalTo(“path”, getAlbum( ).getSelectedMedia(i).getPath( ) ).findAll( );
                                       If ( favouriteImagesModels.size() == 1) {
                                           favouriteImagePresent = true;
                                           imagesUnfavourited++;
                                       }
                                       favouriteImagesModels.deleteAllFromRealm();
                                   }
                               }
                   }

After the doInBackground( ) method has been executed the onPostExecute( ) comes into play and some other UI related changes are done such as a SnackBar message is shown if the image is removed from favourites.

Resources

  • Realm for Android

https://realm.io/blog/realm-for-android/

  • Asynchronous Transactions in Realm

https://realm.io/docs/java/latest/#working-with-realmobjects

 

Tags: GSoC18, FOSSASIA, Phimp.Me, Unfavourite image, Realm  

Continue Reading

Use PreferenceManager in place of SharedPreferences

SharedPreferences is used in android to store data in the form of a key-value pair in an application. But, sometimes we need to store data at many places in the application such as saving the login email or a particular information that remains the same for the entire use of the app. In SUSI.AI android app PrefManager.java class is made that uses the sharedpreferences in android and provides a custom wrapper that is used to store the data in the key-value pair form in the app.

To store data in sharedpreferences through the PreferenceManager class we just need to declare an instance of the PreferenceManager in the location in which we want to save the data. The SUSI.AI Android app opens up the login screen and on just viewing the welcome cards for the first time due to the incorrect implementation of the sharedpreferences, using the Preference Manager we can correctly implement the preferences in welcome activity.

Whenever we install the SUSI.AI Android app, on opening it for the first time we see welcome cards that give a basic overview of the app. So, now after swiping all the cards and on reaching the final card instead of clicking GOT IT to move to the login screen and on pressing the home button we put the app in the background. Now, what happened was when the app showed cards, it was due to the WelcomeActivity.java activity.

In the onCreate() method of this activity we check whether the WelcomeActivity is opened for the first time or not. In this case we make use of the PrefManager.java class.

if (PrefManager.getBoolean(“activity_executed”, false)) {
  Intent intent = new Intent(this, LoginActivity.class);
  startActivity(intent);
  finish();
} else {
  PrefManager.putBoolean(“activity_executed”, true);
}

This piece of code calls the getBoolean(<preferenceKey>,<preferenceDefaultValue>) of the PrefManager.java class and checks the boolean value against the preference key passed in the function.

If it is the first time when this function is called then the default value is accessed against the preference key: “activity_executed”, which is passed as false and no code within the if statement is executed and so the code in the else block is executed which puts the value true as the preference value against the key “activity_executed” and the next line is executed in the sequence after this line. In short, on opening the app for the first time the preference key “activity_executed” has a value of true against it.

When we close the app by destroying it while it is in the WelcomeActivity, the onDestroy() method is called and memory is cleared, but when we open the app again we expect to see the Welcome Cards, but instead we find that we are on the login screen, even though the GOT IT button on the final card was not pressed. This is because when we go through the onCreate() method again the if condition is true this time and it sends the user directly to the Login activity.

To, solve this problem the method that was followed was :

  1. Remove the else condition in the if condition where we check the value stored against the key “activity_executed” key.
  2. In the onCreate() method of the LoginActivity.kt file use the PrefManager class to put a boolean value equal to true against the key : “activity_executed”.So the code added in the LoginActivity.kt file’s onCreate() method was :
PrefManager.putBoolean(“activity_executed”, true)

Also, the else condition was removed in the Welcome activity. Now, with these changes in place unless we click GOT IT on the final card in the welcome activity we won’t be able to go to the Login Activity, and since we won’t be able to go to the final activity the value against the key “activity_executed” will be false. Also, once we move to the login activity we will not be able to see the cards as the if condition described above will be true and the intent will fire the user to the Login Activity forever.

Therefore, using the PrefManager class it became very easy to put values or get values stored in the sharedpreferences in android. The methods are created in the Preference manager for different types corresponding to the different return types present that can be used to store the values allowed by the SharedPreferences in Android.

Using the PrefManager class functions it was made easy in SUSI.AI android app to access the SharedPreferences and using it a flaw in the auth flow and opening of the app was resolved.

 

Resources

  1. How to launch an activity only once for the first time! – Divya Jain:  https://androidwithdivya.wordpress.com/2017/02/14/how-to-launch-an-activity-only-once-for-the-first-time/
  2. Save key-value data : ttps://developer.android.com/training/data-storage/shared-preferences
  3. PrefManager Class: https://github.com/fossasia/susi_android/blob/development/app/src/main/java/org/fossasia/susi/ai/helper/PrefManager.java
Continue Reading

Implementing Color Picker in the Open Event Orga App

In the Open Event Orga App, we have implemented a color picker to change the color of the Session’s item heading. Earlier the user had to enter the hex code in the given field to provide the color which is a bit cumbersome but now it is much easier with the help of a color picker. The PR related to this issue can be found here: #1073

The following approach was followed to implement the color picker.

Firstly we have used the library https://github.com/Pes8/android-material-color-picker-dialog. So it is included in the build.gradle file. The following line is added this file.

dependencies {
       implementation ‘com.pes.materialcolorpicker:library:1.2.0’
   }

Then navigate to the CreateTracksFragment.java class where we will need to implement the color picker. But firstly when the user enters the fragment , he should be able to see a random color in the edit text field and a small demo to the right of it where the selected color will be shown. These two things are done in the following steps.

1. To auto fill the color field with a random color the following code is written:

public String getRandomColor() {
  Random random = new Random();
  colorRed = random.nextInt(255);
  colorGreen = random.nextInt(255);
  colorBlue = random.nextInt(255);
  colorRGB = Color.rgb(colorRed, colorGreen, colorBlue);
  return String.format(“#%06X”,(0xFFFFFF & colorRGB));
}

public int getRed() {
  return colorRed;
}

public int getGreen() {
  return colorGreen;
}

public int getBlue() {
  return colorBlue;
}

public int getColorRGB() {
  return colorRGB;
}

 

With the help of the above code a random hex color is generated with the help of colorRed, colorGreen and colorBlue. These random fields are assigned to the local variables as they need to be used later on in the CreateTracksFragment.

This method is then called from the onStart( ) of the CreateTracksFragment.

2. Now a small demo color view is created to the right of the edit text field where the user can see the color of the current hex code. To implement this the following XML code is written. A small image view is created with the id color_picker.

<ImageView
  android:id=“@+id/color_picker”
  android:layout_width=“24dp”
  android:layout_height=“24dp”
  android:layout_margin=“10dp”
  android:layout_weight=“0.2” />

The above image view is filled with the current selected color with the help of the following code:

This ensures that whenever the user opens the CreateTracksFragment he should be able to view the color.

binding.form.colorPicker.setBackgroundColor(getPresenter().getColorRGB());

Now after implementing the above 2 features, the color picker dialog needs to be shown when the user clicks on the demo color view and the dialog box should show the value of Red, Green, Blue generated by the random method in the presenter. So these values are obtained from the presenter and added as constructor parameters as shown below. The color picker library has 2 main methods that need to be implemented which are :

→ setCallback( ) which is a callback method triggered when the user has selected the color and clicks on submit button. The action that needs to be performed after this is written in this particular callback method. In our case, we want to fill the edit text with the selected color and so we set the edit text with the callback value. Also we need fill the image view with the selected color and hence its color is also set in the callback function.

→ The show( ) method is required to make the dialog box of the color picker visible.

 

private void setColorPicker() {
  if (colorPickerDialog == null)
      colorPickerDialog = new ColorPicker(getActivity(), getPresenter().getRed(), getPresenter().getGreen(), getPresenter().getBlue());

  binding.form.colorPicker.setBackgroundColor(getPresenter().getColorRGB());

  binding.form.colorPicker.setOnClickListener(view -> {
      colorPickerDialog.show();
  });

  colorPickerDialog.setCallback(color -> {
      binding.form.trackColor.setText(String.format(“#%06X”, (0xFFFFFF & color)));
      binding.form.colorPicker.setBackgroundColor(color);
      colorPickerDialog.dismiss();
  });
}

 

→ When the colors have been selected we need to dismiss the dialog box. This is done by calling the .dismiss( )of the color picker.

Now after the CreateTracksFragment has been implemented certain changes need to be done to the UpdateTracksPresenter as well. Whenever the user selects to update the track, he should see the colors associated with that track as well which were set during its creation. We add the following to the UpdateTracksFragment.

 

public int getRed() {
  String colorRed = track.getColor();
  return Integer.valueOf(colorRed.substring(1, 3), 16);
}

public int getGreen() {
  String colorGreen = track.getColor();
  return Integer.valueOf(colorGreen.substring(3, 5), 16);
}

public int getBlue() {
  String colorBlue = track.getColor();
  return Integer.valueOf(colorBlue.substring(5, 7), 16);
}

public int getColorRGB() {
  return Color.rgb(getRed(), getGreen(), getBlue());
}

These methods are then eventually called from the UpdateTracksFragment and provided as parameters to the colorpicker similar to what had been implemented in the CreateTracksFragment.

private void setColorPicker() {
  if (colorPickerDialog == null)
      colorPickerDialog = new ColorPicker(getActivity(), getPresenter().getRed(), getPresenter().getGreen(), getPresenter().getBlue());

  binding.form.colorPicker.setBackgroundColor(getPresenter().getColorRGB());

  binding.form.colorPicker.setOnClickListener(view -> {
      colorPickerDialog.show();
  });

  colorPickerDialog.setCallback(color -> {
      binding.form.trackColor.setText(String.format(“#%06X”, (0xFFFFFF & color)));
      binding.form.colorPicker.setBackgroundColor(color);
      colorPickerDialog.dismiss();
  });

The final result can be seen in the form of GIF

 

Resources

1. Color Picker Library

https://github.com/Pes8/android-material-color-picker-dialog

2. Generating random hex values

https://stackoverflow.com/questions/11094823/java-how-to-generate-a-random-hexadecimal-value-within-specified-range-of-value

Continue Reading

Setup interactive charts for data representation

At the end of this blog, you would be able to setup interactive charts using HighCharts and D3.js. As the charts/data-visualisation models will form the backbone of the upcoming SUSI.AI Analytics dashboard, as well as the data representational model for various useful data. For the purpose of integration with the SUSI Skills CMS project, we will be using the react-highcharts and react-tagcloud library.

There are various kinds of charts and plots that HighCharts offers. They are –

  • Line charts
  • Area charts
  • Column and Bar Charts
  • Pie Charts
  • Scatter and Bubble Charts
  • Combinations
  • Dynamic Charts
  • Gauges
  • Heat and Tree maps
  • 3D charts

Check out this link for the full list.

We would be aiming to build up the above charts for analysis of Term Frequency Trends and Trending Clouds.

For the Term Frequency Trends, we will need to setup a Basic Line Graph and for the later,  we need a World Cloud.

Setting up a Basic Line Graph

The aim is to setup a basic line graph and to accomplish that we use a react library called react-highcharts, which makes our work very easier.

Firstly, we create an object config that contains the labels and the required data, with the key values as mentioned in the API reference. The object looks like this –

const config = {
  xAxis: {
    categories: ['01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20']
  },
  series: [{
    data: [750, 745, 756, 740, 760, 752, 765]
  }]
};

Secondly, we create a React Component and pass the config object as a property to the ReactHighcharts component.

Finally, we render the component in a div of the index.html file, and the following output is achieved.

The code for the component that renders the Chart is as follows:

import React from 'react';
import ReactHighcharts from 'react-highcharts';
import ReactDOM from 'react-dom';

const config = {
  xAxis: {
    categories: ['01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20']
  },
  series: [{
    data: [750, 745, 756, 740, 760, 752, 765]
  }]
};

ReactDOM.render(<ReactHighcharts  config={config} />, document.getElementById('app'));

Setting up a WordCloud

Here, we wish to setup a WordCloud that would show the different words that got searched or the top trending words. We would be using the react-tagcloud library for this.

Firstly, we create an object data that contains the text along with the count/frequency of search. The object looks like this –

const data = [
  { value: "JavaScript", count: 38 },
  { value: "React", count: 30 },
  { value: "Nodejs", count: 28 },
  { value: "Express.js", count: 25 },
  { value: "HTML5", count: 33 },
  { value: "MongoDB", count: 18 },
  { value: "CSS3", count: 20 }
];

Secondly, we create a React Component and pass the data object as a property to the TagCloud component.

Finally, we render the component in a div of the index.html file, and the following output is achieved.

The code for the component that renders the Chart is as follows:

import React from 'react';
import React, { Component } from 'react';
import { TagCloud } from "react-tagcloud";
 
const data = [
  { value: "JavaScript", count: 38 },
  { value: "React", count: 30 },
  { value: "Nodejs", count: 28 },
  { value: "Express.js", count: 25 },
  { value: "HTML5", count: 33 },
  { value: "MongoDB", count: 18 },
  { value: "CSS3", count: 20 }
];
 
const SimpleCloud = () => (
  <TagCloud 
       minSize={12}
       maxSize={35}
       tags={data}
       onClick={tag => alert(`'${tag.value}' was selected!`)} />
);

ReactDOM.render(<SimpleCloud />, document.getElementById('app'));

 

These were some examples of setting up some of the data-visualization models, that would form the basic building block of the SUSI Analytics project. I hope this blogs would be a good starting point for those wanting to start with setting up charts, graphs, etc.

Resources

Continue Reading

Implementing Filters in Phimp.me Android App

Image filters in phimp.me android app are implemented using the OpenCV library that enables the android developers to write code in C++/C which is compatible with java. I have implemented around eight filters in the filters.cpp file, mainly dealing with the colour variations that took place between the images.

Suppose that the color model used in consideration is the RGB (Red,Green,Blue) model and we define that every pixel is defined using these values. If we simply want to boost or focus on the red part of the image it can be done by enhancing the particular color in the source image i.e. modifying each pixel of the app by enhancing the blue color of each pixel. The code for the same can be written as :

void applyBlueBoostEffect(cv::Mat &src, cv::Mat &dst, int val) {
register int x, y;
float opacity = val * 0.01f;
cvtColor(src, src, CV_BGRA2BGR);
dst = Mat::zeros(src.size(), src.type());
uchar r, g, b,val1;

for (y = 0; y < src.rows; y++) {
for (x = 0; x < src.cols; x++) {
r = src.at<Vec3b>(y, x)[0];
g = src.at<Vec3b>(y, x)[1];
b = src.at<Vec3b>(y, x)[2];
val1 = saturate_cast<uchar>(b*(1+opacity));
if(val1 > 255) {
val1 = 255;
}
dst.at<Vec3b>(y, x)[0] =
saturate_cast<uchar>(r);
dst.at<Vec3b>(y, x)[1] =
saturate_cast<uchar>(g);
dst.at<Vec3b>(y, x)[2] =
saturate_cast<uchar>(val1);
}
}
}

In the above function what we are doing is taking the source image and converting it to a matrix of known number of rows and columns. Now, we loop over this matrix created and at each

position in the matrix calculate the red, green and blue values present there. There is a parameterized value “val” that determines the amount to which the color chosen is enhanced.

A simple variable val1 contains the enhanced value of the color chosen, here which is color blue. We modify and boost the color by the equation  :

B = B*(1 + (0.01f* val) )

Finally at this particular position in the matrix all the three values of the color are updated i.e. the original red and green color but the modified blue color.

The output of this filter converts images as below shown :

Before                                                                                   After

  

In the above method defined we instead of enhancing one color we can even modify two colors or all the three at the same time to get different effects as per the required needs. An example where I implemented a filter to enhance two colors at the same time is given below, the purpose of this filter is to add a violet color tone in the image.

The code for the filter is :

void applyRedBlueEffect(cv::Mat &src, cv::Mat &dst, int val) {
register int x, y;
float opacity = val * 0.01f;
cvtColor(src, src, CV_BGRA2BGR);
dst = Mat::zeros(src.size(), src.type());
uchar r, g, b;
uchar val1,val3;

for (y = 0; y < src.rows; y++) {
for (x = 0; x < src.cols; x++) {
r = src.at<Vec3b>(y, x)[0];
g = src.at<Vec3b>(y, x)[1];
b = src.at<Vec3b>(y, x)[2];
val1 = saturate_cast<uchar>(r*(1+opacity));
if(val1 > 255) {
val1 = 255;
}

val3 = saturate_cast<uchar>(b*(1+opacity));
if(val3 > 255) {
val3 = 255;
}
dst.at<Vec3b>(y, x)[0] =
saturate_cast<uchar>(val1);
dst.at<Vec3b>(y, x)[1] =
saturate_cast<uchar>(g);
dst.at<Vec3b>(y, x)[2] =
saturate_cast<uchar>(val3);
}
}
}

 

Here we can easily see that there are two values that are updated at the same time i.e. the red and the blue part. The resultant image in that case will have two color values updated and enhanced.

Similarly, instead of two we can also update all the three colors to get uniform enhancement across each color and the resultant image to have all the three colors with a boost effect overall.

The output that we get after the filter that boosts the red and blue colors of an image is  :

 BEFORE                                                                 

AFTER

Resources

Continue Reading

Making a SUSI Skill to get details about bank from IFSC

We are going to make a SUSI skill that fetches information about a bank when the IFSC (Indian Financial System Code) is known. Here is a detailed explanation of how we going about doing this.

Getting started with the skill creation

API endpoint that returns the bank details

Before going to the skill development, we need to find an API that would return the bank details from the IFSC, On browsing through various open source projects. I found an apt endpoint by Razorpay. Razorpay is a payment gateway for India which allows businesses to accept, process and disburse payments with ease. The Github link  to the repository is https://github.com/razorpay/ifsc.

API endpoint –  https://ifsc.razorpay.com/<:ifsc>
Request type –  GET
Response type –  JSON

Now, head over to the SUSI Etherpad, which is the current SUSI Skill Development Environment and create a new Pad. 

Here, we need to define the skill in the Etherpad. We will now write rules/intents for the skill. An intent represents an action that fulfills a user’s spoken request.

Intents consist of 2 parts –

  • User query – It contains different patterns of query that user can ask.
  • Answer – It contains the possible answer to the user query.

The main intent that our skill focuses on is, returning the bank name and address from the IFSC code. Here is how it looks –

Name of bank with IFSC code * | Bank's name with IFSC code *
!example:Name of bank with IFSC code SBIN0007245
!expect: The name of bank is State Bank of India
!console:The name of bank with IFSC code $1$ is $object$
{
"url":"https://ifsc.razorpay.com/$1$",
"path":"$.BANK"
}
eol

Part-wise explanation of the intent

  • The first line contains the query pattern that the user can use while querying. You can see that a wildcard character (*) is used in the pattern. It contains the IFSC of the bank that we wish to know, and will later on use to fetch the details via the API.
  • The second line contains an example query, followed by third line that contains the expected answer.
  • Last part of the rule contains the answer that is fetched from an external API –  https://ifsc.razorpay.com/<:ifsc>  ,via the console service  provided by SUSI Skills. Here, <:ifsc> refers to the IFSC that the user wants to know about. We get it from the user query itself, and can access it by the variable name $1$ as it matches with the 1st wildcard present in the query. If there would be 2 wildcards, we could have accessed them by $1$ and $2$ respectively.
  • The console service provides us with an option to enter the url of the API that we want to hit and path of the key we want to use.

The sample response of the endpoint looks like this :

{
  "BANK": "Karnataka Bank",
  "IFSC": "KARB0000001",
  "BRANCH": "RTGS-HO",
  "ADDRESS": "REGD. & HEAD OFFICE, P.B.NO.599, MAHAVEER CIRCLE, KANKANADY, MANGALORE - 575002",
  "CONTACT": "2228222",
  "CITY": "DAKSHINA KANNADA",
  "RTGS": true,
  "DISTRICT": "MANGALORE",
  "STATE": "KARNATAKA"
}

 

  • Since, we want to extract the name of the bank, the BANK key contains our desired value and we will use $.BANK in the path of the console service. And it can be accessed by $object$ in the answer. We frame the answer using $object$ and $1$ variables, and it like the one mentioned in the expected answer. eol marks the end of the console service.
  • Similarly, the intent that gives us the address of the bank looks like this –
Address of bank with IFSC code * | Bank's address with IFSC code *
!example:Address of bank with IFSC code SBIN0007245
!expect: The address of bank is TILAK ROAD HAKIMPARA, P.O.SILIGURI DARJEELING, WEST BENGAL ,PIN - 734401
!console:The address of bank with IFSC code $1$ is $object$
{
  "url":"https://ifsc.razorpay.com/$1$",
  "path":"$.BANK"
}
eol

Testing the skill

  • Open any SUSI Client and then write dream <your dream name> so that dreaming is enabled for SUSI. We will write down dream ifsc. Once dreaming is enabled, you can now test any skills which you’ve made in your Etherpad.
  • We can test the skills by asking queries and matching it with the expected answer. Once the testing is done, write stop dreaming to disable dreaming for SUSI.

  • After the testing was successful completely, we will go ahead and add it to the susi_skill_data.
  • The general skill format is –
::name <Skill_name>
::author <author_name>
::author_url <author_url>
::description <description> 
::dynamic_content <Yes/No>
::developer_privacy_policy <link>
::image <image_url>
::term_of_use <link>

#Intent
User query1|query2|query3....
Answer answer1|answer2|answer3...

We will add the basic skill details and author details to the etherpad file and make it in the format as mentioned above. The final text file looks like this –

::name IFSC to Bank Details
::author Akshat Garg
::author_url https://github.com/akshatnitd
::description It is a bank lookup skill that takes in IFSC code from the user and provides you all the necessary details for the Bank. It is valid for banks in India only
::dynamic_content Yes
::developer_privacy_policy 
::image images/download.jpeg
::terms_of_use 

Name of bank with IFSC code * | Bank's name with IFSC code *
!example:bank with IFSC code *
!expect: The name of bank is SBI
!console:The name of bank with IFSC code $1$ is $object$
{
"url":"https://ifsc.razorpay.com/$1$",
"path":"$.BANK"
}
eol

Address of bank with IFSC code * | Bank's address with IFSC code *
!example:Address of bank with IFSC code *
!expect: The address of bank is 
!console:The address of bank with IFSC code $1$ is $object$
{
"url":"https://ifsc.razorpay.com/$1$",
"path":"$.ADDRESS"
}
eol

Submitting the skill

The final part is adding the skill to the list of skills for SUSI. We can do it by 2 ways:

1st method (using the web interface)

  • Open https://susi.skills.com and login into SUSI account (or sign up, if not done).
  • Click on the create skill button.
  • Select the appropriate fields like Category, Language, Skill name, Logo.
  • Paste the text file that we had created.
  • Add comments regarding the skill and click on Save to save the skill.

2nd method (sending a PR)

  • Send a Pull Request to susi_skill_data repository providing the dream name. The PR should have the text file containing the skill.

So, this was a short blog on how we can develop a SUSI skill of our choice.

Resources

Continue Reading

Option to secure particular albums in Phimpme Android Application

In the Phimpme Android application, users can perform various operations on the albums available such as creating a zip file of the album, rename an album and many more. However, one another useful functionality that has been added to the Phimpme Android application is the option to secure particular albums. So in this post, I will be discussing the implementation of this security feature.

Step 1

Firstly, a view item for providing the option to enable security for particular albums is required to be added to the security settings layout. The two-state toggle switch widget provided by the Android framework along with a textview has been added as the required view item. A screenshot depicting the layout change is provided below.

The code snippet representing the operation is provided below.

<TextView
  android:id=“@+id/security_body_apply_folders_title”
  android:layout_width=“wrap_content”
  android:layout_height=“wrap_content”
  android:text=“@string/local_folder”
  android:textColor=“@color/md_dark_background”
  android:textSize=“@dimen/medium_text” />

<android.support.v7.widget.SwitchCompat
  android:id=“@+id/security_body_apply_folder_switch”
  android:layout_width=“wrap_content”
  android:layout_height=“wrap_content”
  android:layout_alignParentEnd=“true”
  android:layout_centerVertical=“true”
  android:layout_gravity=“center_vertical”
  android:button=“@null”
  android:hapticFeedbackEnabled=“true” />

Step 2

Now we need to keep track of the albums selected by the user to secure. This can be done by storing the selected album/albums paths in an ArrayList<String> which can be referred later when required in the process.

The required code snippet to perform the above mentioned operation is provided below.

holder.foldercheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b){
securedfol.add(a.getPath());
a.setsecured(true);
}else{
securedfol.remove(a.getPath());
a.setsecured(false);
}
}
});

Step 3

Now we need to store the selected albums preference in the SharedPreference so that the user’s security preference persists even when the user exits the application and the user doesn’t have to redo the securing operation the next time user launches the application. The ArrayList<String> object containing the path of the user choice albums are converted to JSON representation by the use of the Gson Java library and the string key denoting the JSON representation of the list is saved in the SharedPreference thereafter.

if(securedfol.size()>0){
  SharedPreferences.Editor editor = SP.getEditor();
  Gson gson = new Gson();
  String securedfolders = gson.toJson(securedfol);
  editor.putString(getString(R.string.preference_use_password_secured_local_folders), securedfolders);
  editor.commit();}

Now at the time of performing other operations on the secured folders, the list containing the secured folder paths is retrieved from SharedPreference and the choosen folder’s path is searched in the obtained list, then the user is asked to authenticate accordingly.

This is how we have implemented the functionality to secure particular albums in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

Resources

1.Android Developer Guide –
https://developer.android.com/training/data-storage/shared-preferences

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Gson Java library tutorial –
http://www.vogella.com/tutorials/JavaLibrary-Gson/article.html

Continue Reading

Implementing Search Functionality in Phimpme Android Application.

In the Phimpme Android application, users are provided with options to perform various operations on the albums available such as move, creating a zip file of the album, rename an album and many more. However, one another useful functionality that has been added to the Phimpme Android application is the option to search albums. So in this post, I will be discussing the implementation of search functionality.

Step 1

Android framework provides developers with a search widget called SearchView that provides a user interface for the user to search a query and submit the request. So first setting up the widget in the action bar of the activity is required. The searchview widget can be added to the action bar as a menu item by adding the following lines in the XML menu resource file menu_albums.xml.

<item
  android:id=“@+id/search_action”
  android:title=“@string/search_menu”
  android:visible=“true”
  android:icon=“@drawable/ic_search_black_24dp”
  android:tint=“@color/white”
  app:actionViewClass=“android.support.v7.widget.SearchView”
  app:showAsAction=“always” />

Step 2

Now SearchView.OnQueryTextListener interface is used for initiating the search operation and listening to the callbacks for changes to the query text. For the purpose of listening to the querytext, two methods are used here both of which are listed below.

onQueryTextChanged(String Text) – This method is called every time the query text is changed by the user and returns a boolean value, false if SearchView should perform the default action of showing any suggestions and true if the action was handled by the listener.

onQueryTextSubmit(String query) – This method is called when the user submits a query which could be done with a key press on the keyboard or by pressing the submit button. It also returns a boolean value which is true if the query has been handled by the listener, otherwise false.

The code snippet for the implementation of the above mentioned interface is provided below.

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
  @Override
  public boolean onQueryTextSubmit(String query) {
      return false;
  }

  @Override
  public boolean onQueryTextChange(String newText) {
      return searchTitle(newText);
  }
});

Step 3

In the final step, with the use of the onQueryTextChange method of the SearchView.onQueryTextListener interface the search operation and displaying the search results in the UI can be achieved. The onQueryTextChange method is called every time the search-query text changes. From the onQueryTextChange method, another method named searchTitle is invoked. Inside the searchTitle method the album names matching the search-query are searched from an Arraylist<Albums> containing all the albums displayed in the application. The albums obtained as a result of the search operation are then stored in another Arraylist<Album> which is thereafter passed as a parameter to the swapDataSet method of the AlbumsAdapter class to display the searched albums in the album view. The code snippet used for the above operations is provided below.

public boolean searchTitle(String newText) {
  if (!fromOnClick) {
      String queryText = newText;
      queryText = queryText.toLowerCase();
      final ArrayList<Album> newList = new ArrayList<>();
      for (Album album : albList) {
          String name = album.getName().toLowerCase();
          if (name.contains(queryText)) {
              newList.add(album);
          }
      }
      if(newList.isEmpty()){
          checkNoSearchResults(newText);
      }
      else{
          if(textView.getVisibility() == View.VISIBLE){
              textView.setVisibility(View.INVISIBLE);
          }
      }
      albumsAdapter.swapDataSet(newList);
  } else {
      fromOnClick = false;
  }
  return true;
}

This is how we have implemented the search functionality in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository.

The screenshot for displaying the search result in album view is provided below.

Resources

1.Android Developer Guide – https://developer.android.com/reference/android/widget/SearchView

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Creating a search interface in Android – https://medium.com/@yugandhardcs21/creating-a-search-interface-in-android-dc1fd6a53b4

Continue Reading
Close Menu