Using react-slick for Populating RSS Feeds in SUSI Chat

To populate SUSI RSS Feed generated, while chatting on SUSI Web Chat, I needed a Horizontal Swipeable Tile Slider. For this purpose, I made use of the package react-slick. The information which was supposed to be handled as obtained from the SUSI Server to populate the RSS feed was

  • Title
  • Description
  • Link

Hence to show all of this information like a horizontal scrollable feed, tiles by react-slick solves the purpose. To achieve the same, let’s see follow the steps below.

  1. First step is to install the react-slick package into our project folder, for that we use
npm install react-slick --save
  1. Next we import the Slider component from react-slick package into the file where we want the slider, here MessageListItem.react.js
import Slider from 'react-slick'
  1. Add Slider with settings as given in the docs. This is totally customisable. For more customisable options go to https://github.com/akiran/react-slick
var settings = {
         speed: 500,
         slidesToShow: 3,
         slidesToScroll: 1,
        swipeToSlide:true,
         swipe:true,
         arrows:false
     };

speed – The Slider will scroll horizontally with this speed.

slidesToShow – The number of slides to populate in one visible screen

swipeToSlide, swipe – Enable swiping on touch screen devices.

arrows – Put false, to disable arrows

  1. The next step is to initialize the Slider component inside the render function and populate it with the tiles. The full code snippet is available at MessageListItem.react.js
<Slider {..settings}>//Append the settings which you created
    {yourListToProps} // Add the list tiles you want to see
</Slider>
  1. Adding a little bit of styling, full code available in ChatApp.css
 .slick-slide{
 margin: 0 10px;
}
.slick-list{
  max-height: 100px;
}
  1. This is the output you would get in your screen.

  • Note – To prevent errors like the following on testing with jest, you will have to add the following lines into the code.

Error log, which one may encounter while using react-slick –

 matchMedia not present, legacy browsers require a polyfill

  at Object.MediaQueryDispatch (node_modules/enquire.js/dist/enquire.js:226:19)
  at node_modules/enquire.js/dist/enquire.js:291:9
  at i (node_modules/enquire.js/dist/enquire.js:11:20)
  at Object.<anonymous> (node_modules/enquire.js/dist/enquire.js:21:2)
  at Object.<anonymous> (node_modules/react-responsive-mixin/index.js:2:28)
  at Object.<anonymous> (node_modules/react-slick/lib/slider.js:19:29)
  at Object.<anonymous> (node_modules/react-slick/lib/index.js:3:18)
  at Object.<anonymous> (src/components/Testimonials.jsx:3:45)
  at Object.<anonymous> (src/pages/Index.jsx:7:47)
  at Object.<anonymous> (src/App.jsx:8:40)
  at Object.<anonymous> (src/App.test.jsx:3:38)
  at process._tickCallback (internal/process/next_tick.js:103:7)

In package.json, add the following lines-

"peerDependencies": {
      "react": "^0.14.0 || ^15.0.1",
      "react-dom": "^0.14.0 || ^15.0.1"
    },
   "jest": {
      "setupFiles": ["./src/setupTests.js", "./src/node_modules/react-scripts/config/polyfills.js"]
   },

In src/setupTests.js, add the following lines.

window.matchMedia = window.matchMedia || (() => { return { matches: false, addListener: () => {}, removeListener: () => {}, }; });

These lines will help resolve any occurring errors while testing with Jest or ESLint.

To have a look at the full project, visit https://github.com/fossasia/chat.susi.ai and feel free to contribute. To test the project visit http://chat.susi.ai

Resources

 

Continue ReadingUsing react-slick for Populating RSS Feeds in SUSI Chat

Implementing Login Functionality in SUSI Web Chat

SUSI Web Chat is fully equipped with all the accounting features which are being provided by the SUSI.AI API. This blog discloses all the API features one needs to know to embed the Login functionality in SUSI Web Chat.

  1. To embed the Login feature, first we create a form using material-ui.com components with the followng fields
    1. Email
    2. Password
    3. Note: We can also chose a Custom Server while logging in, here I have used the Standard Server ie. http://api.susi.ai to make the user Login

The form can be made with the help of the following fields

  • TextField for Email, props to be passed
    • Name – email
    • Value – this.state.email which gets the value of the current email
    • floatingLabelText is Email,
    • errorText is the message which we want to show when the email does not match the regex or its empty.

Code Snippet –

<TextField name="email" value={this.state.email} onChange={this.handleChange} errorText={this.emailErrorMessage}    floatingLabelText="Email" />
  • PasswordField for Password
    • Name – password
    • Value – this.state.password which gets the value of the current email
    • floatingLabelText is Password,
    • errorText is the message which we want to show when the password is not filled.

Code Snippet-

<PasswordField name='password' value={this.state.password} onChange={this.handleChange} errorText={this.passwordErrorMessage}   floatingLabelText='Password' />
  • The next elements are RadioButton groups taken from material-ui.com. This ensures the user signs into a standard server or even to a custom server. This is not compulsory as of now.
  • And lastly we need a submit button, which is disabled until all the fields are filled.

Code Snippet –

<RaisedButton label="Login" type="submit" labelColor="#fff" disable={!this.state.validForm} />

For the full form, check out this file at Login.react.js

  1. A Sample UI could be as shown in the image
  2. Next after creating the Login Screen, we make the onSubmit prop which is to be hooked up with another function called handleSubmit. An example code snippet from Login.react.js
 handleSubmit = (e) => {
        e.preventDefault();
        // Get the trimmed values from the fields
        var email = this.state.email.trim();
        var password = this.state.password.trim();
        // Set the default server to login
        let BASE_URL = defaults.Server;
            // handle all the details of the chosen server
        let serverUrl = this.state.serverUrl;
        if(serverUrl.slice(-1) === '/'){
            serverUrl = serverUrl.slice(0,-1);
        }
        if(serverUrl !== ''){
            BASE_URL = serverUrl;
        }
// if email and password is filled return true
        if (!email || !password) { return this.state.isFilled; }
// Check the regex of email
        let validEmail = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email); 
// Pass the parameters to the loginEndPoint
        let loginEndPoint =
            BASE_URL+'/aaa/login.json?type=access-token&login=' +
            this.state.email + '&password=' + this.state.password;
        // If email and password is filled and valid call AJAX
        if (email && validEmail) {
            // AJAX Calls
        }
    }
    1. Then we make the Ajax Calls and store the created token from hitting the URL at http://api.susi.ai/aaa/login.json?type=access-token&login=EMAIL&password=PASSWORD. We store the cookie in browser and generate a session for the user using a package ‘universal-cookies’.
$.ajax({
    url: loginEndPoint,
    dataType: 'jsonp',
    jsonpCallback: 'p',
    jsonp: 'callback',
    crossDomain: true,
    success: function (response) {
        cookies.set('serverUrl', BASE_URL, { path: '/' });
        let accessToken = response.access_token;
        let state = this.state;// Adding the current State
        let time = response.valid_seconds; // Get the valid time of the cookie
        state.isFilled = true; // Set isFilled to true
        state.accessToken = accessToken; // Get the token
        state.success = true; // Set Success to true
        state.msg = response.message; // Get the server message
        state.time = time; // Get the time in the state
        this.setState(state); // Set the  state with the values
/* Pass the token to the binding function handleOnSubmit passing the arguments - token and the valid time */
        this.handleOnSubmit(accessToken, time);
    }.bind(this),
    error: function (errorThrown) {
        let msg = 'Login Failed. Try Again';
        let state = this.state;
        state.msg = msg;
        this.setState(state);
    }.bind(this)
});

 

    1. We then fire up the handleOnSubmit(accessToken, time) which saves the token for the given expiration time from the server.

Here’s the sample code

handleOnSubmit = (loggedIn, time) => {
        let state = this.state;
        if (state.success) {
            cookies.set('loggedIn', loggedIn, { path: '/', maxAge: time }); // set the cookie in the browser to maintain the loggedIn state
            this.props.history.push('/', { showLogin: false });
            window.location.reload();// reload after the loggedIn cookie creation
        }
        else {
            this.setState({
                error: true,
                accessToken: '',
                success: false
            });
        }
    }
  1. We then check the access token and redirect him based on his login state. This is handled in MessageSection.react.js
import Cookies from 'universal-cookie';
const cookies = new Cookies();
if (cookies.get('loggedIn')) {
    //Show all functionalities of loggedIn state
}
else {
//Redirect user to login page
}

 

To have a look at the full project, visit https://github.com/fossasia/chat.susi.ai and feel free to contribute. To test the project visit http://chat.susi.ai

Resources

Continue ReadingImplementing Login Functionality in SUSI Web Chat

Retrieving & Creating Events in Open Event Front-end Using Ember Data

Open Event Front-end uses ember data which is a data persistence library for Ember.js. It uses promises to manage loading and saving records using JSON API format. Ember data uses models to which represent the API response to handle all the requests to be made to the server. Lets see how do we retrieve events & create new events using the Orga Server.

Installing ember data

npm install ember-data

Creating a model

We need a model in ember data which is a class containing all the attributes in the API response. We can create a model using ember cli command:

ember g model event

Populating the model

We must add all the attributes from the API response in the newly generated event model. Ember data automatically takes care of the ajax calls with high level of abstraction using promises. 

identifier             : attr('string'),
name                   : attr('string'),
description            : attr('string'),
startTime              : attr('date', { defaultValue: () => moment().add(1, 'months').startOf('day').toDate() }),
endTime                : attr('date', { defaultValue: () => moment().add(1, 'months').hour(17).minute(0).toDate() }),
locationName           : attr('string'),
searchableLocationName : attr('string'),

thumbnail       : attr('string'),
large           : attr('string'),
backgroundImage : attr('string'),
placeholderUrl  : attr('string'),

longitude : attr('number'),
latitude  : attr('number'),

type     : attr('string'),
topic    : attr('string'),
subTopic : attr('string'),

schedulePublishedOn: attr('date'),

organizerName        : attr('string'),
organizerDescription : attr('string'),
email                : attr('string'),

eventUrl      : attr('string'),
ticketUrl     : attr('string'),
codeOfConduct : attr('string'),

state   : attr('string'),
privacy : attr('string'),

licenceDetails : attr(),
copyright      : attr(),

callForPapers : fragment('call-for-speakers'),
version       : fragment('version'),
socialLinks   : fragmentArray('social-link')

We add all the attributes in the Orga server API response to the event model class.

Once we create the model containing all the attributes we are all set to make API requests in our application. We will now see how to make GET & POST requests in Open Event Front-end project.

GET requests

Ember data lets us retrieve records of a single type using store. The records to be fetched must have respective model in the application. We can make a GET request using

this.store.findAll(‘event’)

This will return a model which contains all the events from the Orga Server, it also contains the events from past as well as the events which are unpublished.

this.store.query('event', {
  include : 'event_topic,event_sub_topic,event_type',
  filter  : [
    {
      name : 'starts_at',
      op   : 'ge',
      val  : moment().toISOString()
    },
    {
      name : 'state',
      op   : 'eq',
      val  : 'Published'
    }
  ]
});

 

To get all the published events we use the query method which lets us add query parameters to the request. We will filter the events on the basis of the state and the end time of the event.

POST requests

Ember data lets us create & update records using the createRecord method of the store. We can create an event using
event = this.store.createRecord(‘event’)

This will create a record locally in the application to send a POST request we use the save() method which send the request to the server.

event.set(name, Event Test);
event.save();


Thank you for reading the blog, you can check the source code for the example
here.
We will discuss in detail how to implement API in Open Event Front-end in the next post.

Resources

Continue ReadingRetrieving & Creating Events in Open Event Front-end Using Ember Data

Multiple dynamic sub routes in Open Event Front-end

Dynamic routing is a section of the path for a route which changes based on the content of a page. Instead of having multiple routes for similar pages we have one route that changes state accordingly. Dynamic routing is extensively used in the Open Event Front-end application, for tables, events & other pages.

Sometimes we might need to have multiple dynamic routes for a particular route in this case we need to do implement a custom route redirection. Let’s see how did we implement it in the events route?

Dynamic routes in events route

The events route has two dynamic subroutes view & list. By default the dynamic route which is declared at the last will be used by ember which is list route in this case. This leads to loss of the view route which uses the ID of the event to display the event details.

this.route('events', function() {
  this.route('view', { path: '/:event_id' }, function() {
  });
  this.route('list', { path: '/:event_state' });
  this.route('import');
});

Here event_id is the ID of an event from the API response, event_state is the state of the list of events created by the user. The event_id is an alphanumeric value which is dynamic in nature and event_state is a string value which is a static value. Due to non overlapping states we can implement a simple procedure that enables us to create multiple dynamic routes.

Implementing multiple dynamic routing in ‘events’ route

As both the subroutes use different states i.e event_id & event_state, we can use this to redirect the routes accordingly. We will add a beforeModel() function in the events/list route and check if the eventState is not equal to live, draft or past which are the states of list route.

beforeModel(transition) {
   const eventState = transition.params[transition.targetName].event_state;
  if (!['live', 'draft', 'past'].includes(eventState)) {
    this.replaceWith('events.view', eventState);
  }
}

If the ‘eventState’ is not in the array we redirect the page to the events.view route using the replaceWith() function. This will ensure that the events.view route is not lost & the event details are rendered properly.

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

Resources

Continue ReadingMultiple dynamic sub routes in Open Event Front-end

Implementing Email Preferences in Open Event Front-end

Open Event Front-end lets users customise the email preferences for the notifications for the events created by the user. The user can toggle the notifications for new paper is submitted, change in schedule of sessions,  and reminder for event. In this post we will implement a functional UI for the email preferences in the application. How did we do it?

Creating email-preferences component

We create a component for the email-preferences component using ember-cli command:

ember g component settings/email-preferences-section


Mock response from server

Each event has notification preferences for three services new papers, change in schedule & new event which are stored as a boolean in the server. Each response model contains all the flags for the email preferences.


Implementing UI for email preferences

We use ui-accordion for each event created by the user. The ui-accordion lets us toggle the display for content section, similar to jquery’s collapse-in class. You can find more about semantic ui’s ui-accordion here.Implementing UI for email-preferences-section

model() {
 return [{
   name             : Techtoma,
   role             : Organiser,
   isNewPaper       : true,
   isChangeSchedule : true,
   isNewEvent       : true
 }];

Each preference can be toggled using ui-checkbox from semantic ui. When a checkbox is toggled the respective flag in the model.

<div class="row">
  <div class="column eight wide">
    {{t 'New Paper is Submitted to your Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isNewPaper onChange=(action (mut event.isNewPaper))}}
  </div>
</div>
<div class="row">
  <div class="column eight wide">
    {{t 'Change in Schedule of Sessions in your Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isChangeSchedule onChange=(action (mut event.isChangeSchedule))}}
  </div>
</div>
<div class="row">
  <div class="column eight wide">
    {{t 'Reminder for Next Event'}}
  </div>
  <div class="ui column eight wide right aligned">
    {{ui-checkbox class='toggle' checked=event.isNewEvent onChange=(action (mut event.isNewEvent))}}
  </div>
</div>

If any of the preference is not set i.e false, we toggle the status of the preferences to off. We use math helper or operation for setting the status of the preferences for each preference. We also toggle between the colour of the button between green and yellow, depending on the status of preferences setting.

<a class="ui circular label {{if (or event.isNewPaper event.isChangeSchedule event.isNewEvent) 'green' 'yellow'}}">
  {{#if (or event.isNewPaper event.isChangeSchedule event.isNewEvent)}}
    {{t 'On'}}
  {{else}}
    {{t 'Off'}}
  {{/if}}
</a> 

This results to the following changes on the Open Event Front-end:

The source code for the example is available here.

Resources

Continue ReadingImplementing Email Preferences in Open Event Front-end

Image Cropper On Ember JS Open Event Frontend

In Open Event Front-end, we have a profile page for every user who is signed in where they can edit their personal details and profile picture. To provide better control over profile editing to the user, we need an image cropper which allows the user to crop the image before uploading it as their profile picture. For this purpose, we are using a plugin called Croppie. Let us see how we configure Croppie in the Ember JS front-end to serve our purpose.

All the configuration related to Croppie lies in a model called cropp-model.js.

 onVisible() {
    this.$('.content').css('height', '300px');
    this.$('img').croppie({
      customClass : 'croppie',
      viewport    : {
        width  : 400,
        height : 200,
        type   : 'square'
      },
      boundary: {
        width  : 600,
        height : 300
      }
    });
  },

  onHide() {
    this.$('img').croppie('destroy');
    const $img = this.$('img');
    if ($img.parent().is('div.croppie')) {
      $img.unwrap();
    }
  },

  actions: {
    resetImage() {
      this.onHide();
      this.onVisible();
    },
    cropImage() {
      this.$('img').croppie('result', 'base64', 'original', 'jpeg').then(result => {
        if (this.get('onImageCrop')) {
          this.onImageCrop(result);
        }
      });
    }

There are two functions: onVisible() and onHide(), which are called every time when we hit reset button in our image cropper model.

  • When a user pushes reset button, the onHide() function fires first which basically destroys a croppie instance and removes it from the DOM.
  • onVisible(), which fires next, sets the height of the content div. This content div contains our viewport and zoom control. We also add a customClass of croppie to the container in case we are required to add some custom styling. Next, we set the dimensions and the type of viewport which should be equal to the dimensions of the cropped image. We define type of cropper as ‘square’ (available choices are ‘square’ and ‘circle’). We set the dimensions of our boundary. The interesting thing to notice here is that we are setting only the height of the boundary because if we pass only the height of the boundary, the width will be will be calculated using the viewport aspect ratio. So it will fit in all the screen sizes without overflowing.

The above two functions are invoked when we hit the reset button. When the user is satisfied with the image and hits ‘looks good’ button, cropImage() function is called where we are get the resulting image by passing some custom options provided by croppie like base64 bit encoding and size of cropped image which we are set to ‘original’ here and the extension of image which is we set here as ‘.jpeg’. This function returns the image of desired format which we use to set profile image.

Resources

Continue ReadingImage Cropper On Ember JS Open Event Frontend

Enhancing Images using Native functions in Phimpme Android

Enhancing the image can be performed by adjusting the brightness, contrast, saturation etc. of that image. In the Phimpme Android Image Application, we implemented many enhancement operations. All these image enhancement operations are performed by the native image processing functions.

An image is made up of color channels. A gray-scale image has a single channel, colored opaque image has three channels and colored image with transparency has four channels. Each color channel of an image represents a two dimensional matrix of integer values. An image of resolution 1920×1080 has 1920 elements in its row and 1080 such rows. The integer values present in the matrices will be ranging from 0 to 255. For a grayscale image there will be a single channel. So, for that image, 0 corresponds to black color and 255 corresponds to white color. By changing the value present in the matrices, the image can be modified.

The implementation of the enhancement functions in Phimpme Application are given below.

Brightness

Brightness adjustment is the easiest of the image processing functions in Phimpme. Brightness can be adjusted by increasing or decreasing the values of all elements in all color channel matrices. Its implementation is given below.

void tuneBrightness(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  signed char bright = (signed char)(((float)(val-50)/100)*127);
  for (i = length; i--; ) {
       red[i] =  truncate(red[i]+bright);
       green[i] = truncate(green[i]+bright);
       blue[i] = truncate(blue[i]+bright);
  }
}

  

low brightness, normal, high brightness(in the order) images are shown above

For the above function, the argument val is given by the seekbar implemented in java activity. Its value ranges from 0 – 100, so a new variable is introduced to change the range of the input argument in the function. You can see that in the for loop there is function named truncate. As the name suggests it truncates the input argument’s value to accepted range. It is added to the top of the c file as below

#define truncate(x) ((x > 255) ? 255 : (x < 0) ? 0 : x)

Contrast

Contrast of an image is adjusted in Phimpme application by increasing the brightness of the brighter pixel and decreasing value of the darker pixel. This is achieved by using the following formula for the adjustment contrast in editor of phimpme application.

pixel[i] = {(259 x (C + 255))/(255 x (259 - C))} x (pixel[i] - 128)

In the above formula, C is the contrast value and pixel[i] is the value of the element in the image matrix that we are modifying for changing the contrast.

 

low contrast, normal, high contrast(in the order) images are shown above

So, after this formula for modifying every pixel value, the function looks like below

void tuneContrast(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int contrast = (int)(((float)(val-50)/100)*255);
  float factor = (float)(259*(contrast + 255))/(255*(259-contrast));

  for (i = length; i--; ) {
       red[i] = truncate((int)(factor*(red[i]-128))+128);
       green[i] = truncate((int)(factor*(green[i]-128))+128);
       blue[i] = truncate((int)(factor*(blue[i]-128))+128);
  }
}

Hue

The below image explains hue shift by showing what happens when shift in hue takes place over time. The image with hue 0 looks identical with image with hue 360. Hue shift is cyclic. The definition and formulae corresponding hue is found in wikipedia page here. Using that formulae and converting them back, i.e we got rgb values from hue in Phimpme application. Its implementation is shown below.

[img source:wikipedia]

void tuneHue(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  double H = 3.6*val;
  double h_cos = cos(H*PI/180);
  double h_sin = sin(H*PI/180);
  double r,g,b;

  for (i = length; i--; ) {
       r = (double)red[i]/255;
       g = (double)green[i]/255;
       b = (double)blue[i]/255;
       red[i] = truncate((int)(255*((.299+.701*h_cos+.168*h_sin)*r +  (.587-.587*h_cos+.330*h_sin)*g + (.114-.114*h_cos-.497*h_sin)*b)));

       green[i] = truncate((int)(255*((.299-.299*h_cos-.328*h_sin)*r + (.587+.413*h_cos+.035*h_sin)*g + (.114-.114*h_cos+.292*h_sin)*b)));

       blue[i] = truncate((int)(255*((.299-.3*h_cos+1.25*h_sin)*r +  (.587-.588*h_cos-1.05*h_sin)*g + (.114+.886*h_cos-.203*h_sin)*b)));
  }
}

Saturation

Saturation is the colorfulness of the image. You can see the below null saturation, unmodified and high saturated images in the respective order. The technical definition and formulae for getting the saturation value from the rgb value is given in the wikipedia page here. In Phimpme application we used those formulae to get the rgb values from the saturation value.

Its implementation is given below.

  

low saturation, normal, high saturation(in the order) images are shown above

void tuneSaturation(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green
  unsigned char* blue = (*bitmap).blue;
  double sat = 2*((double)val/100);
  double temp;
  double r_val = 0.299, g_val = 0.587, b_val = 0.114;
  double r,g,b;
  for (i = length; i--; ) {
      r = (double)red[i]/255;
      g = (double)green[i]/255;
      b = (double)blue[i]/255;
      temp = sqrt( r * r * r_val +
                     g * g * g_val +
                       b * b * b_val );
      red[i] = truncate((int)(255*(temp + (r - temp) * sat)));
      green[i] = truncate((int)(255*(temp + (g - temp) * sat)));
      blue[i] = truncate((int)(255*(temp + (b - temp) * sat)));
  }
}

Temperature

If the color temperature of the image is high, i.e the image with the warm temperature will be having more reds and less blues. For a cool temperature image reds are less and blues are more. So In Phimpme Application, we implemented this simply by adjusting the brightness of the red channel matrix and blue channel matrix as we did in brightness adjustment. We didn’t modify the green channel here.

  

low temperature, normal, high temperature(in the order) images are shown above

void tuneTemperature(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int temperature = (int)1.5*(val-50);
  for (i = length; i--; ) {
       red[i] = truncate(red[i] + temperature);
       blue[i] = truncate(blue[i] - temperature);
  }
}

Tint

In Phimpme application, we adjusted the tint of an image in the same way of adjusting the temperature. But in this instead of modifying the red and blue channels, we modified the green channel of the image. An image with more tint will have a tone of magenta color and if it is decreased the image will have a greenish tone. The below shown code shows how we implemented this function in image editor of Phimpme application.

  

low tint, normal, high tint(in the order) images are shown above

void tuneTint(Bitmap* bitmap, int val) {
  register unsigned int i;
  unsigned int length = (*bitmap).width * (*bitmap).height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  int tint = (int)(1.5*(val-50));

  for (i = length; i--; ) {
       green[i] = truncate(green[i] - tint);
  }
}

Vignette

Vignetting is the reduciton in the brightness of the image towards the edges than the center. It is applied to draw the attention of the viewer to the center of the image.

 

normal and vignetted images are shown above

For implementing vignette in Phimpme application, we reduced the brightness of the pixel corresponding to a radial gradient value which is generated based on the pixel’s distance from the corner and center. It’s function in Phimpme as is shown below.

double dist(int ax, int ay,int bx, int by){
   return sqrt(pow((double) (ax - bx), 2) + pow((double) (ay - by), 2));
}

void tuneVignette(Bitmap* bitmap, int val) {
  register unsigned int i,x,y;
  unsigned int width = (*bitmap).width, height = (*bitmap).height;
  unsigned int length = width * height;
  unsigned char* red = (*bitmap).red;
  unsigned char* green = (*bitmap).green;
  unsigned char* blue = (*bitmap).blue;
  double radius = 1.5-((double)val/100), power = 0.8;
  double cx = (double)width/2, cy = (double)height/2;
  double maxDis = radius * dist(0,0,cx,cy);
  double temp,temp_s;
   for (y = 0; y < height; y++){
       for (x = 0; x < width; x++ ) {
           temp = dist(cx, cy, x, y) / maxDis;
           temp = temp * power;
           temp_s = pow(cos(temp), 4);
           red[x+y*width] = truncate((int)(red[x+y*width]*temp_s));
           green[x+y*width] = truncate((int)(green[x+y*width]*temp_s));
           blue[x+y*width] = truncate((int)(blue[x+y*width]*temp_s));
       }
   }
}

All these above mentioned functions are called from main.c file by creating JNI functions corresponding to each. These JNI functions are further defined with proper name in Java and arguments are passed to it. If you are not clear with JNI, refer my previous posts.

Resources

Continue ReadingEnhancing Images using Native functions in Phimpme Android

Sorting Events in Open Event Organizer Android App

While working on Open Event Organizer project, we had to display events in a single list in custom order with proper sub headings. Initially, we were thinking of using tabbed activity and showing events in respective tabs. But the thing with tabs is that it requires you to nest fragments and then each of them will have adapters. Also, we have used Model View Presenter pattern in the project, so this is another reason we did not use view pager as it would increase the number of presenter and view classes for the same feature. So we decided to display events in a single list instead. The custom order decided was that events would be divided into three categories – live, upcoming and past. In each category, a recent event will be at the top of another.

Adding SubHeadings support to the Recycler View

So the first thing was adding subheading support to the recycler view. We have used timehop’s sticky header decorators library for subheadings implementation. First, your adapter should implement the interface StickyRecyclerHeadersAdapter provided by the library. In our case the implemented methods look like:

@Override
public long getHeaderId(int position) {
  Event event = events.get(position);
  return DateService.getEventStatus(event).hashCode();
}

@Override
public EventsHeaderViewHolder onCreateHeaderViewHolder(ViewGroup viewGroup) {
  return new EventsHeaderViewHolder(EventSubheaderLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false));
}

@Override
public void onBindHeaderViewHolder(EventsHeaderViewHolder holder, int position) {
  Event event = events.get(position);
  holder.bindHeader(DateService.getEventStatus(event));
}

@Override
public int getItemCount() {
  return events.size();
}

 

The first one is getHeaderId which returns a unique id for a group of items which should appear under a single subheading. In this case, DateService.getEventStatus returns status of an event (either live, past or upcoming) and so hashcode of it is returned as a unique id for that header. OnCreateHeaderViewHolder is same as onCreateViewHolder of your adapter. Return your header view here. Similarly in onBindViewHolder, bind data to the header. getItemCount returns total number of items.

Sorting Events

The important thing to do was sorting events in the order decided. We had to implement the Comparable interface to Event model which will compare any two events using our custom rules such that after sorting we get events in the order – Live, Upcoming and Past with recent one at the top in each category. The compareTo method of Event model looks like:

public int compareTo(@NonNull Event otherEvent) {
  Date now = new Date();
  try {
     Date startDate = DateUtils.getDate(getStartTime());
     Date endDate = DateUtils.getDate(getEndTime());
     Date otherStartDate = DateUtils.getDate(otherEvent.getStartTime());
     Date otherEndDate = DateUtils.getDate(otherEvent.getEndTime());
     if (endDate.before(now) || otherEndDate.before(now)) {
         // one of them is past and other can be past or live or upcoming
         return endDate.after(otherEndDate) ? -1 : 1;
     } else {
         if (startDate.after(now) || otherStartDate.after(now)) {
             // one of them is upcoming other can be upcoming or live
             return startDate.before(otherStartDate) ? -1 : 1;
         } else {
             // both are live
             return startDate.after(otherStartDate) ? -1 : 1;
         }
     }
  } catch (ParseException e) {
  e.printStackTrace();
  }
  return 1;
}

 

The compareTo method returns a positive integer value for greater than, the negative integer value for less than and 0 if equal. Accordingly, we have implemented the method as per our need. At first case, we check if one of the events is past by comparing end dates with now. So the other event can be past, live or upcoming. In all the cases we will need to have an event top of another if an end date of the event is before the end date of another. In next case, only live and upcoming events pair will reach to this case. So, in this case, we check if one of them is upcoming so that other can be either upcoming or live. In both the cases, we need to have an event with start date before another’s start date at the top. Hence just comparing start dates of them will do the trick. For the last case, we are left with both live events. So here we need an event with start date after another event at the top. Hence just comparing start date if it is after other’s start date then it comes on top of another.

Using this method, events are sorted and supplied to the adapter which implements StickyRecyclerHeadersAdapter. Hence in the list, events are displayed in Live, Upcoming and Past categories as expected with respective section headers and in each category, a recent event comes on top of another.

Links:
Sticky headers decorator library- https://github.com/timehop/sticky-headers-recyclerview

Continue ReadingSorting Events in Open Event Organizer Android App

How to use Digital Ocean and Docker to setup Test CMS for Phimpme

One of the core feature of Phimpme app is sharing images to other different accounts, including various open source CMS such as WordPress, Drupal etc and other open source data storage account such as OwnCloud, NextCloud etc.

One can not have everything at place, but for development and testing purpose it is required in our end. So problem I was facing to get things done in most optimize way. I thought setting things on hosted server would be good, because it saves lots of time in setting locally in our system, adding all the dependencies. And also we cannot share the account as it is limited to our local environment.

Digital Ocean caught my attention in providing hosting service. It is very easy to use with their droplet creation. Select as per your system requirement and service requirement, the droplet will be ready in few moments and we can use it anywhere.

Note: DigitalOcean is a paid service. Student can use Github Education Pack for free credits on Digital Ocean. I used the same.

I currently worked on Nextcloud integration so here in this blog I will tell how to quickly create nextcloud server using Digital Ocean and Docker.

Step 1: Creating Droplet

DigitalOcean completely work on droplets and one can anytime create and destroy different droplets associated with their account.

Choose an Image

So there are three options of choosing the image of Droplet.

Distributions : Which is other operating systems you want to use

One Click app: It is a very good feature as it creates everything for use in just one click. But again, it doesn’t provide everything, like there is no NextCloud. That’s why I used docker to take its image.

Snapshots: This is if you saved your droplet already, so it will pick it and creates similar to the saved image. Here I selected Docker from one-click apps section.

Selecting the size

This is for selecting the size of the server we are creating, For small development purpose $5 plan is good. There is a good thing in DigitalOcean as it didn’t charge on the monthly basis to the use. It works on hourly basis and charge according to that. Also providing the SSD Disk for fast IO operations.

Choose a datacenter Region

Add SSH

This is very important to add a ssh key. Otherwise you have to setup root password or used the shell they provide. To work on your computer terminal, its good that you setup an ssh key already and it to the account.

How to get ssh key in your system: https://help.github.com/

Rename the number of droplet and name of the droplet and create.

Now it will show there in your droplet section

Step 2: Access the Server

As we have already added the ssh key to our droplet. Now we can access it from our terminal. Open the terminal and type this

➜  ~ ssh root@<your IP> 

It will logged in to you

root@docker-512mb-blr1-01:~# 

Our objective is setting a NextCloud Account.

Here now I will use Docker. Firstly, What is Docker?

Go here to read: https://www.docker.com/what-docker

I will explain docker in other words. Like I setted up everything which I need. Now If I have to destroy this all and want to use it after some days. Or if my friends wants to use the setted platform. What is the option here?

Recreate and everything everytime? NO.

Just create docker image, save it pull the image when you want, and run it to serve on the serve. Your friends need, provide them the docker image.

Isn’t it cool and much time saving.

Browse the Docker Hub

In the hub we can find docker images for various platforms officially maintained by the authors.

Nextcloud have their official account on Docker to provide latest images to the developers.

Here is the link : https://hub.docker.com/_/nextcloud/

Pull the image in your server.

root@docker-512mb-blr1-01:~# docker pull nextcloud
Using default tag: latest
latest: Pulling from library/nextcloud
9f0706ba7422: Pull complete
4c407763908f: Pull complete
82e2bc3a45c1: Pull complete
c84e1013aed1: Pull complete
a3b5e03d7e24: Pull complete
917f836a88be: Pull complete
b2dc54431819: Pull complete
a60b574790b8: Pull complete
49ef0f1aff88: Pull complete
7773a865ee49: Pull complete
9e0e5cc56a9d: Pull complete
bfade1c7421e: Pull complete
ece8ceb33bed: Pull complete
c691d2747a3e: Pull complete
4b5e96bf54c9: Pull complete
6fbe30ae456b: Pull complete
e0c534b35a6b: Pull complete
4d2687f4b6f3: Pull complete
00197422846a: Pull complete
6ab57168c49c: Pull complete
9e1260db005f: Pull complete
Digest: sha256:1bb5c256f19dcec60d8468c00bc7dc74efdf93390666cb82e20bcacbbbd9746c
Status: Downloaded newer image for nextcloud:latest
root@docker-512mb-blr1-01:~#

Following the documentation

I need to run this command $ docker run -d -p 8080:80 nextcloud

It serves the account on localhost.

Check on your https://<IP>:8080

So in this way I easily setup different account for testing and integration purpose in Phimpme Android app. It really saves lots of time and speed up the process.

You can easily destroy the droplet after work is done.

Student can use the free credits from GitHub Education Pack.

Source:

 

Continue ReadingHow to use Digital Ocean and Docker to setup Test CMS for Phimpme

Using Variables in a SUSI skill

One of the best feature provided in making a skill is the ease of using variables. From storing the favourite book of the user to the most recent movie he searched for to the mood he is in, variables play an indispensable part. If any problem is faced with the code part, the skill referred in this blog is coded in this file in susi_skill_data repository

This link refers to the official docs of SUSI, which walk you through some basic examples of how to use variables in a SUSI skill. Great skills can be achieved using them like the skill below:

It’s easy to make such skills by using variables. Let’s check it out how this skill can be achieved.

To store value in a variable we use this syntax during the skill development

^value^>_variableName

First, let’s save the favourite dish of the user and then we will try to surprise him/her with a witty answer.

I love * dish
^$1$^>_userFavouriteDish

So, if the user types “I love biryani dish”, $1$ will be equal to biryani. Let’s save it to _userFavouriteDish variable.

Now if user asks “What should i eat” to SUSI, I bet SUSI will answer a well calculated answer!

What should i eat?
I am sure you will love $_userFavouriteDish$!

Another example that can answer back the user efficiently:

How to cook biryani?

#Gives recipies and links to cook a dish
* cook *
!console:To cook  $title$ , check out $href$ and make sure you have $ingredients$! ^$2$^>_recentSearch
{
"url":"http://www.recipepuppy.com/api/?q=$2$",
"path":"$.results"
}
eol

In the above code, we saved the dish searched for at the end of the output.

If somehow user ends up asking “what is the most recent dish i searched for”. It’s skill will be:

what is the most recent dish I searched for?
It was $_recentSearch$

Even if before asking this question, user asks “how to cook sushi”. The _recentSearch variable will be overridden with value “sushi” instead of “biryani”. Hence, SUSI won’t mistake answering “most recent dish” as “sushi”!

Now I think we are bit comfortable with use of variables in a skill. Let’s get back to our target skill i.e. remembering skill. We store the thing asked to remember in a variable having the same name as of that thing and the statement related to it as the value of that variable. Examples:

Remember that my keys are on the table. So the variable will be named “keys” and it’s value will be “on the table”.

Remember that my birthday is on 20th of December. So the variable will be named “birthday” and it’s value will be “on 20th of December”.

Remember that my meetings are at 8 pm with mentors and at 9:30 pm with Shruti. So the variable will be named “meetings” and it’s value will be “at 8 pm with mentors and at 9:30 pm with Shruti”.

Hence the skill:

Remember that my * is * | Remember that my * is *
Okay, remembered!^$2$^>_$1$

When the user will ask for any of its thing, we will just show the value of the variable having the same name as of the thing asked. Examples:

#$_keys$ will be our answer
Where are my keys?
On the table                   

#$_meetings$ will be our answer
When are my meetings?
at 8 pm with mentors and at 9:30 pm with Shruti

Hence the skill which answers the question is:

when are my * | where is my * | where are my *
$_$1$$

So the skill as a whole will be:

Remember that my * is * | Remember that my * is *
Okay, remembered!^$2$^>_$1$

when are my * | where is my * | where are my *
$_$1$$

Resources

Continue ReadingUsing Variables in a SUSI skill