Adding Dredd Tests for Image Sizes on Open Event Flask Server

In this blog, we will talk about how to add dredd hooks for testing the API of Event Image Sizes and Speaker Image Sizes in Open Event Server. The focus is on adding the factory class and dredd hooks of these APIs using factory-boy python library and Dredd API testing framework.

Factory Creation

For the Event and Speaker Image Sizes, we’ll make our factory classes EventImageSizeFactory  and SpeakerImageSizeFactory as follows

Now, let’s try to understand this class.

In this class, we are writing the sample data two records of ImageSizes Model, these records corresponds to Event and Speaker Image Sizes.

  1. First of all, we inherit class factory.alchemy.SQLAlchemyModelFactory to build our sample data which for Image Sizes.
  2. Class Meta has model and sqlalchemy_session attributes. Model tells the factory class of to which model this factory class push the data to database and sqlalchemy_session is assigned with the current database session.
  3. Next, we add the attributes according to the model and Schema of Image Sizes.

Adding Dredd Hooks

For the ImageSizes, we’ll make our dredd hooks as follows

Now, let’s try to understand these tests.

In this tests, we check the API by matching the response after adding a record in these API to one which is present at API blueprint.

  1. First of all, we use decorator @hooks.before which means we first add a record in the database and then match the response we get from API say /v1/event-image-sizes with the response mentioned at Image Size > Event Image Size Details > Get Event Image Size Details in API blueprint.
  2. We create an instance of EventImageSizeFactory which is a record of model Image Sizes.
  3. This record is then returned as a response of API /v1/event-image-sizes and matches with the blueprint at Image Size > Event Image Size Details > Get Event Image Size Details

Similarly, we have added other dredd tests for PATCH method as well.

So, we saw how factory-boy python library and Dredd API testing framework helped us in testing the REST APIs on Open Event Server.

Resources

Continue ReadingAdding Dredd Tests for Image Sizes on Open Event Flask Server

Events API Integration on Admin User Route Open Event Frontend

This blog article will illustrate how the Events API has been integrated into the admin users route  Open Event Frontend, as well as how the action buttons are added to view, edit or delete the events of any user in the list by the admin.

To make the events user link in the user link column of the users table functional a new sub route is added to the app’s user route as follows:

this.route('users', function() {
     this.route('view', { path: '/:user_id' }, function() {
       this.route('events', function() {
         this.route('list', { path: '/:event_status' });
       });
     });

The newly added route further contains a dynamic sub route called list. This nested route fulfills the requirement of filtering the various events of a given user according to their states. Interestingly, the routes admin/users/view and admin/users/list are both dynamic and expect a parameter after /users/ hence, the app cannot distinguish between them on it’s own, thus explicit handling of the dynamic parameter of the routes was implemented, differentiating them on the basis of the route’s state as follows:

beforeModel(transition) {
this._super(...arguments);
const userState = transition.params[transition.targetName].users_status;
if (!['all', 'deleted', 'active'].includes(userState)) {
this.replaceWith('admin.users.view', userState);
}
}

Thus if the dynamic portion of the route doesn’t contain the parameters all, deleted or active, then it must be referring to a user’s events or sessions and the route needs to be replaced with the desired events or sessions route accordingly.

The server is queried to fetch the events of a given user by making use of the hasMany relationship a user has with his sessions. They are loaded in the route admin/users/view/events/list.js

model() {
const userDetails = this.modelFor('admin.users.view');
return this.store.findRecord('user', userDetails.id, {
include: 'events'
});

After fetching the the events from the server, a proper ember table is called in the template file of this route, and all the actions like viewing and editing an event are declared in the template.

{{events/events-table
columns=columns data=model.events
useNumericPagination=true
moveToDetails=(action 'moveToDetails')
editEvent=(action 'editEvent')
openDeleteEventModal=(action 'openDeleteEventModal')
}}

In the controller the columns of the table for events are defined and all the actions are defined.

moveToDetails(id) {
this.transitionToRoute('events.view', id);
},
editEvent(id) {
this.transitionToRoute('events.view.edit.basic-details', id);
},
deleteEvent() {
this.set('isLoading', true);
this.store.findRecord('event', this.get('eventId'), { backgroundReload: false }).then(function(event) {
event.destroyRecord();
})

So, the admin can view the list of the events of a particular user and send a patch or delete request for any event.

Resources

Continue ReadingEvents API Integration on Admin User Route Open Event Frontend

Adding Event Roles Permission API on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. Event managers can create invitation forms for speakers and build schedules in a drag and drop interface. The event information is stored in a database. The system provides API endpoints to fetch the data, and to modify and update it.

The Open Event Server is based on JSON 1.0 Specification and hence build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema).

In this blog, we will talk about how to add API for accessing and updating the events role permissions on Open Event Server. The focus is on Schema creation and it’s API creation.

Schema Creation

For the Events Role Permission, we’ll make our Schema as follows

 

Now, let’s try to understand this Schema.

In this feature, we are providing Admin the rights to get and update the permission given to a role concerning a service.

  1. First of all, we are provide the four fields in this Schema, which are can_create, can_read, can_update and can_delete which are Boolean.
  2. All these fields gives us idea whether a user with a role can create, read, update and delete a service or not respectively in the whole system.
  3. Next there is a relationship with role which is one of organizer, coorganizer, track_organizer, moderator, registrar or attendee.
  4. Next there is a relationship with service which is one of Track, Microlocation, Session, Speaker or Sponsor.

API Creation

For the Events Role Permissions, we’ll make our API as follows

Now, let’s try to understand this API.

In this feature, we are providing Admin the rights to get and update the permission given to a role concerning a service.

  1. First of all, there is the need to know that this API has two method GET and PATCH.
  2. Decorators shows us that only Admin has permissions to access PATCH method for this API i.e. only Admins can modify the events role permissions .
  3. In EventsRolePermissionList, we are inheriting ResourceList from Flask Rest JSONAPI which will allow us to get all the records for the model Permission.
  4. In EventsRolePermissionDetail, we are inheriting ResourceDetail from Flask Rest JSONAPI which will allow us to get and update attributes of a record of model Permission.
  5. In EventsRolePermissionRelationship, we are inheriting ResourceRelationship from Flask Rest JSONAPI which will allow us to get and update relationships of a record of model Permission.

So, we saw how Events Role Permission Schema and API is created to allow users to get it’s values and Admin users to modify it’s attributes and relationships.

Resources

Continue ReadingAdding Event Roles Permission API on Open Event Server

Adding a list view for the Sessions Public Page in Open Event Frontend

This blog article will describe how the sessions are listed in the public pages of an event in Open Event Frontend, which allows the user to view all the sessions of an event. The sessions are filtered as per date. The primary end point of Open Event API with which we are concerned with for fetching the the users details is GET /v1/events/{event_identifier}/sessions

The route of the public page fetches all the sessions of a particular events and filters them as per the criteria selected by the user. The user can view the sessions of a particular day, week or month. The user can also view the list of all the sessions. The query written in the route is:

async model(params) {
   const eventDetails = this.modelFor('public');
   let sessions =  null;
   if (params.session_status === 'today') {
     sessions = await this.get('store').query('session', {
       filter: [
         {
           and: [
             {
               name : 'event',
               op : 'has',
               val : {
                 name : 'identifier',
                 op : 'eq',
                 val : eventDetails.id
               }
             },
             {
               name : 'starts-at',
               op : 'ge',
               val : moment().startOf('day').toISOString()
             },
             {
               name : 'starts-at',
               op : 'lt',
               val : moment().endOf('day').toISOString()
             }
           ]
         }
       ]
     });
   } else {
     sessions = await this.get('store').query('session', {
       filter: [
         {
           name : 'event',
           op : 'has',
           val : {
             name : 'identifier',
             op : 'eq',
             val : eventDetails.id
           }
         }
       ]
     });
   }
   return {
     event  : eventDetails,
     session : sessions
   };
 }

The view route is located at app/e/{event_identifier}/sessions/all. This route will show all the sessions of the selected event. Similarly /week will show the sessions of a week and /month will show the sessions of a month.Four joint buttons are used in the UI of the public page to redirect to these routes.

To list the sessions ember component of session cards is used to include a session in a card with the details of the session like the time, abstract etc and also the session’s track and the details of the speakers like the name, information and social media accounts. In the template of the route this component is called and used in the UI within an ember component. In case there are no sessions that exist between a given time period, a helper text is displayed stating “No sessions exist for the given period”.

class="ui buttons"> {{#link-to 'public.sessions.list' model.event.id 'all' class="ui button"}}{{t 'All'}}{{/link-to}} {{#link-to 'public.sessions.list' model.event.id 'today' class="ui button"}}{{t 'Today'}}{{/link-to}} {{#link-to 'public.sessions.list' model.event.id 'week' class="ui button"}}{{t 'Week'}}{{/link-to}} {{#link-to 'public.sessions.list' model.event.id 'month' class="ui button"}}{{t 'Month'}}{{/link-to}}
class="ui raised very padded text container segment"> {{#each model.session as |session|}} {{public/session-item session=session}} {{else}}
class="ui disabled header">{{t 'No Sessions exist for this time period'}}
{{/each}} </div>

Resources

Continue ReadingAdding a list view for the Sessions Public Page in Open Event Frontend

Add Routes to Add and Edit multiple Sessions in the CFS section of Open Event Frontend

This blog article will describe how the users can add multiple session proposals and edit them through the Call for Speakers modal in Open Event Frontend. The logged in user first adds himself as the speaker through the modal then he can add multiple sessions. The user will be added as the speaker for all the sessions he adds through the CFS modal.

To submit the sessions the user first has to add himself as a speaker of that event in the route:
e/{event_identifier}/cfs

After the user registers himself as the speaker of that event whose Call for Speakers is open he can add multiple sessions and also edit those sessions.

When Add Session Details button is clicked the user gets redirected to a form with the route

e/{event_identifier}/cfs/new-session.

async model() {
const eventDetails = this.modelFor('public');
return {
  event : eventDetails,
  forms : await eventDetails.query('customForms', {
    sort        : 'id',
    'page[size]' : 50
  }),
  session: await this.get('store').createRecord('session', {
    event   : eventDetails,
    creator : this.get('authManager.currentUser')
  }),
  tracks       : await eventDetails.query('tracks', {}),
  sessionTypes : await eventDetails.query('sessionTypes', {})
}

On this route there is a session form where the user can add details like title, short abstract, comments, track etc. Once he clicks on the save button after entering the details post request is sent to the server and that session is added to the list of sessions of that event and the user is added as the speaker of that session.

class="ui container"> {{#if speaker.id}} {{forms/session-speaker-form fields=model.forms data=model isLoading=isLoading save=(action 'save' speaker) isSession=true includeSession=true}} {{/if}}

The user can add another session or edit the sessions previously entered by him. When Edit session is clicked the user gets redirected to the route e/{event_identifier}/cfs/edit/{session_id}

async model(params) {
const eventDetails = this.modelFor('public');
return {
event : eventDetails,
forms : await eventDetails.query('customForms', {
sort        : 'id',
'page[size]' : 50
}),
session: await this.get('store').findRecord('session', params.session_id, {
include: 'session-type,track'
})
};
}

On this route the user can change the details of the session he had entered before. On clicking save a patch request is sent to the server and the new details are saved.

Resources

Continue ReadingAdd Routes to Add and Edit multiple Sessions in the CFS section of Open Event Frontend

Building the API of Speaker Image Size on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues.It uses the JSON 1.0 Specification and build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add API for accessing and updating the Speaker Image Size on Open Event Server. The focus is on its API creation.

API Creation

For the SpeakerImageSizeDetail, we’ll make our Schema as follows

Now, let’s try to understand SpeakerImageSizeDetail.

In this feature, we are providing Admin the rights to Get and Update the SpeakerImageSizes

  1. kwargs[‘id’] = 2 states that Image Size model has 2 records and 1st record is used for Event Image Size and 2nd record is used for Speaker Image Size.
  2. decorators = (api.has_permission(‘is_admin’, methods=”PATCH”, id=”2″),) states that for Speaker Image Size, Update API is accessible to Admins only.
  3. methods = [‘GET’, ‘PATCH’] states that this API provides two methods i.e. GET and PATCH.
  4. schema = SpeakerImageSizeSchema states that the schema which is used to return the response is Speaker Image Size Schema.
  5. data_layer = {‘session’: db.session, ‘model’: ImageSizes} states the session and Model used to fetch the records.

Resources

Continue ReadingBuilding the API of Speaker Image Size on Open Event Server

Adding Functionality to Switch between List and Grid View of the Skill Cards on SUSI.AI CMS

In this blog post, we are going to understand the implementation of the functionality that enables the user to switch between the List View and the Grid View UI for the skill cards that is displayed on various routes of the SUSI Skill CMS Web-App. Let us go through the implementation in the blog –

Working of the feature

Going through the implementation

  • The UI for implementing the switching of views was achieved via the use of RadioButtonGroup component of the Material-UI library for React.
  • The type of view currently being shown was stored in the component state of the BrowseSkill component as viewType, whose default value is set to list, indicating that the skills are firstly shown in a List View.

.
.

export default class BrowseSkill extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        .
        …..
        viewType: 'list',
        ….
    };
  }
….
}

 

  • The RadioButtonGroup component has 2 child components, for each view. The child component that is to be used is RadioButton.
  • The props passed in the RadioButtonGroup are –
    • name : It is the name given to the component.
    • defaultSelected : It is the default view type.
    • style : It contains the style object of the UI.
    • valueSelected : It is set to the state variable assigned for storing view type.
    • onChange : It is the handler which executes, when the radio buttons are clicked.
  • The style for the desktop view and mobile view is different depending on the screen size and is follows –

//Mobile view
Style {
    right: 12,
    position: 'absolute',
    top: 216,
    display: 'flex',
}

//Desktop view
style={
    display: 'flex',
    marginTop: 34
}

 

  • The props passed in the RadioButton are –
    • value : The value stored in the state, that is responsible for the view type. The values for List and Grid view are list and grid respectively.
    • label : The label for the RadioButton.
    • labelStyle : The style object for the label.
    • checkedIcon : The icon used in the checked state.
    • uncheckedIcon : The icon used in the unchecked state.

UI of the Radio Buttons

  • The onClick handler of the radio buttons is –

handleViewChange = (event, value) => {
    this.setState({ viewType: value });
};

 

  • The code snippet for the UI implementation, written inside the render function is as follows :

….
<RadioButtonGroup
  name="view_type"
  defaultSelected="list"
  style={
    window.innerWidth < 430
      ? {
          right: 12,
          position: 'absolute',
          top: 216,
          display: 'flex',
        }
      : { display: 'flex', marginTop: 34 }
  }
  valueSelected={this.state.viewType}
  onChange={this.handleViewChange}
>
  <RadioButton
    value="list"
    label="List view"
    labelStyle={{ display: 'none' }}
    style={{ width: 'fit-content' }}
    checkedIcon={
      <ActionViewStream style={{ fill: '#4285f4' }} />
    }
    uncheckedIcon={<ActionViewStream />}
  />
  <RadioButton
    value="grid"
    label="Grid view"
    labelStyle={{ display: 'none' }}
    style={{ width: 'fit-content' }}
    checkedIcon={
      <ActionViewModule style={{ fill: '#4285f4' }} />
    }
    uncheckedIcon={<ActionViewModule />}
  />
</RadioButtonGroup>
….

 

I hope the implementation of the switching between Views would be clear after going through the blog and proved to be helpful for your understanding.

References

Continue ReadingAdding Functionality to Switch between List and Grid View of the Skill Cards on SUSI.AI CMS

Implementing ProGuard in Open Event Orga App

In the Open Event Orga App there has been an issue with the size of the app. So to decrease the size of the app, we had to enable Proguard. By implementing proguard the size of the app has reduced from 5.9 Mb to 2.9 Mb. The following article shows the steps taken to implement Proguard.

  • Firstly the following parameters need to set to true in the the app level build.gradle file.
  release {
      minifyEnabled true
      shrinkResources true
      proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’

The built-in shrinker in minify enabled removes the dead code. It doesn’t obfuscate or optimizes the code. In this section of code, we also set the proguard-rules.pro file in which all the rules regarding code obfuscation are set. The file is a custom file unlike the default proguard-android.txt file which is already a part of Android Studio.

  • Now we head over to the proguard-rules.pro file. But first we need to understand the 2 most important terms –dontwarn and –keep class.

 

2.1 -dontwarn → When proguard is implemented, code obfuscation takes place and the name of the classes get shortened to single letters. Because of this there might be a few conflicts. But there might be some unresolved references in the obfuscated code. dontwarn ignores all these references.

2.2 –keep class → Suppose there are certain classes which shouldn’t be obfuscated then they are preceded with keep class annotation.

 

  • In the proguard-rules.pro file the following lines are added
# Platform calls Class.forName on types which do not exist on Android to determine platform.
dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
keepattributes Exceptions

The keepattributes annotation is basically used for Generics which are JAVA JDK 5 and higher. It basically prevents the code from obfuscation.

  • Now Retrofit classes also need to be obfuscated. For that we add the following lines of code. The obfuscation considerably reduces the size of the app.
-dontwarn okio.**

-dontwarn com.squareup.okhttp.**
  • The model classes need to be prevented from getting obfuscated or else it may lead to app crash. Hence these classes need to be kept and so we add the following lines of code to prevent app crash. The link for the PR which fixed the app crash cause due to buggy proguard rule.
keep class org.fossasia.openevent.app.data.** {
*;
}
  • The orga app uses the Jackson library for parsing JSON data. Proguard rules also needs to be applied for this library.
# Jackson
keepattributes *Annotation*,EnclosingMethod,Signature
keepnames class com.fasterxml.jackson.** { *; }
dontwarn com.fasterxml.jackson.databind.**
keep class org.codehaus.** { *; }
-keepclassmembers public final enum org.codehaus.jackson.annotate.JsonAutoDetect$Visibility {
  public static final org.codehaus.jackson.annotate.JsonAutoDetect$Visibility *;
}
keep class com.github.jasminb.** { *; }
  • The other libraries used in the app also have specific proguard rules. These are listed below.
# General
keepattributes SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,Signature,Exceptions,InnerClasses
keep class android.support.v7.widget.SearchView { *; }

keep class com.github.mikephil.charting.** { *; }
G
keep public class * extends com.bumptech.glide.module.AppGlideModule
keep class com.bumptech.glide.GeneratedAppGlideModuleImpl

 

References:

  1. Medium article by Jon Finerty https://medium.com/@jonfinerty/beginner-to-proguard-b3327ff3a831

Official Developer documentation  https://developer.android.com/studio/build/shrink-code

Continue ReadingImplementing ProGuard in Open Event Orga App

Add More Languages to a Skill in SUSI.AI

The SUSI SKill CMS provides skills in multiple languages. Often there are similar skills in different languages. For example, there is a News skill in English and Samachar skill in Hindi. Then why not link them together and mark one as the translation of the other. This will help the user to reach and explore the desired skill in an efficient way. Moreover, it may be easier to type ‘News’ than ‘समाचार’ and find the required skill through translations. So here it has been explained how to link two SUSI skills as translations.

Server side implementation

Create a skillSupportedLanguages.json file to store the related skills together as translations and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the language name and the skill name in that language, wrapped in an array.

public static JsonTray skillSupportedLanguages;

Path skillSupportedLanguages_per = skill_status_dir.resolve("skillSupportedLanguages.json");
Path skillSupportedLanguages_vol = skill_status_dir.resolve("skillSupportedLanguages_session.json");
skillSupportedLanguages = new JsonTray(skillSupportedLanguages_per.toFile(), skillSupportedLanguages_vol.toFile(), 1000000);
OS.protectPath(skillSupportedLanguages_per);
OS.protectPath(skillSupportedLanguages_vol);

Now create an API that accepts the skill details and translation details and stores them in the JSON file. Create UpdateSupportedLanguages.java class for the API.

Endpoint: /cms/updateSupportedLanguages.json

Minimum user role: Anonymous

Params:

  • Model
  • Group
  • Language (language of the skill for which translation is to be added)
  • Skill (name of the skill for which translation is to be added)
  • New language (translation language of the skill)
  • New skill name (name of the skill in translated language)

When a new translation is added check if it already exists in the translation group stored in the skillSupportedLanguages.json. Use the DAO object and loop over the array, check is the language name and the language name already exists. If yes then simply return.

if (!alreadyExixts) {
    groupName.put(createSupportedLanguagesArray(language_name, skill_name, new_language_name, new_skill_name));
}

Otherwise, create a new object containing the new language name and the skill name in that language and add it to the translation group.

public JSONArray createSupportedLanguagesArray(String language_name, String skill_name, String new_language_name, String new_skill_name) {
    JSONArray supportedLanguages =  new JSONArray();

    JSONObject languageObject = new JSONObject();
    languageObject.put("language", language_name);
    languageObject.put("name", skill_name);
    supportedLanguages.put(languageObject);

    JSONObject newLanguageObject = new JSONObject();
    newLanguageObject.put("language", new_language_name);
    newLanguageObject.put("name", new_skill_name);
    supportedLanguages.put(newLanguageObject);

    return supportedLanguages;
}

Add this API to SusiServer.java

// Add translation to the skill
UpdateSupportedLanguages.class

Resources

 

Continue ReadingAdd More Languages to a Skill in SUSI.AI

Using Android Architecture Components in Organizer App

In the Open Event Organizer Android App there is an issue with the Memory leaks, Data persistence on configuration changes and difficulty in managing the Activity lifecycle. So as to deal with these issues we have implemented Android Architecture Components.

The first step towards moving on with AAC’s was the conversion of a presenter class to a ViewModel class and then implementing LiveData in the refactored class.

LiveData

It is an Observable data holder. It notifies the observers whenever there is change in the data so that the UI can be updated.

Livedata is also bound to the lifecycle which means that it will be observing changes only when the activity is in started or resumed state and hence there is no chance of memory leaks or null pointer exceptions.

ViewModel

The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations.

In the following I’ll be explaining how the LoginViewModel class was made in the Orga App.

Steps

  • Creating one’s own custom ViewModelFactory. This is done so as to follow the Single Responsibility Principle. This custom class extends the ViewModelProvider.Factory and handles the creation of View Models. Adding this class also ensures that a Constructor can also get injected in the View Model class.
@Singleton
public class OrgaViewModelFactory implements ViewModelProvider.Factory {

  private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

  @Inject
  public OrgaViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
      this.creators = creators;
  }

  @NonNull
  @Override
  @SuppressWarnings({“unchecked”, “PMD.AvoidThrowingRawExceptionTypes”, “PMD.AvoidCatchingGenericException”})
  public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
      Provider<? extends ViewModel> creator = creators.get(modelClass);
      if (creator == null) {
          for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
              if (modelClass.isAssignableFrom(entry.getKey())) {
                  creator = entry.getValue();
                  break;
              }
          }
      }
      if (creator == null) {
          throw new IllegalArgumentException(“unknown model class “ + modelClass);
      }
      try {
          return (T) creator.get();
      } catch (Exception e) {
          throw new RuntimeException(e);
      }
  }
}
  • Injecting this custom ViewModelFactory into the ViewModelModule. Adding the method bindLoginViewModel with the LoginViewModel as its parameter. Always add any new ViewModel class into the ViewModelModule otherwise it might show DaggerAppComponent errors.
@Module
public abstract class ViewModelModule {

  @Binds
  @IntoMap
  @ViewModelKey(LoginViewModel.class)
  public abstract ViewModel bindLoginViewModel(LoginViewModel loginViewModel);

  @Binds
  public abstract ViewModelProvider.Factory bindViewModelFactory(OrgaViewModelFactory factory);

}
  1. Refactoring the LoginPresenter to LoginViewModel class and extending it to ViewModel.
  2. In the fragment class injecting the ViewModelProviderFactory.
@Inject
ViewModelProvider.Factory viewModelFactory;
  • Pass this parameter in the ViewModelProviders.of( ) method as follows:
loginFragmentViewModel = ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel.class);
  • Now the only task in hand is the use of LiveData in the ViewModels and observing the LiveData from the Fragments. In the following LiveData has been applied to observe the state of Progress Bar. When the login button is pressed, the value of MutableLiveData<Boolean> progress is set to true.
public void login() {
  compositeDisposable.add(loginModel.login(login)
      .doOnSubscribe(disposable -> progress.setValue(true))
      .doFinally(() -> progress.setValue(false))
      .subscribe(() -> isLoggedIn.setValue(true),
          throwable -> error.setValue(ErrorUtils.getMessage(throwable))));
}

 

  • This change in state is observed by the following code in the fragment class:
loginFragmentViewModel.getProgress().observe(this, this::showProgress);

On observing this, the showProgress is called which handles the visibility if the progress bar. Currently as the progress value was set to True, the progress bar is visible till the processing goes on.

  • Once the login takes place the progress of the LoginViewModel is set to false and the progress bar gets hidden, again which gets observed in the fragment class.

References:

https://android.jlelse.eu/android-architecture-components-livedata-1ce4ab3c0466

Continue ReadingUsing Android Architecture Components in Organizer App