Change Password Feature for Open Event Android Organizer App

In Open Event Organizer Android App, the users were able to successfully login and sign up but in case they wanted to change their login password they could not. So, we added a feature to allow users to change their existing password. This blog explains the technical details to implement this feature following MVVM architecture and using highly efficient libraries like Retrofit, RxJava, Raziz Labs DbFlow, Data Binding.

Specifications

We will implement a page where users can enter their old password and new password along with a confirm password field. Their will be a login button to send the password change request to server. Server then return a response and we will provide feedback regarding the request. We are following MVP architecture so there will be a Model class, Fragment class, Presenter class and Network Layer to make network requests.

Let’s start with creating ChangePassword model class. There are three fields to store old password, new password and new confirmed password. Several Lombok annotations like @Data, @AllArgsConstructor, @NoArgsConstructor are used to avoid boilerplate code for getters, setters and constructors. @JsonNaming annotation is used to translate the Java Object names to KebabCase when they are serialized.

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
public class ChangePassword {

public String oldPassword;
public String newPassword;

@JsonIgnore
public String confirmNewPassword;
}

The layout file is binded to model using Data Binding. There will be three TextInputEditText fields for user input. An option to toggle password visibility and a login button.

The Fragment class binds layout file to the Fragment and handle UI stuff. Presenter is called to make Login request when login button is pressed.

public class ChangePasswordFragment extends BaseFragment<ChangePasswordPresenter> implements ChangePasswordView {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.change_password_fragment, container, false);
validator = new Validator(binding);

AppCompatActivity activity = ((AppCompatActivity) getActivity());
activity.setSupportActionBar(binding.toolbar);

ActionBar actionBar = activity.getSupportActionBar();
if (actionBar != null) {
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}

return binding.getRoot();
}

@Override
public void onStart() {
super.onStart();
getPresenter().attach(this);
binding.setOrganizerPassword(getPresenter().getChangePasswordObject());
getPresenter().start();

binding.btnChangePassword.setOnClickListener(view -> {
if (!validator.validate())
return;

String url = binding.url.baseUrl.getText().toString().trim();
getPresenter().setBaseUrl(url, binding.url.overrideUrl.isChecked());
getPresenter().changePasswordRequest(binding.oldPassword.getText().toString(),
binding.newPassword.getText().toString(),
binding.confirmNewPassword.getText().toString());

});
}

When the Login button is pressed, changePasswordRequest() method is called which makes an asynchronous call to ChangePasswordModel in order to perform the task of sending and receiving data from network in a different thread than the UI thread. Along with making requests, this method also verifies the password typed in confirm password field and send the the error as well as success message to the fragment.

public class ChangePasswordPresenter extends AbstractBasePresenter<ChangePasswordView> {

public void changePasswordRequest(String oldPassword, String newPassword, String confirmPassword) {
if (!newPassword.equals(confirmPassword)) {
getView().showError(“Passwords Do Not Match”);
return;
}

organizerPasswordObject.setOldPassword(oldPassword);
organizerPasswordObject.setNewPassword(newPassword);
organizerPasswordObject.setConfirmNewPassword(confirmPassword);

changePasswordModel.changePassword(organizerPasswordObject)
.compose(disposeCompletable(getDisposable()))
.compose(progressiveErroneousCompletable(getView()))
.subscribe(() -> getView().onSuccess(“Password Changed Successfully”), Logger::logError);
}
}

We are using Retrofit to make POST Request to server using the REST API. @Body annotation denotes the object request body which here contains a Map<String, ChangePassword> object. The Response from server is captured in Observable<ChangePasswordResponse> which is an RxJava Observable.

@POST(“auth/change-password”)
Observable<ChangePasswordResponse> changePassword(@Body Map<String, ChangePassword> changePassword);

This is the declaration for the method in Network Layer where the actual network request is made. It takes as input the changePassword object from Presenter which is already binded with data. Then it uses RxJava to asynchronously call the Api class and pass in the Map<String, ChangePassword> object. The result is then processed and Completable object is returned to the presenter. The Presenter processes the Completable object and shows user feedback in the form of a message in SnackBar.

References

  1. Official documentation for RxJava by ReactiveX https://github.com/ReactiveX/RxJava
  2. Official documentation for Retrofit by Square Inc https://github.com/square/retrofit
  3. Codebase for Open Event Organizer App on Github https://github.com/fossasia/open-event-orga-app
  4. Open Event Server deployment at heroku https://open-event-api-dev.herokuapp.com/
Continue Reading

Create Session in Open Event Android Organizer Application

Open Event Android Organizer Application offered variety of features to Organizers from all over the world to help them host their Events globally but it didn’t had the functionality to create Sessions in the app itself and associate it to Tracks. This feature addition was crucial since it enables Organizer to create Sessions which every common person enquires about before attending and event. In this Blog Post we will see how we added this functionality in the app.

Open Event Android Organizer Application is a client for Open Event Server which provides the REST API.

Problem

There can be various Sessions associated with Tracks for an Event. Open Event API had the endpoint to implement Creating Session but the Orga app didn’t, so we worked on creating a Session in the app.

The UI for creating a Session is shown above. User can fill in the necessary details and click on the green Floating Action Button to create a Session.

How to implement functionality?

We will follow MVP Architecture and use Retrofit 2.x, RxJava, Dagger, Jackson, Data Binding and other industry standard libraries to implement this functionality.

Firstly, let’s create Session model class specifying the attributes and relationships to set up in database using RazizLabs DbFlow library. The POJO will be serialized into JSON by Jackson library to be passed on as a part of RequestBody to server.

Now we will create SessionApi class that will contain the request details to be passed to Retrofit. @POST denotes a POST request and @Body denotes the requestBody of the request which is a Session object.

public interface SessionApi {
@POST(“sessions”)
Observable<Session> postSession(@Body Session session);
}

This is the CreateSessionFragment class that contains the code binding model to the view. The Fragment class implements the CreateSessionView class overriding the method declarations present there. The @Inject annotation of Dagger is used to load singleton presenter instance lazily to improve app’s performance.

Event-Id and Track-Id’s are retrieved from Bundle from Fragment Transaction. These are then passed on to presenter when Create Session button is pressed. There are other methods to show binding progressbar, snackbar and other UI components to show progress of the background request to server and database.

public class CreateSessionFragment extends BaseFragment<CreateSessionPresenter> implements CreateSessionView {

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup

container, @Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater,

R.layout.session_create_layout, container, false);
validator = new Validator(binding.form);

binding.sessionCreate.setOnClickListener(view -> {
if (validator.validate()) {
getPresenter().createSession(trackId, eventId);
}
});

return binding.getRoot();
}

@Override
public void onStart() {
super.onStart();
getPresenter().attach(this);
binding.setSession(getPresenter().getSession());
}
}

In the Presenter createSession method is called when create button is pressed in UI. The method attaches track-id and event-id to Session object. This is necessary for Relationship constraints on Session Model. Then after binding all the data to Session object, we pass it on to SessionRepository. The success response is provided to user by passing success response in getView().onSuccess() method.

public class CreateSessionPresenter extends

AbstractBasePresenter<CreateSessionView> {

public Session getSession() {
return session;
}

public void createSession(long trackId, long eventId) {

Track track = new Track();
Event event = new Event();

track.setId(trackId);
event.setId(eventId);
session.setTrack(track);
session.setEvent(event);

sessionRepository
.createSession(session)
.compose(dispose(getDisposable()))
.compose(progressiveErroneous(getView()))
.subscribe(createdSession ->

getView().onSuccess(“Session Created”), Logger::logError);
}
}

The SessionRepository uses RxJava to make asynchronous Retrofit Call to Server. We throw a Network Error to user if the device does not have Internet Connectivity.

The session object accepted as a parameter in createSession method is passed on to sessionApi. It will return Observable<Session> Response which we will process in doOnNext() method. Then the Session object along with required foreign key relationships with Track and Event is saved in database for offline use.

@Override
public Observable<Session> createSession(Session session) {
if (!repository.isConnected()) {
return Observable.error(new Throwable(Constants.NO_NETWORK));
}return sessionApi
.postSession(session)
.doOnNext(created -> {
created.setTrack(session.getTrack());
created.setEvent(session.getEvent());
repository
.save(Session.class, created)
.subscribe();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

The above code snippets are from Open Event Orga Application. For exploring the entire codebase please refer here. For details about the REST API used by the app please visit here.

References

  1. Official RxJava Project on Github by ReactiveX https://github.com/ReactiveX/RxJava.
  2. Official Retrofit Project on Github by Square Inc https://github.com/square/retrofit.
  3. Official Open Event Organizer App on Github by FOSSASIA https://github.com/fossasia/open-event-orga-app.
  4. Documentation for REST API of Open Event Server on Heroku by FOSSASIA https://open-event-api-dev.herokuapp.com/.
Continue Reading
Close Menu