Implementing Timeline for Attendees Activity in Organizer App

Open Event Organizer App offers the functionality to Checkin/checkout attendees but the Organizer was unable to view when a particular attendee was checkin or checkout. We decided to implement a feature to view the timeline of checkin/checkout for each attendee.

Let’s begin by adding the dependency in build.gradle.

implementation “com.github.vipulasri:timelineview:”1.0.6”

In the recyclerview item layout add the TimeLineView layout. Following are some of the useful attributes.

  1. app:markerInCenter – This defines the position of the round marker within the layout. Setting it to true, position it in center.
  2. app:marker – Custom drawables can be set as marker.
<com.github.vipulasri.timelineview.TimelineView
android:id=”@+id/time_marker”
android:layout_width=”wrap_content”
android:layout_height=”match_parent”
app:marker=”@drawable/ic_marker_active”
app:line=”#aaa4a4″
app:lineSize=”2dp”
app:linePadding=”3dp”
app:markerInCenter=”true”
app:markerSize=”20dp” />

The ViewHolder class will extend the RecyclerView,ViewHolder class. In the constructor, we will add a parameter viewType and then set it to TimeLine Marker layout using method initLine.

public CheckInHistoryViewHolder(CheckInHistoryLayoutBinding binding, int viewType) {
super(binding.getRoot());
this.binding = binding;
binding.timeMarker.initLine(viewType);
}

In RecyclerViewAdapter, we will override the getItemViewType() method. Here we will use the getTimeLineViewType method which takes in position and total size of the recycler view list and returns a TimeLineView type object.

@Override
public int getItemViewType(int position) {
return TimelineView.getTimeLineViewType(position, getItemCount());
}

References

  1. TimeLineView library by VipulAsri https://github.com/vipulasri/Timeline-View
  2. Android Documentation for RecyclerViewAdapter https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter
  3. Android Documentation for RecyclerViewView https://developer.android.com/reference/android/support/v7/widget/RecyclerView
Continue Reading

Testing the ViewModels in Open Event Organizer App

In Open Event Organizer Android App we follow Test Driven Development Approach which means the features added in the app are tested thoroughly by unit tests. More tests would ensure better code coverage and fewer bugs. This blog explains how to write tests for Viewmodel class in MVVM architecture.

Specifications

We will use JUnit4 to write unit tests and Mockito for creating mocks. The OrdersViewModel class returns the list of Order objects to the Fragment class. The objects are requested from OrderRepository class which fetches them from Network and Database. We will create a mock of OrderRepository class since it is out of context and contain logic that doesn’t depend on Orders Respository. Below is the getOrders method that we will test.

 public LiveData<List<Order>> getOrders(long id, boolean reload) {
if (ordersLiveData.getValue() != null && !reload)
return ordersLiveData;

compositeDisposable.add(orderRepository.getOrders(id, reload)
.compose(dispose(compositeDisposable))
.doOnSubscribe(disposable -> progress.setValue(true))
.doFinally(() -> progress.setValue(false))
.toList()
.subscribe(ordersLiveData::setValue,
throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString())));

return ordersLiveData;
}

We will be using InstantTaskExecutorRule() which is a JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. We will use setUp() method to load the RxJavaPlugins, RxAndroid plugins and reset them in tearDown method which will ensure each test runs independently from the other and avoid memory leaks. After doing this initialization and basic setup for tests we can begin code the method shouldLoadOrdersSuccessfuly() to test the getOrders method present in ViewModel class. Let’s see the step by step approach.

  1. Use Mockito.when to return Observables one by one from ORDERS_LIST whenever the method getOrders of the mock orderRepository is called.
  2. We will use Mockito.InOrder and pass orders, orderRepository and progress to check if they are called in a particular order.
  3. We will use .observeForever method to observe on LiveData objects and add a ArrayList on change.
  4. Finally, we will test and verify if the methods are called in order.
@Test
public void shouldLoadOrdersSuccessfully() {
when(orderRepository.getOrders(EVENT_ID, false))
.thenReturn(Observable.fromIterable(ORDERS_LIST));

InOrder inOrder = Mockito.inOrder(orders, orderRepository, progress);

ordersViewModel.getProgress().observeForever(progress);

orders.onChanged(new ArrayList<>());

ordersViewModel.getOrders(EVENT_ID, false);

inOrder.verify(orders).onChanged(new ArrayList<>());
inOrder.verify(orderRepository).getOrders(EVENT_ID, false);
inOrder.verify(progress).onChanged(true);
inOrder.verify(progress).onChanged(false);
}

Similar approach can be followed for writing tests to check other behaviour of the ViewModel.

References

  1. Official Documentation for testing. https://developer.android.com/reference/android/arch/core/executor/testing/InstantTaskExecutorRule
  2. Official Documentation for JUnit.  https://junit.org/junit4/
  3. Official documentation for Mockito.  http://site.mockito.org/
  4. Open Event Organizer App codebase.  https://github.com/fossasia/open-event-orga-app
Continue Reading

Showing Order Details in Eventyay Organizer Android App

In Open Event Organizer App, the organizer was not able to view the details for the Orders received from attendees for his/her events. So in this blog we’ll see how we implemented this functionality in the Orga App.

Specifications

There is a fragment showing the list of all orders for that event. The user will be able to click on order from the list which will then take the user to another fragment where Order details will be displayed. We will be following MVVM architecture to implement this functionality using REST API provided by Open Event Server. Let’s get started.

Firstly, we will create Order Model class. This contains various fields and relationship attributes to setup the table in database using RazizLabs DbFlow annotations.

Then, We will make a GET request to the server using Retrofit 2  to fetch Order object.

@GET(“orders/{identifier}?include=event”)
Observable<Order> getOrder(@Path(“identifier”) String identifier);

The server will return the Order details in form of a Order object and then we will save it in local  database so that when there is no network connectivity then also we can show data to the user and user can refresh to fetch the latest data from network. The network observable handles fetching data from network and disk observable handles saving data in local database.

@NonNull
@Override
public Observable<Order> getOrder(String orderIdentifier, boolean reload) {
Observable<Order> diskObservable = Observable.defer(() ->
repository
.getItems(Order.class, Order_Table.identifier.eq(orderIdentifier)).take(1)
);

Observable<Order> networkObservable = Observable.defer(() ->
orderApi.getOrder(orderIdentifier)
.doOnNext(order -> repository
.save(Order.class, order)
.subscribe()));

return repository
.observableOf(Order.class)
.reload(reload)
.withDiskObservable(diskObservable)
.withNetworkObservable(networkObservable)
.build();
}

Now, we will make a Fragment class that will bind the layout file to the model in the onCreateView method using DataBindingUtil. Further, we will be observing on ViewModel to reflect changes of Order, Progress and Error objects in the UI in the onStart method of the Fragment.

public class OrderDetailFragment extends BaseFragment implements OrderDetailView {

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

orderDetailViewModel = ViewModelProviders.of(this, viewModelFactory).get(OrderDetailViewModel.class);

return binding.getRoot();
}

@Override
public void onStart() {
super.onStart();
setupRefreshListener();

orderDetailViewModel.getOrder(orderIdentifier, eventId, false).observe(this, this::showOrderDetails);
orderDetailViewModel.getProgress().observe(this, this::showProgress);
orderDetailViewModel.getError().observe(this, this::showError);
}
}

Next, we will create OrderDetailsViewModel.This is the ViewModel class which interacts with the repository class to get data and the fragment class to show that data in UI.

Whenever the user opens Order details page, the method getOrder() twill be called which will request an Order object from OrderRepository, wrap it in MutableLiveData and provide it to Fragment.

Using MutableLiveData to hold the data makes the data reactive i.e. changes in UI are reflected automatically when the object changes. Further, we don’t have to worry handling the screen rotation as LIveData handles it all by itself.

  public LiveData<Order> getOrder(String identifier, long eventId, boolean reload) {
if (orderLiveData.getValue() != null && !reload)
return orderLiveData;

compositeDisposable.add(orderRepository.getOrder(identifier, reload)
.compose(dispose(compositeDisposable))
.doOnSubscribe(disposable -> progress.setValue(true))
.doFinally(() -> progress.setValue(false))
.subscribe(order -> orderLiveData.setValue(order),
throwable -> error.setValue(ErrorUtils.getMessage(throwable))));

if (!reload) {
getEvent(eventId);
}

return orderLiveData;
}
}

References

  1. Codebase for Open Event Orga App https://github.com/fossasia/open-event-orga-app
  2. Official documentation for LiveData Architecture Component https://developer.android.com/topic/libraries/architecture/livedata
  3. Official Github Repository of Retrofit  https://github.com/square/retrofit
  4. Official Github Repository for RxJava https://github.com/ReactiveX/RxJava
Continue Reading

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 Reading

Using SharedViewModel for data persistence in multiple Fragments in Open Event Organizer Android App

Google announced the new architectural pattern Model-View-View-Model (or MVVM), which has gained a lot of popularity for increased simplicity. The Open Event Organizer Android App is currently (at the time of writing this blog) using Model-View-Presenter (MVP) architecture for the most part. Only the Login module had been converted to MVVM, but it wasn’t using any Shared View Model.

Let’s look at the problem at hand:

For the Login fragment, there’s an email field for user’s email in every fragment of the auth module. But there’s the use case where users can bounce around different fragment screens in order to perform different operations like login, signup, forgot password and enter new password.

A good User Experience is at the core of every successful product. Users don’t like to spend time doing trivial things like registration and they hate to retype emails again and again.

To solve the problem, the approach that I first used was, using interfaces to tell each and every component when the fragment opens and send the email text through the interface. I spent quite some time implementing the interfaces right and then opened a PR. It was around 200 lines addition at first, and I also received 2-3 requested changes from my project mentor.

Defining the interface:

The interface  EmailIdTransfer  was defined in the  LoginFragment  as follows:

public class LoginFragment extends BaseFragment implements LoginView, EmailIdTransfer {

Using the interface:

getPresenter().getEmailId().setEmail(
((LoginFragment.EmailIdTransfer) getContext()).getEmail()

Imagine setting up the   EmailIdTransfer  interface for each Fragment, and handling their updation and reception.

As you can see, this is a bad solution for something as trivial as the problem at hand! The better solution which stuck me (from Google I/O) afterwards was to use ViewModels. This obviously wasn’t MVP, but since we had to eventually move to MVVM, I dug a little more about it and found out that there’s also a SharedViewModel.

SharedViewModel is basically a View Model that every view of a module shares. It is not very different from ViewModel.

Here’s how to set it up:

public class SharedViewModel extends ViewModel {

   private final MutableLiveData<String> email = new MutableLiveData<>();

   public void setEmail(String email) {
       this.email.setValue(email);
   }

   public LiveData<String> getEmail() {
       return email;
   }
}

The MVVM architecture uses LiveData instead of Observables.

Android LiveData is a variant of the original observer pattern, with the addition of active/inactive transitions. As such, it is very restrictive in its scope. RxJava provides operators that are much more generalized.

Now to use this SharedViewModel in each fragment, we need:

sharedViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

sharedViewModel.getEmail().observe(this, email -> binding.getForgotEmail().setEmail(email));

This is the initial setup for the the fragment.

private void openLoginPage() {

   …
   sharedViewModel.setEmail(binding.getUser().getEmail());
   …
}

This is needed to be done for every fragment which needs to set and use the common email.

This was a dive into using LiveData<>, ViewModel and how to use a SharedViewModel.

This is how the result looks like:

References:

Continue Reading
Close Menu