Integrating Forgot Password feature within Login Screen in SUSI Android App

For a user’s point of view the interface and the flow of the app is of great importance, the UI of the app should be simple and sufficient so that it does not confuse the user and provides all the necessary information to the user with the first look. In all the  apps it is the user interface that engages the user and makes the user want to use the app. So, in SUSI Android app UI flow was improved by removing the Forgot Password activity altogether.

What SUSI.AI android app previously had ?

Previously the SUSI.AI android app used to have three different screens for the user’s account related support :

  1. Login Screen
  2. Forgot Password Screen
  3. SignUp Screen    

The login screen had a Forgot Password? Option that takes the user to a new screen where the details entered in the login activity had to be entered again and only then can the user request a new password.

What are the drawbacks of this ?

Separately, providing a new activity for the specific purpose of resetting the password does not contribute towards an efficient use of UI items of the screen. A scenario where this will be annoying to the user is for eg :  when a user tries to login to the app and is unable to do so because of the incorrect credentials, user simply clicks on the Forgot Password option and on opening the Forgot Password activity to the user’s surprise all the fields entered in the login screen are to be entered again and this is really fuzzy and sometimes frustrating to the user.

A simple solution implemented for this purpose was to automatically reflect the credentials entered by the user in the login screen on the forgot password screen so  that user did not had to enter all the details again.

What better could be done and the solution?

The simplest UI for the purpose of resetting a password is to just click the Forgot Password? and user receives an email to reset the password.

Using this approach several changes were made to the app’s code.

The first change to be made was to implement the ForgotPasswordPresenter.kt functions in the LoginPresenter.kt and similarly implement the IForgotPasswordView.kt functions in the LoginActivity.kt.

The two major functions in the  IForgotPasswordPresenter.kt were :

fun requestPassword(email: String, url: String, isPersonalServerChecked: Boolean)

fun cancelSignup()

Along with these functions in the LoginPresenter.kt the view functions to reflect the view actions of the ForgotPasswordActivity.kt had to be implemented in the LoginActivity.kt file, so the functions added to the ILoginView.kt file were :

fun showForgotPasswordProgress(boolean: Boolean)

fun resetPasswordSuccess()

fun resetPasswordFailure(title: String?, message: String?, button: String?, color: Int)

Now, the two functions above which were earlier present in the ForgotPasswordPresenter.kt file were implemented in the LoginPresenter.kt file and along with the requestPassword() method the listener IForgotPasswordModel.OnFinishListener had to be implemented in the Login Presenter too. So, on implementing this listener we implement a method :

override fun onForgotPasswordModelSuccess(response: Response<ForgotPasswordResponse>) {
  loginView?.showForgotPasswordProgress(false)
  if (response.isSuccessful && response.body() != null) {
      loginView?.resetPasswordSuccess()
  } else if (response.code() == 422) {
      loginView?.resetPasswordFailure(utilModel.getString(R.string.email_invalid_title), utilModel.getString(R.string.email_invalid), utilModel.getString(R.string.retry), Color.RED)
  } else {
      loginView?.resetPasswordFailure(“${response.code()} “ + utilModel.getString(R.string.error), response.message(), utilModel.getString(R.string.ok), Color.BLUE)
  }

}

Now after implementing these methods in Presenter file we have to implement the methods. The function resetPasswordSuccess() works as :

override fun resetPasswordSuccess() {
  startActivity(Intent(this@LoginActivity, ForgotPass::class.java))
}

On successful request for the password from the server the above method in the activity is called and so it takes us to the new activity. The new activity  only contains a simple screen with a default message :

The above screen is the final output once we click on Forgot Password? on the login screen.

References :

Trying to Build Android MVP App in Kotlin – Eminarti Sianturi https://android.jlelse.eu/trying-to-build-android-mvp-app-in-kotlin-afdff9da2f28

Build a Responsive UI with constraint layout

https://developer.android.com/training/constraint-layout/

 

How the presenter and view interact in the MVP pattern

https://softwareengineering.stackexchange.com/questions/284356/how-can-the-presenter-or-view-interact-with-the-model-in-the-mvp-pattern

 

Continue ReadingIntegrating Forgot Password feature within Login Screen in SUSI Android App

“STOP” action in SUSI Android App

Generally whenever there was a long query asked from SUSI through speech, it would respond to the user with a speech output, similarly the output is given through speech whenever the user clicks on the “Try it” button on the skill details activity.

The long answers for e.g. asking SUSI “How to cook biryani ?” gives a very long response which when conveyed through the speech output takes a long amount of time. Also most of the time users don’t want to listen to such long answers, but at the same time there is no option or feature to stop SUSI. The user either needs to switch over to another activity or has to close the app.

So, to solve this problem such that neither the user has to shut down the app nor the user has to switch over to another activity, the “STOP” action was added in SUSI.

The “STOP” action was integrated in the server and is of following type :

 

“actions”: [{“type”: “stop”}],

How to define STOP response ?

To integrate this action type in the app, a separate response type was added and checked for. In the file ParseSusiResponseHandler.kt the action type stop was added as :

Constant.STOP -> try {
  stop = susiResponse.answers[0].actions[1].type
} catch (e: Exception) {

}

So, now whenever the query of type “stop” was entered by the user it would be caught by this block and further processing could be done. The stop variable takes the value from the JSON response fetched and the value extracted is the one stored against the type key in the second field of the actions JSON in the first field of the answers JSON.

What to be done when executing STOP?

After the STOP action was caught the task to do was to define the behaviour of the app when this response was caught. So, first thing that should happen when STOP is received is that the TextToSpeech Engine should close so that SUSI no longer can speak the sentence but defining top is more than just closing the TTS engine. STOP signifies that any activity that is being continued right now should be stopped.

So, Using the android lifecycle methods I added a function in the IChatView interface to define what actions should take place in the stopping process.

override fun stopMic() {
  onPause()
  registerReceiver(networkStateReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))

  window.setSoftInputMode(
          WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
  )

  if (recordingThread != null)
      chatPresenter.startHotwordDetection()

  if (etMessage.text.toString().isNotEmpty()) {
      btnSpeak.setImageResource(R.drawable.ic_send_fab)
      etMessage.setText(“”)
      chatPresenter.micCheck(false)
  }

  chatPresenter.checkPreferences()
}

The above function sends the activity to the onPause() lifecycle method to put the activity in a pausing state so that all that is taking place right now in the activity stops and this definitely serves our purpose. But after pausing the activity, there was a further need to perform some functionality that had to done on the start of the activity and therefore the code for that was also added in this function.

How to catch STOP?

The below code was added in ChatPresenter.kt file in which if the actionType from the psh object of type ParseSusiResponseHelper is “STOP” then the view function stopMic() is called which was defined above.

val psh = ParseSusiResponseHelper()
psh.parseSusiResponse(susiResponse, i, utilModel.getString(R.string.error_occurred_try_again))

var setMessage = psh.answer
if (psh.actionType == Constant.ANSWER && (PrefManager.checkSpeechOutputPref() && check || PrefManager.checkSpeechAlwaysPref())) {
  setMessage = psh.answer

  var speechReply = setMessage
  if (psh.isHavingLink) {
      speechReply = setMessage.substring(0, setMessage.indexOf(“http”))
  }
  chatView?.voiceReply(speechReply, susiResponse.answers[0].actions[i].language)
} else if (psh.actionType == Constant.STOP) {
  setMessage = psh.stop
  chatView?.stopMic()
}

Final Output

References

  1. Stop  json response from susi server : https://api.susi.ai/susi/chat.json?timezoneOffset=-330&q=susi+stop
  2. Android life cycle methods – Google: https://developer.android.com/guide/components/activities/activity-lifecycle
  3. Interaction between view and presenters : https://medium.com/@cervonefrancesco/model-view-presenter-android-guidelines-94970b430ddf
Continue Reading“STOP” action in SUSI Android App

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 ReadingUse PreferenceManager in place of SharedPreferences

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 ReadingImplementing Filters in Phimp.me Android App