Adding Emoji Support in SUSI Web Chat

SUSI.AI web chat sometimes, renders responses which contains emojis. We cannot rely on browser’s capability to render these emojis. The Problem is, that the default support for emojis of browsers does not offer a great variety of emojis to be rendered. The solution we implemented in the SUSI.AI web chat is to make use of a npm package to support our need for displaying emojis.

There were many options to choose from. For example :

Comparison between emoji packages :

Property Twemoji React-easy-emoji React-twemoji React-emojione
Built by Twitter Appfigures ZxMYS Pladaria
Usage Can be used as an object with function parse: twemoji.parse() Can be used as function: emoji() It is a simple wrapper for Twemoji.Can be used as component: <Twemoji> Can be used as function: emojify() or component: <Emojify>
Conversion compatibility Provides standard Unicode emoji support across all platforms Parse only basic emojis.Doesn’t parse emoji names like 🙂 and emoticons like 🙂 Convert emoji characters to Twemoji images Converts shortnames, unicode and ASCII smileys into renderable emojis
Dependencies None loot-web-kit lodash, prop-types, twemoji None

After detailed analysis of the above mentioned packages, we decided to go with React-emojione.

The major reasons are :

  • It is very easy to use.
  • It has no dependencies.
  • It can convert shortnames, unicode and ASCII symbols properly.
  • It can be used as both function and component, which diversifies its usage.

Installation:

npm install -S react-emojione

Basic usage (as function)

import {emojify} from 'react-emojione';
 
ReactDOM.render(
    <div>
        {emojify(':p')}
    </div>,
    document.body
);

Basic usage (as component)

import Emojify from 'react-emojione';
 
ReactDOM.render(
    <Emojify>
        <span>:p</span>
    </Emojify>,
    document.body
);

Some notes about the <Emojify> component:

  • If it has a single child, it won’t be wrapped
  • Otherwise it will be wrapped with a <span>

Difference between component and function?

Functional Stateless Components are just a ‘dumb’ function that takes props as an input. They do not have any state or methods. Just (props) => { return <span>content</span>; }

Class components can have state, variables, methods etc.

Now we needed our react app to render emojis. Our component named MessageListItem.react renders all the text and images of response.

There is a function called imageParse in this component. We use this function to parse our emojis.

Screenshot of SUSI Web Chat

Emoji’s like (:p) are now rendered properly

The implementation is as follows :

function imageParse(stringWithLinks){
  let replacePattern = new RegExp([
                      '((?:https?:\\/\\/)(?:[a-zA-Z]{1}',
                      '(?:[\\w-]+\\.)+(?:[\\w]{2,5}))',
                      '(?::[\\d]{1,5})?\\/(?:[^\\s/]+\\/)',
                      '*(?:[^\\s]+\\.(?:jpe?g|gif|png))',
                      '(?:\\?\\w+=\\w+(?:&\\w+=\\w+)*)?)'
                      ].join(''),'gim');
  let splits = stringWithLinks.split(replacePattern);
  let result = [];
  splits.forEach((item,key)=>{
    let checkmatch = item.match(replacePattern);
    if(checkmatch){
      result.push(
        <img key={key} src={checkmatch}
            style={{width:'95%',height:'auto'}} alt=''/>)
    }
    else{
      result.push(<Emojify  key={key}>{item}</Emojify>);
    }
  });
  return result;
}

Here we put {item} inside <Emojify> tag to render all the emoji’s present inside {item}.

This parses all emojis regardless of browser support. This package fulfills all our needs in this case.

Resources:

react-emojione package: https://www.npmjs.com/package/react-emojione

Testing link: SUSI.AI (Web Chat): http://chat.susi.ai/

Continue ReadingAdding Emoji Support in SUSI Web Chat

Using @Output EventEmitter to Hide Search Suggestions in Angular for Susper Web App

Problem: In Susper the suggestions box doesn’t hide when there are no suggestions. To fix this, we have used @Output to create interaction between the search bar and suggestions box.

Susper gives suggestions to the user when user types a query. These suggestions are retrieved from the suggest.json endpoint from Yacy server.

We have a separate component for searching a query and a separate component for showing suggestions (auto-complete.component.ts). The architectural link between the query box, suggestion box and the results page is a bit complicated.

The search bar and the auto-complete component doesn’t interact directly. Whenever a new query is entered, the search bar triggers an action with a payload including the query. On receiving the new query, auto-complete component calls Yacy server to get suggestions from the endpoint and display them inside the suggestion box. Whenever a user searches making a new query, the search bar implementation opens the suggestion box even if there are no results. So there should be a way to inform search bar component that suggestions box has received empty results and search bar could hide the suggestions box.

To achieve this we used @Output to emit an event

@Output() hidecomponent: EventEmitter<any> = new EventEmitter<any>();

autocomplete.component.ts:-

this.autocompleteservice.getsearchresults(query).subscribe(res => {
 if (res) {
   if (res[0]) {
     this.results = res[1];
     if (this.results.length === 0) {
       this.hidecomponent.emit(1);
     } else {
       this.hidecomponent.emit(0);
     }
}

 

Then in search bar component, this is binded to a function hidesuggestions() which takes care of hiding the suggestion box.

searchbar.component.html

<app-auto-complete (hidecomponent)="hidesuggestions($event)" id="auto-box" [hidden]="!ShowAuto()"></app-auto-complete>

 

searchbar.component.ts

hidesuggestions(data: number) {
 if (data === 1) {
   this.displayStatus = 'hidebox';
 } else {
   this.displayStatus = 'showbox';
 }
}
ShowAuto() {
 return (this.displayStatus === 'showbox');
}

 

Here you could see that the auto-complete component’s hidden attribute in searchbar.component.ts is binded with ShowAuto() function which takes care about the interaction and hides the suggestions box whenever there are no results.

Below a GIF shows how this suggestions feature is working on Susper

Source code related to this implementation is available at this pull

References:

Continue ReadingUsing @Output EventEmitter to Hide Search Suggestions in Angular for Susper Web App

Multiple Page Rendering on a Single Query in Susper Angular Front-end

Problem: Susper used to render a new results page for each new character input. It should render a single page for the final query as reported in issue 371. For instance, the browser’s back button shows five pages for each of the five characters entered as a query.

Solution: This problem was arising due to code:

this.router.navigate(['/search'], {queryParams: this.searchdata});

Before we have this one line in search-bar component which gets called on each character entry

Fix:To fix this issue we required calling router.navigate only when we receive results and not on each character input.

So, we first removed the line which was cause of this issue from search-bar component and replaced it with

this.store.dispatch(new queryactions.QueryServerAction(query));

 

This triggers a QueryServer action, and make a request to Yacy end point for search results.

Now in app.component.ts , we get subscribed to resultscomponentchange$ which gets called only when new search results are received and hence we navigate to a new page after the resultscomponentchange subscription is called.

this.resultscomponentchange$ = store.select(fromRoot.getItems);
this.resultscomponentchange$.subscribe(res => {
 if (this.searchdata.query.length > 0) {
   this.router.navigate(['/search'], {queryParams: this.searchdata});
 }

});
this.wholequery$ = store.select(fromRoot.getwholequery);
this.wholequery$.subscribe(data => {
 this.searchdata = data;
});
if (localStorage.getItem('resultscount')) {
 this.store.dispatch(new queryactions.QueryServerAction({'query': '', start: 0, rows: 10, search: false}));
}

 

 

Finally, this problem got fixed and now there is only one page being rendered for a valid search. Source code for this implementation is available in this pull.

Resources:

Continue ReadingMultiple Page Rendering on a Single Query in Susper Angular Front-end

Using RouterLink in the Susper Angular Frontend to Speed up the Loading Time

In Susper, whenever the user clicks on some links, the whole application used to load again, thereby taking more time to load the page. But in Single Page Applications (SPAs) we don’t need to load the whole application. In Fact, SPAs are known to load internal pages faster than traditional HTML web pages. To achieve this we have to inform the application that a link will redirect the user to an internal page. So that the application doesn’t reload completely and reinitializes itself. In angular, this can be done by replacing href with routerLink for the tag.

Routerlink when used with tag syntactically as

<a routerLink="/contact" routerLinkActive="active">Contact</a>

doesn’t load the whole page instead it asks the server for only the contact component and renders it in place of <router-outlet></router-outlet>

This happens through an ajax call to the server asking for only contact component, thereby reducing the time it takes and doesn’t show a whole complete reload of the page.

Below time graph shows requests made when a tag with href was clicked.

If you observe it takes more than 3 seconds to load the page.

But when you use [routerLink] as an attribute for navigation, you find the page being displayed in just a blink.

What we have done in Susper?

In Susper, on issue #167, @mariobehling has noticed that there are some links which are loading slowly. On looking at the issue and a test run of the issue, I found that the problem is with the loading of the whole page, thereby immediately checked with the tag and found that a “href” attribute was used instead of “[routerLink]” angular attribute. I made a pull changing href to “[routerLink]” thereby speeding up Susper to around 3x faster than before.

https://github.com/fossasia/susper.com/pull/234/files

References

Continue ReadingUsing RouterLink in the Susper Angular Frontend to Speed up the Loading Time

Reset Password for SUSI Accounts Using Verification Link

In this blog, I will discuss how the SUSI server interprets the verification link sent to your email id to reset SUSI account password. The email one receives to reset password looks like this :  

http://api.susi.ai/apps/resetpass/index.html?token={30 character long token}

*Original link contains a token of length of 30 characters. The link has been tampered for security purpose.

Taking a close look at the reset link, one would find it easy to decode. It simply contains path to an application on current SUSI Accounts hosting. Name of the application is “resetpass” for Reset Password. But what about the token in the link?

As soon as a user clicks on the link, the app is called and token is passed as a GET parameter. The app in background makes a call to the server where the token is evaluated for whether the token is hashed against some user’s email id and has not expired yet. Below is code of first step the client does which is to make a simple ajax call on Ready state.

$(document).ready(function()
{
	var passerr = false, confirmerr = false, tokenerr = false;

	// get password parameters
	var regex;
	var urltoken = getParameter('token');

	$.ajax(	"/aaa/recoverpassword.json", {
		data: { getParameters: true, token: urltoken },
		dataType: 'json',
		success: function (response) {
			regex = response.regex;
			var regexTooltip = response.regexTooltip;
			$('#pass').tooltip({'trigger':'focus', 'placement': 'left', 'title': regexTooltip});
			$('#status-box').text(response.message);
			tokenerr = false;
		},
		error: function (xhr, ajaxOptions, thrownError) {
			$('#status-box').text(thrownError);
			$('#status-box').addClass("error");
			$('#pass').prop( "disabled", true );
			$('#confirmpass').prop( "disabled", true );
			$('#resetbut').prop( "disabled", true );
			tokenerr = true;
		},
	});

As you can see the call is made to /aaa/recoverypassword.json end point with token as the request parameter. Now, the client has to just evaluate the JSON response and render the message for user accordingly. If this request returns an error then the error message is shown and the entries are disabled to enter the password. Otherwise, user email id is shown with green text and user can now enter new password and confirm it.

In next step, client simply evaluates the password and sends a query to server to reset it. Let us now look at how server functions and processes such requests.

//check if a token is already present
if (call.get("getParameters", false)) {
			if (call.get("token", null) != null && !call.get("token", null).isEmpty()) {
				ClientCredential credentialcheck = new ClientCredential(ClientCredential.Type.resetpass_token,
						call.get("token", null));
				if (DAO.passwordreset.has(credentialcheck.toString())) {
					Authentication authentication = new Authentication(credentialcheck, DAO.passwordreset);
					if (authentication.checkExpireTime()) {
						String passwordPattern = DAO.getConfig("users.password.regex", "^(?=.*\\d).{6,64}$");
						String passwordPatternTooltip = DAO.getConfig("users.password.regex.tooltip",
								"Enter a combination of atleast six characters");
						result.put("message", "Email ID: " + authentication.getIdentity().getName());
						result.put("regex", passwordPattern);
						result.put("regexTooltip", passwordPatternTooltip);
						result.put("accepted", true);
						return new ServiceResponse(result);
					}
					authentication.delete();
					throw new APIException(422, "Expired token");
				}
				throw new APIException(422, "Invalid token");
			} else {
				throw new APIException(422, "No token specified");
			}
		}

In the above code snippet, server evaluates the received token on the basis of three parameters. First it checks whether the token is provided or not. If not, it throws APIException with error code 422 and a message “No token specified”. If it passes the check, next it checks if the token passed is valid or not. If the token is invalid, it throws different APIException with same error code but different message “Invalid token”. Finally it checks whether the token is expired or not {life of each token is 7 days. After that, server marks it as expired}.

If all checks pass, the server finds the valid email id against which the token was hashed and sends it to user in JSON format. Now let us see how the final reset  password call is handled at the server.

If the token is valid  and still has life, user will be asked to enter new password and confirm it. Client locally checks whether new password and confirm password are same or not. It will now make a call to the below given servlet.

/aaa/resetpassword.json

Till now, server has already made the client identity using the token. Next it will check if the password matches regular expression or not. If not, it sends  an error message “Invalid Password” with error code 400.

if (DAO.hasAuthentication(emailcred)) {
			Authentication emailauth = DAO.getAuthentication(emailcred);
			String salt = createRandomString(20);
			emailauth.remove("salt");
			emailauth.remove("passwordHash");
			emailauth.put("salt", salt);
			emailauth.put("passwordHash", getHash(newpass, salt));
		}

Above code snippet shows what happens when new password matches the conditions of regular expression. The server will generate a random string of 20 characters and use it as the new salt to hash the password. It updates the salt and password hash for the particular user. Next time whenever user makes a login request, server will use the new salt-hash pair to authorise the user. Below given is a flowchart for better understanding.

Resources

 

Continue ReadingReset Password for SUSI Accounts Using Verification Link

Sending Emails for Email-Verification from SUSI Server

In this blog post, I will explain how emails are sent from the SUSI server to validate Email IDs.

Whenever a user creates an account on SUSI.AI Client we are required to check the email and verify that email in order to activate it. By doing so we can reduce the number of spam and fake accounts.

//After all the imports 
public class EmailHandler {

This is a regex expression for validating the user input on the server side. This expression checks that the email address entered must match something similar to “xyz@abc.com”.

    public static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
            + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
   

This function below sends an email to the parameter addressTo. “subject” is the email subject and text the content of the Mail. The content of the mail could be plain text or HTML. Using HTML in emails helps to make clickable links and more formatted emails.

    public static void sendEmail(@Nonnull String addressTo, @Nonnull String subject, @Nonnull String text) throws Exception {
        String senderEmail = DAO.getConfig("smtp.sender.email", null);
        String displayname = DAO.getConfig("smtp.sender.displayname", null);
        sendEmail(senderEmail, displayname, addressTo, subject, text);
    }

This function below is an example of Function overloading. This is used when a displayName parameter is passed and then a mail is sent with the name of the user.

For applications using SSL/TLS endpoints whose certificates are not signed by a public CA, it is required to tell applications to trust self-signed certs.

 public static void sendEmail(String senderEmail, String displayname, @Nonnull String addressTo, @Nonnull String subject, @Nonnull String text) throws Exception {
        
        if (!"true".equals(DAO.getConfig("smtp.mails.enabled", "false"))) {
            throw new Exception("Mail sending disabled");
        }

        String username = DAO.getConfig("smtp.sender.username", null);
        String password = DAO.getConfig("smtp.sender.password", null);
        String hostname = DAO.getConfig("smtp.host.name", null);
        String encryption = DAO.getConfig("smtp.host.encryption", null);
        boolean disableCertChecking = DAO.getConfig("smtp.trustselfsignedcerts", false);

Here are some null checks so that server should not crash unexpectedly. Here we are checking for Sender Email, password and the hostname. These are used to generate the SMTP configuration for sending emails.

if(senderEmail == null || password == null || hostname == null){
            throw new Exception("Invalid SMTP configuration");
        }

Now we check the regex of the previously declared regex expression for validating the email addresses. This is done using the pattern that we defined at the very beginning of this file.

Pattern pattern = Pattern.compile(EMAIL_PATTERN);
        if (!new TimeoutMatcher(pattern.matcher(addressTo)).matches()) {
            throw new Exception("Invalid email ID");
        }
        if (!new TimeoutMatcher(pattern.matcher(senderEmail)).matches()) {
            throw new Exception("Invalid sender ID");
        }

Finally, the email is composed here with all the headers, subject and text. The Mime Message and Transport are a part of javax.mail library.

You can install that by adding the below line in you build.gradle file.

compile group: 'com.sun.mail', name: 'javax.mail', version: '1.+'
    MimeMessage message = new MimeMessage(session);
        message.addHeader("Content-type", "text/HTML; charset=UTF-8");
        message.addHeader("format", "flowed");
        message.addHeader("Content-Transfer-Encoding", "8bit");
        message.setSentDate(new Date());
        message.setReplyTo(new Address[]{new InternetAddress(senderEmail, displayname)});
        message.setFrom(new InternetAddress(senderEmail, displayname));
        message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(addressTo, false));
        message.setSubject(subject, "UTF-8");
        message.setText(text, "UTF-8");

Then we call the send method to finally send an email to the user. This sends the “message” that we composed above with all out headers, encodings, dates etc.

    Transport.send(message);
        Log.getLog().info("Successfully send mail to " + addressTo);
    }

This is a pretty old technique to validate email addresses. Mail addresses cannot be created and doctored too quickly. Also, email address verification is not the only way to verify users. This can be totally replaced by using social logins.

References

SUSI Server : https://github.com/fossasia/susi_server/

Tutorial: https://www.javatpoint.com/java-mail-api-tutorial

Official Docs: https://javaee.github.io/javamail/

Continue ReadingSending Emails for Email-Verification from SUSI Server

Performing Fourier Transforms in the PSLab Android App

Oscilloscope is one of the key features of PSLab. When periodic signals such as sine waves are read by the Oscilloscope, curve fitting functions are used to construct a curve that has the best fit to a series of data points. In PSLab, the sine curve fitting involves the Fourier Transforms. FFT (short for “Fast Fourier Transform”) is nothing more than a curve-fit of sines and cosines to some given data. In order to understand the implementation of Fourier Transforms in PSLab Android App let’s first have a look at the Fourier transform equations.

The first equation here is the Forward Fourier transform. It converts the function of time (t) into the function of frequency (ω).
The second equation is Inverse Fourier transform. It does the opposite to first equation ie. it converts the function of frequency (ω) into the function of time (t).

So, first I will answer what is transform?
It is the mapping between two different sets of domains. In this case, the information is changed from the time domain to frequency domain. The data in these domains look different but represent the same information. A transform will get you from one representation to another.

Fourier transforms converts between the time domain f(t) and the frequency domain F(ω).
Performing Fourier Transforms in Android
Let’s perform Forward Fourier transform. This means it converts the function of time (t) into the function of frequency (ω). We will use Apache Maths Commons to perform Fourier transforms. Since we have finite input data set we will calculate Discrete Fourier Transform (DFT).
The algorithm which is being used here is Fast Fourier Transform (FFT) which is the best algorithm to calculate Fourier transforms.

FastFourierTransformer fastFourierTransformer = 
      new FastFourierTransformer(DftNormalization.STANDARD);

Here we are creating an instance of FastFourierTransformer which passed STANDARD normalization convention to its constructor. Normalization other than STANDARD is UNITARY.
Complex complex[] = fastFourierTransformer.transform(input, TransformType.FORWARD);

Here, input array and TransformType. FORWARD is also passed to transform method. Input is an array of data representing time whereas TransformType. FORWARD defines the type of Fourier transform that should be performed ie. forward or inverse.

Complex complex[] = fastFourierTransformer.transform(input, TransformType.FORWARD);

The output will be an array of complex number. Each data point will be represented like the following graph in the complex plane.

Suppose the amplitude of the data point given in above graph is 1 and phase shift is 45°. So, solving this we will get 2/√2 as both real and imaginary components. Therefore, F (ω) would be 1/√2 + (1/√2) i.
Dealing with Complex numbers in Java
A complex number has both real and imaginary part. Using Apache Maths Commons we can use Complex to represent a complex number.

Complex number = new Complex(1,2);
System.out.println(number);
Output
1.0 + 2.0i

We can also get real and imaginary parts separately of Complex numbers.

System.out.println(number.getReal());
System.out.println(number.getImaginary());
Output
1.0
2.0

The array of the Complex numbers can be implemented like the following

Complex[] number;
Complex c1 = new Complex(1, 2);
Complex c2 = new Complex(3, 4);
number = new Complex[]{c1, c2};
System.out.println(Arrays.toString(number));
System.out.println(number[1]);
Output

[(1.0, 2.0), (3.0, 4.0)]
(3.0, 4.0)

Resources

Continue ReadingPerforming Fourier Transforms in the PSLab Android App

Storing a Data List in Phimpme Android

In Phimpme Android, it is required to store all the available camera parameters like a list of ISO values, available camera resolution etc. so that it can be displayed to the user in the camera settings. In Phimpme, we have stored these list of data in SharedPreferences with some modifications. As we cannot store a list directly in SharedPreference, in this post I will be discussing how we achieved this in Phimpme Android application.

To store the ArrayList you have to create a function that will convert the array into a string by using some symbol.

Step – 1

First, Create a class say TinyDB which contains functions to store an array in sharedPreferences.

public class TinyDB
{
private SharedPreferences preferences;
public TinyDB(Context appContext) {
preferences = PreferenceManager.getDefaultSharedPreferences(appContext);
}
}

Step – 2

Create functions to convert the array into string and store in sharedPreferences.

putListInt() method will convert the string ArrayList to String and store in sharedPreferences.

Similarly, putListString() method will convert the integer ArrayList to string and store in sharedPreferences.

public void putListInt(String key, ArrayList<Integer> intList) {
  if (key == null) return;
  if (intList==null) return;
  Integer[] myIntList = intList.toArray(new Integer[intList.size()]);
  preferences.edit().putString(key, TextUtils.join(“‚‗‚”, myIntList)).apply();
}

  
public void putListString(String key, ArrayList<String> stringList) {
  if (key == null) return;
  if (stringList ==null)return;
  String[] myStringList = stringList.toArray(new String[stringList.size()]);
  preferences.edit().putString(key, TextUtils.join(“‚‗‚”, myStringList)).apply();
}

 

 

Now create the object of TinyDB.class to call the above functions using tinyDb object.

Now our data is saved in sharedPreference to get this data we have to create a getter for the ArrayList.

 

Step-3

Add two functions in TinyDB.class to get the string and integer ArrayList.

public ArrayList<String> getListString(String key) {
        return new ArrayList<String>(Arrays.asList(TextUtils.split(preferences.
=getString(key, “”), “‚‗‚”)));
}



public ArrayList<Integer> getListInt(String key) {
  String[] myList = TextUtils.split(preferences.getString(key, “”), “‚‗‚”);
  ArrayList<String> arrayToList = new ArrayList<String>(Arrays.asList(myList));
  ArrayList<Integer> newList = new ArrayList<Integer>();

  for (String item : arrayToList)
      newList.add(Integer.parseInt(item));

  return newList;
}

Now to get the saved integer and string ArrayList simply call this function by creating an instance of TinyDB.class.

The below screenshot depicts how we have stored the list of camera resolutions in SharedPreference using TinyDB class.

So this is how you can store the entire ArrayList in sharedPreferences. For more detail, you can see the TinyDb.class in our Phimpme project.

Resources:  

https://stackoverflow.com/questions/7057845/save-arraylist-to-sharedpreferences

http://blog.nkdroidsolutions.com/arraylist-in-sharedpreferences/

http://findnerd.com/list/view/Save-ArrayList-of-Object-into-Shared-Preferences-in-Android/510?page=10&ppage=3

https://github.com/fossasia/phimpme-android/blob/development/app/src/main/java/org/fossasia/phimpme/opencamera/Camera/TinyDB.java

 

 

Continue ReadingStoring a Data List in Phimpme Android

Protecting Gallery Images in Phimpme Android

Encrypting media is very important to ensure privacy and for this some users install apps to lock protect their images with a password or any other security method i.e Gallery lock. In Phimpme, along with the camera, accounts and gallery, we are providing an inbuilt encryption option in which user can set password to hide media and to protect from deletion of media. In this post I am explaining how we implemented the security feature in the Phimpme Android app.

Step-1

In Phimpme I created a SecurityHelper class which contains the user password information along with the details of where the user has applied for the protection.

public class SecurityHelper {
   private boolean activeSecurity;
   private String passwordValue;
   private Context context;
   public SecurityHelper(Context context){
       this.context = context;
       }
}

The dialog box to enter the password in Phimpme.

Step -2

Now in phimpme, we will ask the user to enter the password and it gets to be done using edittext and on click on submit the password gets stored in sharedPreferences in preference_use_password in Strings.xml.

Step – 3

The next step is to protect Phimpme gallery so we have to check before deleting any images using gallery it can be done using SecurityHelper.isActiveSecurity() method.

If security option is activated, we will provide the dialog box in which user have to enter the password and we will compare the password with the saved password in Sharedpreference, if it matches then we will allow the deletion of the selected media.

if (securityObj.isActiveSecurity()) {
AlertDialog passwordDialog = passwordDialogBuilder.create();
           passwordDialog.show();

           passwordDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   // if password is correct, call DeletePhotos and perform deletion
                   if (securityObj.checkPassword(editTextPassword.getText().toString())) {
                       passwordDialog.dismiss();
                       new DeletePhotos();
                   }
                   // if password is incorrect, don't delete and notify user of incorrect password
                   else {
                       Toast.makeText(getApplicationContext(), R.string.wrong_password, Toast.LENGTH_SHORT).show();
                       editTextPassword.getText().clear();
                       editTextPassword.requestFocus();
                   }
               }
           });
       } else new DeletePhotos().execute();
   }
});

This is how we are providing the inbuilt encryption to protect gallery in Phimpme-Android.

Resources

 

 

Continue ReadingProtecting Gallery Images in Phimpme Android

Implementing the Message Response Status Indicators In SUSI WebChat

SUSI Web Chat now has indicators reflecting the message response status. When a user sends a message, he must be notified that the message has been received and has been delivered to server. SUSI Web Chat implements this by tagging messages with ticks or waiting clock icons and loading gifs to indicate delivery and response status of messages ensuring good UX.

This is implemented as:

  • When the user sends a message, the message is tagged with a `clock` icon indicating that the message has been received and delivered to server and is awaiting response from the server
  • When the user is waiting for a response from the server, we display a loading gif
  • Once the response from the server is received, the loading gif is replaced by the server response bubble and the clock icon tagged to the user message is replaced by a tick icon.

Lets visit SUSI WebChat and try it out.

Query : Hey

When the message is sent by the user, we see that the displayed message is tagged with a clock icon and the left side response bubble has a loading gif indicating that the message has been delivered to server and are awaiting response.

When the response from server is delivered, the loading gif disappears and the user message tagged with a tick icon.

 

How was this implemented?

The first step is to have a boolean flag indicating the message delivery and response status.

let _showLoading = false;

getLoadStatus(){
  return _showLoading;
},

The `showLoading` boolean flag is set to true when the user just sends a message and is waiting for server response.  When the user sends a message, the CREATE_MESSAGE action is triggered. Message Store listens to this action and along with creating the user message, also sets the showLoading flag as true.

case ActionTypes.CREATE_MESSAGE: {

  let message = action.message;
  _messages[message.id] = message;
  _showLoading = true;
  MessageStore.emitChange();
  
  break;
}

The showLoading flag is used in MessageSection to display the loading gif. We are using a saved gif to display the loading symbol. The loading gif is displayed at the end after displaying all the messages in the message store. Since this loading component must be displayed for every user message, we don’t save this component in MessageStore as a loading message as that would lead to repeated looping thorugh the messages in message store to add and delete loading component.

import loadingGIF from '../../images/loading.gif';

function getLoadingGIF() {

  let messageContainerClasses = 'message-container SUSI';

  const LoadingComponent = (
    <li className='message-list-item'>
      <section className={messageContainerClasses}>
        <img src={loadingGIF}
          style={{ height: '10px', width: 'auto' }}
          alt='please wait..' />
      </section>
    </li>
  );
  return LoadingComponent;
}

We then use this flag in MessageListItem class to tag the user messages with the clock icons. We used Material UI SVG Icons to display the clock and tick messages. We display these beside the time in the messages.

import ClockIcon from 'material-ui/svg-icons/action/schedule';

statusIndicator = (
  <li className='message-time' style={footerStyle}>
    <ClockIcon style={indicatorStyle}
      color={UserPreferencesStore.getTheme()==='light' ? '#90a4ae' : '#7eaaaf'}/>
  </li>
);

When the response from server is received, the CREATE_SUSI_MESSAGE action is triggered to render the server response. This action is again collected in MessageStore where the `showLoading` boolean flag is reset to false. This event also triggers the state of MessageSection where we are listening to showLoading value from MessageStore, hence triggering changes in MessageSection and accordingly in MessageListItem where showLoading is passed as props, removing the loading gif component and displaying the server response and replacing the clock icon with tick icon on the user message.

case ActionTypes.CREATE_SUSI_MESSAGE: {
  
  let message = action.message;
  MessageStore.resetVoiceForThread(message.threadID);
  _messages[message.id] = message;
  _showLoading = false;
  MessageStore.emitChange();
  
  break;
}

This is how the status indicators were implemented for messages. The complete code can be found at SUSI WebChat Repo.

Resources

Continue ReadingImplementing the Message Response Status Indicators In SUSI WebChat