Testing Endpoints on Local Server

All servlets in SUSI.AI have a BaseUserRole defined. It represents the access level you need to access the endpoint corresponding to that servlet. The lowermost BaseUserRole a SUSI.AI servlet can have is ANONYMOUS, which means that anyone can access the endpoint corresponding to these endpoints. But if the BaseUserRole is higher than that, then you need an access token to access the endpoint. This blog post explains how you can get access token to access the endpoints on a local server.

What are endpoints in an API?

An endpoint in API is one end of a communication channel. When an API interacts with another system, the touchpoints of this communication are considered endpoints. For APIs, an endpoint can include a URL of a server or service. Each endpoint is the location from which APIs can access the resources they need to carry out their function.

APIs work using ‘requests’ and ‘responses.’ When an API requests information from a web application or web server, it will receive a response. The place that APIs send requests and where the resource lives, is called an endpoint.

For example, the endpoint for https://api.susi.ai/cms/getSkillRating.json?queryParameters would be /cms/getSkillRating.json.

Servlets and Endpoints in SUSI.AI

All servlets in our SUSI project define an endpoint and also define a BaseUserRole, that is, the amount of privileges required to access the information on those endpoints. If the BaseUserRole defined is ANONYMOUS, then anyone can access the endpoint directly. But if the BaseUserRole is anything higher than that, then we would need an access token to access that.

How to get Access Token?

If you’re trying to access the endpoints with BaseUserRole higher than ANONYMOUS on the actual hosted server, then you can simply login to https://chat.susi.ai and get the access token from the Network tab of the Developers Tool. We can then use that token and pass that as a query parameter along with the other parameters of that particular endpoint. For example,

http://localhost:4000/aaa/listUserSettings.json?access_token=6O7cqoMbzlClxPwg1is31Tz5pjVwo3

 

But, the problem arises when you are trying to access such endpoints on local server. The local User data is completely different from the server User data. Hence, we need to generate an access token in localhost itself.

To generate access token for local server, we need to follow these steps :

  1. First, we need to hit the /aaa/signup.json endpoint with a new account credentials which we want to register for the localhost session. This is done as shown in below example:

http://localhost:4000/aaa/signup.json?signup=anyEmail&password=anyPassword

 

  1. Then, we need to hit the /aaa/login.json endpoint with the same credentials you registered in the previous step. This is done as shown in below example:

http://localhost:4000/aaa/login.json?login=yourEmail&type=access-token&password=yourPassword

 

If you’ve entered the registered credentials correctly, then the output of the /aaa/login.json endpoint would be a JSON as shown below:

{
  "accepted": true,
  "valid_seconds": 604800,
  "access_token": "7JPi7zNwemg1YYnr4d9JIdZMaIWizV",
  "message": "You are logged in as anyemail",
  "session": {"identity": {
    "type": "host",
    "name": "127.0.0.1_4e75edbb",
    "anonymous": true
  }}
}

 

As it can be seen from the above JSON response, we get the access token which we needed. Hence, copy this access token and store it somewhere because you can now use this access token to access the endpoints with BaseUserRole as User for this localhost session.

Note that you’ll have to follow all the above steps again if you start a fresh localhost session.

Resources

Continue ReadingTesting Endpoints on Local Server

Implementing Carousel Slider in PSLab Android App

This blog is a demonstration for creating a Carousel Picker in Android by taking an example of the Carousel Picker made in PSLab Android app under PR #1007. Some improvement to this would be to add custom animation to the ViewPager and adjusting the ViewPager sliding speed. So first let’s start with the basics and terminology of Carousel.

What is Carousel?

Carousel according to the dictionary means roundabout or merry-go-round. The term was mainly used for the traditional amusement ride of a merry-go-round in amusement parks with seats of horses. The same is the working of Carousel View in Android. It gives a smooth sliding effect to slide between a variety of options available.

How to implement Carousel View in the app?

Following are the steps to implement a basic Carousel View in the app. Further effects and upgrades can be given as per the need.

  • The first step is to add jitpack to your app’s gradle file
maven { url 'https://jitpack.io '}
  • Now add a library dependency in your project level gradle file
compile 'com.github.Vatican-Cameos:CarouselPicker:v1.0

The above dependency uses the View Pager and Gesture Detector functionality provided by Android. The Gesture Detector class detects the swipe gesture made by the user and the View Pager highlights the relevant label in the Carousel box according to the swipe done i.e left or right.

  • Now Carousel Picker is ready to be added directly to layouts. So, add the Carousel by adding the following layout code at a proper section in layouts file.
<in.goodiebag.carouselpicker.CarouselPicker
	android:id="@+id/carouselPicker"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:layout_marginTop="20dp"
	android:layout_marginBottom="20dp"
	android:background="#DDD"
	apps:items_visible="three" />

Here, the items_visible is used to provide the Carousel Picker with the number of max items to be seen at a time on screen where only one item will be in focus. Other items are adjusted on the side and can be viewed by scrolling.

  • Now as we have implemented the layouts, so now’s the time to set adapter and resource type for Carousel to hold in Java files. First, find the Carousel View with its id.
CarouselPicker carouselPicker = findViewById(R.id.carouselPicker);
  • Now set a list of items to be added in the Carousel Picker. The items can be both images and texts.
List<CarouselPicker.PickerItem> items = new ArrayList<>();

To add images :

items.add(new CarouselPicker.DrawableItem(R.mipmap.ic_launcher));

To add texts/strings :

items.add(new CarouselPicker.TextItem("Example", 10));

Here, the integer that is added after the text indicates the size in sp of the text that is to be displayed in Carousel View.

  • Now after creating a list of items, make an adapter which provides this list of information to Carousel Picker.
CarouselPicker.CarouselViewAdpater adapter = new CarouselPicker.CarouselViewAdpater(this, items);
  • Now set the adapter for the Carousel View :
carouselPicker.setAdapter(adapter);
  • To dynamically add items to the Carousel View, simply change the list of items in the list provided to the adapter and then use
adapter.notifyDataSetChanged();
  • Now to change the functionality of the app with every Carousel item, implement the onPageChangeListener as Carousel View implements ViewPager class.
carouselPicker.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        	@Override
        	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        	}

        	@Override
        	public void onPageSelected(int position) {
        	}

        	@Override
        	public void onPageScrollStateChanged(int state) {
          }

Following GIF shows how Carousel View looked after implementation in PSLab app. Each option provided in the view was used to provide user with a different channel selection mode.

Figure 1. GIF of implemented Carousel View in PSLab app

So in this way, a Carousel Picker or Carousel View can be implemented in the app. Further functionalities of animations, mirroring, shadow effect, all can be done with just minor changes in the above code. And to fully customize the look of the Carousel or to enable infinite scrolling feature, a local Carousel Picker can be implemented by just making a custom adapter and a class that extends ViewPager class. Below are the resources to implement both custom and dependency based Carousel View.

Resources

  1. https://www.youtube.com/watch?v=sTJm1Ys9jMI – Youtube Video for dependency based Carousel View
  2. https://www.youtube.com/watch?v=4ct0oPf_u2o – Youtube Video for implementing infinite scrolling
  3. http://www.codexpedia.com/android/android-carousel-view-using-viewpager/ – An article to implement custom Carousel View

 

 

 

 

 

Continue ReadingImplementing Carousel Slider in PSLab Android App

Attendee details in the Open Event Android App

To be able to create an order we first need to create an attendee with whom we can associate an order. Let’s see how in Open Event Android App we are creating an attendee.

We are loading the event details from our local database using the id variable. Since only logged in users can create an attendee, if the user is not logged in then the user is redirected to the login screen. If any errors are encountered while creating an attendee then they are shown in a toast message to the user. When the user clicks on the register button a POST request is sent to the server with the necessary details of the attendee. In the POST request we are passing an attendee object which has the id, first name, last name and email of the attendee. The ticket id and event id is also sent.

attendeeFragmentViewModel.loadEvent(id)

if (!attendeeFragmentViewModel.isLoggedIn()) {
redirectToLogin()
Toast.makeText(context, "You need to log in first!", Toast.LENGTH_LONG).show()
}

 

attendeeFragmentViewModel.message.observe(this, Observer {
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
})

attendeeFragmentViewModel.progress.observe(this, Observer {
it?.let { Utils.showProgressBar(rootView.progressBarAttendee, it) }
})

 

attendeeFragmentViewModel.event.observe(this, Observer {
it?.let { loadEventDetails(it) }
})

rootView.register.setOnClickListener {
val attendee = Attendee(id = attendeeFragmentViewModel.getId(),
firstname = firstName.text.toString(),
lastname = lastName.text.toString(),
email = email.text.toString(),
ticket = ticketId,
event = eventId)

attendeeFragmentViewModel.createAttendee(attendee)

 

We are using a method called loadEvent in the above code which is defined in the View Model let’s have a look. We are throwing an IllegalStateException if the id is equal to -1 because this should never happen. Then we are fetching the event from the database in a background thread. If we face any errors while fetching the event we report it to the user

 

fun loadEvent(id: Long) {
if (id.equals(-1)) {
throw IllegalStateException("ID should never be -1")
}
compositeDisposable.add(eventService.getEvent(id)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.value = it
}, {
Timber.e(it, "Error fetching event %d", id)
message.value = "Error fetching event"
}))
}

 

This method is used to create an attendee. We are checking if the user has filled all the fields if any of the fields is empty a toast message is shown. Then we send a POST request to the server in a background thread. The progress bar starts loading as soon as the request is made and then finally when the attendee has been created successfully, the progress bar stops loading and a success message is shown to the user. If we face any errors while creating an attendee, an error message is shown to the user.

fun createAttendee(attendee: Attendee) {
if (attendee.email.isNullOrEmpty() || attendee.firstname.isNullOrEmpty() || attendee.lastname.isNullOrEmpty()) {
message.value = "Please fill all the fields"
return
}

compositeDisposable.add(attendeeService.postAttendee(attendee)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
progress.value = true
}.doFinally {
progress.value = false
}.subscribe({
message.value = "Attendee created successfully!"
Timber.d("Success!")
}, {
message.value = "Unable to create Attendee!"
Timber.d(it, "Failed")
}))
}

 

This function sends a POST request to the server and stores the attendee details in the local database.

fun postAttendee(attendee: Attendee): Single<Attendee> {
return attendeeApi.postAttendee(attendee)
.map {
attendeeDao.insertAttendee(it)
it
}

 

This is how the attendee details are inserted into the local database. In case of a conflict the attendee object gets replaced.

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAttendee(attendee: Attendee)

 

Resources

  1. ReactiveX official documentation : http://reactivex.io/
  2. Vogella RxJava 2 – Tutorial : http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial : https://www.androidhive.info/RxJava/
Continue ReadingAttendee details in the Open Event Android App

Added “table” type action support in SUSI android app

SUSI.AI has many actions supported by it, for eg: answer, anchor, map, piechart, websearch and rss.These actions are a few of those that can be supported in the SUSI.AI android app, but there are many actions implemented on the server side and the web client even has the implementation of how to handle the “table” type response.

The table response is generally a JSON array response with different json objects, where each json object have similar keys, and the actions key in the JSON response has the columns of the table response which are nothing but the keys in the data object of the response.

To implement the table type response in the susi android app a separate file needed to made to parse the table type response, since the keys and values both are required to the display the response. The file ParseTableSusiResponseHelper.kt was made which parsed the JSON object using the Gson converter factory to get the key value of the actions :

“actions”: [

       {

         “columns”: {

           “ingredients”: “Ingredients”,

           “href”: “Instructions Link”,

           “title”: “Recipe”

         },

         “count”: -1,

         “type”: “table”

       }

     ]

 

The inside the columns the keys and the values, both were extracted, values were to displayed in the title of the column and keys used were to extract the values from the “data” object of the response.

The files TableColumn.java, TableData.java are POJO classes that were used for storing the table columns and the data respectively. The TableDatas.java class was used to store the column list and the data list for the table response.

To fetch the table type response from the server a TableSusiResponse.kt file was added that contained serializable entities which were used to map the response values fetched from the server. A variable that contained the data stored in the “answers” key of the response was made of type of an ArrayList of TableAnswers.

@SerializedName(“answers”)
@Expose
val answers: List<TableAnswer> = ArrayList()

The TableAnswer.kt is another file added that contains serializable variables to store values inside the keys of the “answers” object. The actions object shown above is inside the answers object and it was stored in the form of an ArrayList of TableAction.

@SerializedName(“actions”)
@Expose
val actions: List<TableAction> = ArrayList()

Similar to TableAnswer.kt file TableAction.kt file also contains serializable variables that map the values stored in the “actions” object.

In the retrofit service interface SusiService.java a new call was added to fetch the data from the server as follows :

@GET(“/susi/chat.json”)
Call<TableSusiResponse> getTableSusiResponse(@Query(“timezoneOffset”) int timezoneOffset,
                                           @Query(“longitude”) double longitude,
                                           @Query(“latitude”) double latitude,
                                           @Query(“geosource”) String geosource,
                                           @Query(“language”) String language,
                                           @Query(“q”) String query);

Now, after the data was fetched, the table response can be parsed using the Gson converter factory in the ParseTableSusiResponseHelper.kt file. Below is the implementation :

fun parseSusiResponse(response: Response<TableSusiResponse>) {
  try {
      var response1 = Gson().toJson(response)
      var tableresponse = Gson().fromJson(response1, TableBody::class.java)
      for (tableanswer in tableresponse.body.answers) {
          for (answer in tableanswer.actions) {
              var map = answer.columns
              val set = map?.entries
              val iterator = set?.iterator()
              while (iterator?.hasNext().toString().toBoolean()) {
                  val entry = iterator?.next()
                  listColumn.add(entry?.key.toString())
                  listColVal.add(entry?.value.toString())
              }
          }
          val map2 = tableanswer.data
          val iterator2 = map2?.iterator()
          while (iterator2?.hasNext().toString().toBoolean()) {
              val entry2 = iterator2?.next()
              count++;
              for (count in 0..listColumn.size – 1) {
                  val obj = listColumn.get(count)
                  listTableData.add(entry2?.get(obj).toString())
              }
          }
          tableData = TableDatas(listColVal, listTableData)
      }
  } catch (e: Exception) {
      tableData = null
  }
}

 

Now the data is also parsed, we pass the two lists the ColumnList and DataList to the variable of TableDatas.

Three viewholder classes were added to display the table response properly in the app and corresponding to these viewholders a couple of adapters were also made that are responsible for setting the values in the recyclerview present in the views. The first viewholder is the TableViewHolder, it contains the horizontal recyclerview that is used to display the items fetched from the “data” object of the response. The recyclerview in the TableViewHolder has each entity of the type TabViewHolder, this is a simple cardview but also contains another recyclerview inside it which is used to store the keys and values of each of the object inside the “data” object.

TableViewHolder.java file has a setView() method that uses the the object of ChatMessage to get the list of columns and data to be set in the view.

 

Changes were made in the ChatPresenter.kt file to catch the tableresponse when a table type action is detected. Below is the implementation :

if (response.body().answers[0].actions[i].type.equals(“table”)) {
  tableResponse(query)
  return
}

The tableResponse function is as follows :

fun tableResponse(query: String) {
  val tz = TimeZone.getDefault()
  val now = Date()
  val timezoneOffset = -1 * (tz.getOffset(now.time) / 60000)
  val language = if (PrefManager.getString(Constant.LANGUAGE, Constant.DEFAULT).equals(Constant.DEFAULT)) Locale.getDefault().language else PrefManager.getString(Constant.LANGUAGE, Constant.DEFAULT)
  chatModel.getTableSusiMessage(timezoneOffset, longitude, latitude, source, language, query, this)
}

 

It calls the chatModel to get the list of columns and data to be set. The ChatFeedRecyclerAdapter.java files checks for the table response code, and if it matches then the view used for displaying SUSI’s message is the TableViewHolder. Here is how this viewholder is inflated :

case TABLE:
  view = inflater.inflate(R.layout.susi_table, viewGroup, false);
  return new TableViewHolder(view, clickListener);

Below is the final result when the table response is fetched for the query “Bayern munich team players” is :

References :

  1. SUSI server response for table query : https://api.susi.ai/susi/chat.json?timezoneOffset=-330&q=barcelona+team+players
  2. GSON for converting java objects to JSON and JSON to java : http://www.vogella.com/tutorials/JavaLibrary-Gson/article.html
Continue ReadingAdded “table” type action support in SUSI android app

Making Bottomsheet responsive using Custom Gesture Detector in PSLab Android App

In the previous blog Creating Instruction Guide using Bottomsheet, I have created the Bottom Sheet guide in instrument activities in PSLab Android app. But simply adding the Bottom Sheet in the layout is not enough as it could lead to some UI issues like no proper way to show or hide the Bottom Sheet, therefore, he/she will find it difficult to work with Bottom Sheet that could degrade User Experience.

We need to make the Bottom Sheet responsive and interactive which we can do by capturing swipe gestures done by the user and overriding their functionality i.e. when the user slides up with the finger then the Bottom Sheet will reveal itself and when the user slides the finger down the Bottom Sheet will hide.

For this Android provides a class GestureDetector which is used with another class SimpleOnGestureListener which acts as a listener to capture Gesture events like swipe, pinch, scroll, long press etc.

In this blog, I will create a custom gesture listener that will listen to the swipe events and according to the gestures it will show/hide the Bottom Sheet.

I will start by creating a gesture listener class called “SwipeGestureListener” extending the class ‘GestureDetector.SimpleOnGestureListener’ and also as I need swipe gestures to control the Bottom Sheet, so I will pass the reference of the Bottom Sheet as a parameter in the constructor.

public class SwipeGestureListener extends GestureDetector.SimpleOnGestureListener{
   private  BottomSheetBehavior bottomSheet;

   public SwipeGestureDetector(BottomSheetBehavior bt) {
       bottomSheet = bt;
   }  
}

Now in this listener class as we are concerned with the swipe events so will only override the below method provided by ‘GestureDetector.SimpleOnGestureListener’ interface

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

This method is called whenever the user swipes its finger in any direction.

In the above code, we can see that the method provides with object e1 and e2 of type MotionEventThe MotionEvent class is used to report movements in terms of Action Codes like ACTION_DOWN, ACTION_UP and also contains other information about the touch like the pressure of the touch, x and y coordinate, orientation of the contact area etc. 

The e1 object will have the attribute values relating to the point when the swipe started and the e2 object will have attribute values relating to the point when the swipe has ended.

Now, the main thing we need to determine if the direction of the swipe which is not directly available using the MotionEvent object.

So, to determine the direction of the swipe I will fetch the coordinates of the initial point and terminal point of the swipe using the objects initial and final point i.e., e1 and e2.

//Initial Point
float x1 = e1.getX(), y1 = e1.getY();

//Final Point
float x2 = e2.getX(), y2 = e2.getY();

Then, using these coordinates to calculate the angle of the swipe and based on the angle I will return the direction of the swipe as shown in the code below

private Direction getDirection(float x1, float y1, float x2, float y2) {

       Double angle = Math.toDegrees(Math.atan2(y1 - y2, x2 - x1));

       if (angle > 45 && angle <= 135)
           return Direction.TOP;
       if (angle >= 135 && angle < 180 || angle < -135 && angle > -180)
           return Direction.LEFT;
       if (angle < -45 && angle>= -135)
           return Direction.DOWN;
       if (angle > -45 && angle <= 45)
           return Direction.RIGHT;

       return null;     // required by java to avoid error
   }

As of now, I have the direction of the swipe so I will apply switch case and handle the swipe up and swipe down gesture as below:

  1. When the user slides up:-  Show the Bottom Sheet by changing the state of the Bottom Sheet from STATE_HIDDEN to STATE_COLLAPSED(partially viewable).
                                          
  2. When the user slides down: – Hide the Bottom Sheet by changing the state of the Bottom Sheet to STATE_HIDDEN.

For doing this, we will modify the onFIing()’ method as shown below

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   switch (getDirection(e1.getX(), e1.getY(), e2.getX(), e2.getY())) {
       case TOP:
           bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
           return true;
       case LEFT:
           return true;
       case DOWN:
           if(bottomSheet.getState()==BottomSheetBehavior.STATE_COLLAPSED){
               bottomSheet.setState(BottomSheetBehavior.STATE_HIDDEN);
           }
           return true;
       case RIGHT:
           return true;
       default:
           return false;
   }
}

Now, the custom gesture listener is implemented but it cannot start listening to the touch event on its own, so we need to resolve this by performing the following steps:

  1. Firstly, we need to create an object of class GestureDetector and pass the current activity context and the object of class ‘SwipeGestureListener’ as parameters. Also while creating the listener for ‘SwipeGestureListener’ we need to pass the object of the Bottom Sheet in it as a parameter.

    GestureDetector gestureDetector = new GestureDetector(this, new SwipeGestureListener(bottomSheetBehavior)); 
  2. Then we need to override the ‘onTouchEvent()’ method of our Activity and pass the event which is received as a parameter to the GestureDetector.
    Doing this will pass the touch event that it received to the GestureDetector for it to handle.

    @Override
    public boolean onTouchEvent(MotionEvent event) {
       gestureDetector.onTouchEvent(event);                
       return super.onTouchEvent(event);
    }
    

The Bottom Sheet is now responsive to the gestures on the screen and this will improve the User Experience.

Resources

  1. Detect Common Gestures – Android Developer Article –  Android documentation
  2. Choreographic animations with Android’s Bottom Sheet – Blog by Orkhan Gasimli

 

Continue ReadingMaking Bottomsheet responsive using Custom Gesture Detector in PSLab Android App

Implementing Five Star Rating UI in SUSI iOS

Five-star rating system introduced in SUSI to rate skills. SUSI enable the user to rate skills between 1 to 5 star. The five-star rating system is the best way to get feedback from the user. It also helps the developer for further development. Ratings help to better understand individual preferences and present a more personalized user experience. The user feedback helps products understand whether or not the content is valuable and improve offerings over time. This can benefit products with and without sophisticated personalization.

Let’s see how the five-star rating system is implemented in SUSI iOS.

Average ratings displayed near the Try It button – It shows the average rating of a particular skill.

Enable user to submit the rating of any skill between 1-star to 5-star.

The only logged-in user can submit the ratings for skills.

Rating chart that display number of rating for each star (1 to 5), the right labels of chart bars shows the number of users rated for a particular star with the percentage.

Average and total ratings for particular skills is also displayed near the bar chart.

Thumbs-up and thumbs-down ratings removed from the skill detail screen and replaced with 5-star ratings.

Implementation of Rating Chart

For the rating chart, we are using TEAChart class, which enable us to present rating data on bar charts.

Setting colors for bar chart:

We are using Google’s Material Design color for rating bars colors.

let barChartColors = [
UIColor.fiveStarRating(),
UIColor.fourStarRating(),
UIColor.threeStarRating(),
UIColor.twoStarRating(),
UIColor.oneStarRating()
]

Assigning colors to bars:

barChartView.barColors = barChartColors

Assign Data to the bars:

// Sample data
barChartView.data = [5, 1, 1, 1, 2]

Set background color and bar spacing:

barChartView.barSpacing = 3
barChartView.backgroundColor = UIColor.barBackgroundColor()

Final Output –

Resources –

  1. Material Design: https://material.io/design/
  2. SUSI iOS Link: https://github.com/fossasia/susi_iOS
Continue ReadingImplementing Five Star Rating UI in SUSI iOS

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

Implementing Check-in time chart in Orga App

Earlier in the Open event orga app there were no charts present to track the check-in time of the attendees. Hence it was quite cumbersome for the organiser to track the people and at what time they have checked-in. Using this feature of check-in time chart, the process has become quite easier.

Whenever an attendee checks-in, the data point is added to the chart and a chart is plotted. The Y-axis shows the number of attendees and the X-axis shows the time at which they have checked-in.

To implement this feature I have taken use of the MPAndroidCharts library which makes the job a lot easier. Following steps were followed to implement the charts:

  • Adding the following Library dependency in the build.gradle file
implementation “com.github.PhilJay:MPAndroidChart:v3.0.3”
  • Now the following code is added to the ticket_analytics.xml file. This is done so that the UI of the charts can be created. The following XML file consists of the LineChart XML tag which shows the check-in time chart on screen. Also the labelling of the axis needs to be done, so the X-axis is explicitly named as “TIME”.
<LinearLayout
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  android:orientation=“vertical”>

  <TextView
      android:layout_width=“wrap_content”
      android:layout_height=“wrap_content”
      android:layout_marginLeft=“@dimen/spacing_normal”
      android:layout_marginStart=“@dimen/spacing_normal”
      android:layout_marginTop=“@dimen/spacing_normal”
      android:text=“@string/check_in_summary”
      android:textAllCaps=“true”
      android:textSize=“@dimen/text_size_small” />

  <com.github.mikephil.charting.charts.LineChart
      android:id=“@+id/chartCheckIn”
      android:layout_width=“match_parent”
      android:layout_height=“200dp”
      android:layout_marginEnd=“@dimen/spacing_normal”
      android:layout_marginLeft=“@dimen/spacing_normal”
      android:layout_marginRight=“@dimen/spacing_normal”
      android:layout_marginStart=“@dimen/spacing_normal” />

  <TextView
      android:layout_width=“wrap_content”
      android:layout_height=“wrap_content”
      android:layout_gravity=“center”
      android:layout_marginBottom=“8dp”
      android:layout_marginTop=“8dp”
      android:text=“@string/check_in_time”
      android:textSize=“10sp” />

  <LinearLayout
      android:layout_width=“match_parent”
      android:layout_height=“wrap_content”
      android:orientation=“vertical”>

      <FrameLayout
          android:layout_width=“match_parent”
          android:layout_height=“1dp”
          android:background=“@color/color_shadow” />

      <FrameLayout
          android:layout_width=“match_parent”
          android:layout_height=“@dimen/spacing_small”
          android:background=“@color/color_bottom_surface” />
  </LinearLayout>
</LinearLayout>
  • Now the a method loadCheckIn( )  chart needs to added to the EventsDashboardPresenter. This is called from the EventsDashboardFragment. The loadDataCheckIn( ) is created in the ChartAnalyzer class. We pass getId( ) as the parameter.
private void loadCheckInTimesChart() {
  chartAnalyser.showChart(getView().getCheckinTimeChartView());
  chartAnalyser.loadDataCheckIn(getId())
      .compose(disposeCompletable(getDisposable()))
      .subscribe(() -> {
          getView().showChartCheckIn(true);
          chartAnalyser.showChart(getView().getCheckinTimeChartView());
      }, throwable -> getView().showChartCheckIn(false));
}
  • Now we add the method loadDataCheckIn( ) in the ChartAnalyzer class. This method returns a Completable and takes eventId as the single parameter.
public Completable loadDataCheckIn(long eventId) {
  clearData();
  isCheckinChart = true;
  return getAttendeeSource(eventId).doOnNext(attendee -> {
     String checkInTime = attendee.getCheckinTimes();
     int length = checkInTime.split(“,”).length;
     String latestCheckInTime = checkInTime.split(“,”)[length – 1];
     error = checkInTime == null ? true : false;
     addDataPointForCheckIn(checkInTimeMap, latestCheckInTime);
  })
    .toList()
    .doAfterSuccess(attendees -> this.attendees = attendees)
    .toCompletable()
    .doOnComplete(() -> {
        if (error)
            throw new IllegalAccessException(“No checkin’s found”);
        checkInDataSet = setDataForCheckIn(checkInTimeMap, “check-in time”);
        prepare();
    });
}

It calls the getAttendeeSource( ) which further gives a call to the method getAttendees( ) from the AttendeeRepository. All the inormation related to the attendees is returned from which the check-in times is extracted. The check-in times are returned in comma separated form and hence we need to extract the first element of the sequence.

private Observable<Attendee> getAttendeeSource(long eventId) {
  if (attendees == null || attendees.isEmpty())
      return attendeeRepository.getAttendees(eventId, false);
  else
      return Observable.fromIterable(attendees);
}
  • After the success of loading the attendees, the method addDataPointForCheckIn is called. We call it by inserting the parameters Map<Integer, Long> and the dateString which we had passed from the loadDataCheckIn( ). Following is the code for it. A map is created out of the data. The key in the map is the time and value is the number of people who have checked-in at that time.
private void addDataPointForCheckIn(Map<Integer, Long> map, String dateString) {
  int hour = DateUtils.getDate(dateString).getHour();
  Long numberOfCheckins = map.get(hour);

  if (numberOfCheckins == null)
      numberOfCheckins = 0L;

  map.put(hour, ++numberOfCheckins);
}
  • After the map is created it is passed on to the setDataForCheckIn( ) and the label is provided as “check-in times”. Following is the code for setDataForCheckIn( ). All the values of the map are parsed and a new entry object is made in which the value of the key and value pairs are passed. This object is then added to the ArrayList.
private LineDataSet setDataForCheckIn(Map<Integer, Long> map, String label) throws ParseException {
  List<Entry> entries = new ArrayList<>();
  for (Map.Entry<Integer, Long> entry : map.entrySet()) {
      entries.add(new Entry(entry.getKey(), entry.getValue()));
  }
  Collections.sort(entries, new EntryXComparator());

  // Add a starting point 2 hrs ago
  entries.add(0, new Entry(entries.get(0).getX() – 2, 0));
  return new LineDataSet(entries, label);
}
  • The object LineDataSet is returned with all the entries stored in the ArrayList. Now the prepare( ) is called. It is in this method that we add the code for the UI of the chart.
private void prepare() {
  if (isCheckinChart) {
      initializeLineSet(checkInDataSet, R.color.light_blue_500, R.color.light_blue_100);
      lineData.addDataSet(checkInDataSet);
  } else {
      initializeLineSet(freeSet, R.color.light_blue_500, R.color.light_blue_100);
      initializeLineSet(paidSet, R.color.purple_500, R.color.purple_100);
      initializeLineSet(donationSet, R.color.red_500, R.color.red_100);
      lineData.addDataSet(freeSet);
      lineData.addDataSet(paidSet);
      lineData.addDataSet(donationSet);
      lineData.setDrawValues(false);
  }
lineData.setDrawValues(false);
}

initializeLineSet( ) is the method where we add the color which will be used for plotting the data set.In our case the color is blue.

  • We also need to plot the time stamps in the X-axis. Unfortunately MPAndroidCharts doesn’t have a functionality for that. So to handle it an inner class MyMyAxisValueFormatter is created which extends IAxisValueFormatter. Following is the code for it.
public class MyAxisValueFormatter implements IAxisValueFormatter {

  @Override
  public String getFormattedValue(float value, AxisBase axis) {
      if (value < 0)
          return values[24 + (int) value];
      return values[(int) value];
  }
}

The values array list consists of the time stamps that will be present on the X-Axis.

String[] values = new String[] {“00:00”, “1:00”, “2:00”, “3:00”, “4:00”, “5:00”, “6:00”, “7:00”, “8:00”, “9:00”, “10:00”, “11:00”, “12:00”, “13:00”, “14:00”, “15:00”, “16:00”, “17:00”, “18:00”, “19:00”, “20:00”, “21:00”, “22:00”, “23:00”};
  • Finally the showChart( ) is called in which we specify details regarding the grid color, legend, visibility of X and Y axis etc. We also specify the animation that needs to be done whenever the chart is on screen.

 

public void showChart(LineChart lineChart) {
  lineChart.setData(lineData);
  lineChart.getXAxis().setEnabled(true);
  lineChart.getAxisRight().setEnabled(false);
  lineChart.getDescription().setEnabled(false);
  lineChart.getLegend().setEnabled(false);

  YAxis yAxis = lineChart.getAxisLeft();
  yAxis.setGridLineWidth(1);
  yAxis.setGridColor(Color.parseColor(“#992ecc71”));
  if (!isCheckinChart)
      if (maxTicketSale > TICKET_SALE_THRESHOLD)
          yAxis.setGranularity(maxTicketSale / TICKET_SALE_THRESHOLD);
  else {
      XAxis xAxis = lineChart.getXAxis();
      xAxis.setValueFormatter(new MyAxisValueFormatter());
      yAxis.setGranularity(1);
      lineChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
      lineChart.getXAxis().setGranularity(1f);
  }

  Description description = new Description();
  description.setText(“”);
  lineChart.setDescription(description);
  lineChart.animateY(1000);
}

 

Continue ReadingImplementing Check-in time chart in Orga App

Implementing Horizontal Stepper In Open Event Orga App

Currently while event creation the user has to fill a form, all of which is present on a single screen. This process is a bit cumbersome. To improve the user interface and make the app more interactive, a horizontal stepper is implemented in the app. Implementing this would also make it consistent with the frontend project. Horizontal stepper actually divides the current fragment into 3 different fragments and the contents are distributed over all of them. The user can navigate through these fragments either by swiping or by using NEXT and PREV button which are present on the screen.

To implement this the following steps are taken:

  • I am using the following library in the app. This is added to the build.gradle file and sync is done.
//stepper
implementation ‘com.github.badoualy:stepper-indicator:1.0.7’
  • Now changes need to be made to the CreateEventActivity which acts as the base activity for the 3 fragments which we will be implementing later. We hover over to the activity_create.xml where we will add the Stepper tag and the viewpager. Following code is written there.
<android.support.v4.view.ViewPager
  android:id=“@+id/pager”
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  android:layout_alignParentLeft=“true”
  android:layout_alignParentStart=“true”
  android:layout_alignParentTop=“true”
  android:layout_marginTop=“100dp” />

<com.badoualy.stepperindicator.StepperIndicator
  android:id=“@+id/stepper_indicator”
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  android:layout_marginLeft=“16dp”
  android:layout_marginRight=“16dp”
  android:layout_marginTop=“32dp”
  app:stpi_animDuration=“200”
  app:stpi_circleColor=“@color/blue_200”
  app:stpi_circleRadius=“10dp”
  app:stpi_indicatorColor=“@color/green_500”
  app:stpi_labels=“@array/stepLabels”
  app:stpi_showDoneIcon=“true” />
  • To add the NEXT, PREVIOUS and SUBMIT buttons on the screen for navigation we will add the following code.
<LinearLayout
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  android:layout_gravity=“bottom”
  android:orientation=“horizontal”
  android:background=“@color/color_accent”>

  <Button
      android:id=“@+id/btn_prev”
      android:layout_width=“0dp”
      android:layout_weight=“0.5”
      android:layout_height=“wrap_content”
      android:background=“@color/color_accent”
      android:text=“Previous”
      android:layout_gravity=“bottom|start”
      android:textColor=“@android:color/white” />

  <Button
      android:id=“@+id/btn_next”
      android:layout_width=“0dp”
      android:layout_height=“wrap_content”
      android:background=“@color/color_accent”
      android:text=“Next”
      android:layout_weight=“0.5”
      android:layout_gravity=“bottom”
      android:textColor=“@android:color/white” />

  <Button
      android:id=“@+id/btn_submit”
      android:layout_width=“0dp”
      android:layout_height=“wrap_content”
      android:background=“@color/color_accent”
      android:text=“Create”
      android:layout_weight=“0.5”
      android:visibility=“gone”
      android:layout_gravity=“bottom”
      android:textColor=“@android:color/white” />

</LinearLayout>
  • A pager adapter class is made which extends the FragmentPagerAdapter. This class handles the position of the adapter and the fragments which need to be displayed at each step. All this is handled in the getItem( ) method.
public class PagerAdapter extends FragmentPagerAdapter {
  public PagerAdapter(FragmentManager fm) {
      super(fm);
  }

  @Override
  public Fragment getItem(int position) {
      switch (position) {
          case 0:
              return EventDetailsStepOne.newInstance();
          case 1:
              return EventDetailsStepTwo.newInstance();
          case 2:
              return EventDetailsStepThree.newInstance();
          default:
              return null;
      }
  }

  @Override
  public int getCount() {
      return 3;
  }
}
  • To link stepper indicator with the view pager the following code is added
assert pager != null;
pager.setAdapter(new PagerAdapter(getSupportFragmentManager()));

indicator.setViewPager(pager, pager.getAdapter().getCount());
  • A page listener is added to the viewpager so that the visibility of the buttons can be handled easily.
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
      if (position == 0) {
          btnPrev.setVisibility(View.GONE);
          btnNext.setVisibility(View.VISIBLE);
          btnSubmit.setVisibility(View.GONE);
      } else if (position == 1) {
          btnPrev.setVisibility(View.VISIBLE);
          btnNext.setVisibility(View.VISIBLE);
          btnSubmit.setVisibility(View.GONE);
      } else if (position == 2) {
          btnPrev.setVisibility(View.VISIBLE);
          btnNext.setVisibility(View.GONE);
          btnSubmit.setVisibility(View.VISIBLE);
      }
  }
  • Now 3 different fragments are created along with their layouts and a SharedViewModel is also added which is shared by the fragments. SharedViewModel is different from a normal ViewModel where we need to provide the Activity context in the ViewModelProviders.of(context).

 

  • The current CreateEventFragment is divided into 3 fragments namely EventDetailsStepOne, EventDetailsStepTwo and EventDetailsStepThree and the code for all of them is redistributed to all of these fragments. So each and every fragment now has a different responsibility which is handled separately.

 

  • Now the current CreateEventFragment is changed to UpdateEventFragment. This will now only handle the editing of events when the Edit Event is selected.

Resources:

  1. Library for implementing horizontal stepper

https://github.com/badoualy/stepper-indicator

Continue ReadingImplementing Horizontal Stepper In Open Event Orga App