Migration to Model-View-ViewModel Architecture and LiveData in Open Event Organizer App

Open Event Organizer App (Eventyay Organizer App) is the Android app used by event organizers to create and manage events on the Eventyay platform as well as check-in and check-out attendees along with other functionalities. The app used the MVP (Model-View-Presenter) architecture and is being ported to MVVM (Model-View-ViewModel). This article will explain the procedure of migrating MVP to MVVM architecture and implementing LiveData.  Why migrate to MVVM? The MVVM architecture is designed to store and manage UI-related data in a lifecycle conscious way. Configuration changes such as screen rotations are handled properly by ViewModels. Tight Coupling: The issue of tight coupling is resolved since only the View holds the reference to ViewModel and not vice versa. A single View can hold references to multiple ViewModels. Testability: Since Presenters are hard bound to Views, writing unit tests becomes slightly difficult as there is a dependency of a View. ViewModels are more unit test friendly as they can be independently tested. There is no dependency of the View. Here, the implementation is being described with the example of About Event module in the Open Event Organizer App. First step is the creation of a new class AboutEventViewModel which extends ViewModel. @Binds @IntoMap @ViewModelKey(AboutEventViewModel.class) public abstract ViewModel bindAboutEventViewModel(AboutEventViewModel aboutEventViewModel); The new ViewModel has to be added to the ViewModelModule: Constructor for the ViewModel: @Inject public AboutEventViewModel(EventRepository eventRepository, CopyrightRepository copyrightRepository, DatabaseChangeListener<Copyright> copyrightChangeListener) { this.eventRepository = eventRepository; this.copyrightRepository = copyrightRepository; this.copyrightChangeListener = copyrightChangeListener; eventId = ContextManager.getSelectedEvent().getId(); } We are using Dagger2 for dependency injection.  LiveData LiveData is a lifecycle-aware data holder with the observer pattern. When we have a LiveData object (e.g. list of attendees), we can add some LifecycleOwner (it can be Activity or Fragment) as an observer. Using this: The Activity or Fragment will remain updated with the data changes. Observers are only notified if they are in the STARTED or RESUMED state which is also known as the active state. This prevents memory leaks and NullPointerExceptions because inactive observers are not notified about changes. Now, let’s discuss about the implementation of LiveData. We will create objects of SingleEventLiveData<> class. private final SingleEventLiveData<Boolean> progress = new SingleEventLiveData<>(); private final SingleEventLiveData<String> error = new SingleEventLiveData<>(); private final SingleEventLiveData<Event> success = new SingleEventLiveData<>(); private final SingleEventLiveData<Copyright> showCopyright = new SingleEventLiveData<>(); private final SingleEventLiveData<Boolean> changeCopyrightMenuItem = new SingleEventLiveData<>(); private final SingleEventLiveData<String> showCopyrightDeleted = new SingleEventLiveData<>(); The functions to get the LiveData objects: public LiveData<Boolean> getProgress() { return progress; } public LiveData<Event> getSuccess() { return success; } public LiveData<String> getError() { return error; } public LiveData<Copyright> getShowCopyright() { return showCopyright; } public LiveData<Boolean> getChangeCopyrightMenuItem() { return changeCopyrightMenuItem; } public LiveData<String> getShowCopyrightDeleted() { return showCopyrightDeleted; } Now, we can remove getView() methods and instead, these objects will be used to call various methods defined in the fragment. Let’s discuss the changes required in the AboutEventFragment now. The Fragment will have ViewModelProvider.Factory injected. @Inject ViewModelProvider.Factory viewModelFactory; Declare an object of the ViewModel. private AboutEventViewModel aboutEventViewModel; Then, in onCreateView(), viewModelFactory will be passed to the ViewModelProviders.of() method as the…

Continue ReadingMigration to Model-View-ViewModel Architecture and LiveData in Open Event Organizer App

Handle app links and apply unit tests in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. Users following links on devices have one goal in mind: to get to the content they want to see. As a developer, you can set up Android App Links to take users to a link's specific content directly in your app, bypassing the app-selection dialog, also known as the disambiguation dialog. Because Android App Links leverage HTTP URLs and association with a website, users who don't have your app installed go directly to content on your site. A unit test generally exercises the functionality of the smallest possible unit of code (which could be a method, class, or component) in a repeatable way. You should build unit tests when you need to verify the logic of specific code in your app. Why unit test cases?Setup app link intent in the appApply unit test casesConclusionResources Let’s analyze every step in detail. Why unit test cases? As it is already discussed what the app link intents are, and it is basically for providing a better user experience for the application. These are some following reason why unit test cases should use -  Rapid feedback on failures.Early failure detection in the development cycle.Safer code refactoring, letting you optimize code without worrying about regressions.Stable development velocity, helping you minimize technical debt. JUnit4 library is used for unit tests. Setup app link intent in the app Declare the frontend host according to build flavor in the app level gradle file: buildTypes { release { resValue "string", "FRONTEND_HOST", "eventyay.com" } debug { resValue "string", "FRONTEND_HOST", "open-event-fe.netlify.com" } } Handle the app link intent in Manifest file by adding intent filter under main activity decleartion: <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="@string/FRONTEND_HOST"/> </intent-filter> Manifest will through the intent in the main activity file. Now handle the intent in main activity: override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) handleAppLinkIntent(intent) } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) handleAppLinkIntent(intent) } private fun handleAppLinkIntent(intent: Intent?) { val uri = intent?.data ?: return val bundle = AppLinkUtils.getArguments(uri) val destinationId = AppLinkUtils.getDestinationId(uri) if (destinationId != null) { navController.navigate(destinationId, bundle) } } Here a new class/object AppLinkUtils is defined which will return destination fragment id and the argument/data according to the intent URI. Apply unit test cases: First, implement the libraries in the gradle file -  1. JUnit for unit tests, 2. Robolectric for using android classes in the test class: testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:3.4.2' Create a test class for testing the app link functions and run it with RoboLectricTestRunner: private const val EVENT = "event" private const val RESET_PASSWORD = "resetPassword" private const val VERIFY_EMAIL = "verifyEmail" @RunWith(RobolectricTestRunner::class) class AppLinkUtilsTest { private fun getAppLink(type: String): Uri { return when (type) { EVENT -> Uri.parse("https://eventyay.com/e/5f6d3feb") RESET_PASSWORD -> Uri.parse("https://eventyay.com/reset-password?token=822980340478781748445098077144") VERIFY_EMAIL -> Uri.parse("https://eventyay.com/verify?token=WyJsaXZlLmhhcnNoaXRAaG") else ->…

Continue ReadingHandle app links and apply unit tests in Open Event Attendee Application

Add Navigation Architecture Component in Eventyay Attendee

The eventyay attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. The project earlier used Fragment transition to handle fragment with multiple activities. Multiple activities are used because it is very complex to handle all fragment with a single activity using fragment transition. If we try to make a single activity application with fragment transition, it contains a lot of bugs with the back stack. But we can use Navigation Architecture Component to efficiently make Eventyay attendee a single activity. It is the perfect technology to handle all fragments and authentication in a single activity with no bigs or back stack. Issues with fragment transition and multiple activitiesWhy navigation architecture component?Process for using Navigation Architecture Component in the applicationAnimation of fragments with Navigation Architecture ComponentConclusionResources  Let’s analyze every step in detail. Issues with fragment transition and multiple activities Need to handle back stack on each Fragment TransitionHard to debugThis can leave your app in an unknown state when receiving multiple click events or during configuration changes.Fragment instances can be createdMultiple activities make the app slower due to using multiple intents Why navigation architecture component? Handling Fragment transactions Handling Up and Back actions correctly by defaultProviding standardized resources for animations and transitionsTreating deep linking as a first-class operationIncluding Navigation UI patterns, such as navigation drawers and bottom navigation, with minimal additional workProviding type safety when passing information while navigatingVisualizing and editing navigation graphs with Android Studio's Navigation Editor Steps involved in using Navigation Architecture Component in the application Adding dependencies to build.gradle: //Navigation implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha09' implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha09' Create new resources file for navigation graph under res -> navigation: Add fragments in navigation_graph.xml with navigation editor. Let’s take one example: <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/mobile_navigation" app:startDestination="@id/eventsFragment"> <fragment android:id="@+id/eventsFragment" android:name="org.fossasia.openevent.general.event.EventsFragment" android:label="EventsFragment" /> </navigation> Here app:startDestination attribute is for the very first fragment after which app exit on back pressed. Replace Frame layout with a fragment in the main activity layout file. And add the above navigation graph to it:  <fragment android:id="@+id/frameContainer" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" android:background="@android:color/white" android:layout_weight="1" app:defaultNavHost="true" app:navGraph="@navigation/navigation_graph" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> Now, this fragment layout is directly connected with all fragments added in the navigation graph. We only need to navigate the fragment. Set bottom navigation drawer with the navigation architecture component -  First, we need to make the ids of menu component same as the ids of fragments in the navigation graph. Bottom navigation menu: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/eventsFragment" android:icon="@drawable/ic_baseline_event_24px" android:title="@string/events" /> <item android:id="@+id/searchFragment" android:icon="@drawable/ic_search_grey_24dp" android:title="@string/search" /> <item android:id="@+id/favoriteFragment" android:icon="@drawable/ic_baseline_favorite_border_24px" android:title="@string/likes" /> <item android:id="@+id/orderUnderUserFragment" android:icon="@drawable/ic_baseline_ticket_24dp" android:title="@string/tickets" /> <item android:id="@+id/profileFragment" android:icon="@drawable/ic_baseline_account_circle_24px" android:title="@string/profile" /> </menu> Setup the bottom navigation menu with navigation controller: // import require libraries import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI.setupWithNavController // Set nav controller with host fragment using Kotlin smart cast val hostFragment = supportFragmentManager.findFragmentById(R.id.frameContainer) if (hostFragment is NavHostFragment) navController = hostFragment.navController setupWithNavController(navigationMenu, navController) Navigate…

Continue ReadingAdd Navigation Architecture Component in Eventyay Attendee

Add PayPal Payment integration in Open Event Attendee Application

The open event attendee is an android app which allows users to discover events happening around the world using the Open Event Platform. It consumes the APIs of the open event server to get a list of available events and can get detailed information about them. PayPal is a very common method to pay for anything throughout the world. It is a highly popular platform and it’s only right that there should be an option to pay through PayPal on Eventyay attendee for tickets. This blog will explain how and why I added PayPal payment feature in the application with a sandbox account. Why PayPal?Get API key for a sandbox accountIntegration with Android StudioConclusionResources Let’s analyze every step in detail. Advantages of using PayPal Payment integration Provide UI to gather payment information from the userGet your credentials, which identify your PayPal account as the payment receiver. Specifically, obtain a client ID and secret.Returns a proof of payment to your app.Provides the user their goods or services. Setup Sandbox account and get the API key Go to https://developer.paypal.com/ and sign up for a developer account: After Sign up, go to the dashboard and create an app:  Now in app credentials go to sandbox accounts. Here you can find your API key. Now, Create a new sandbox account with entering some amount of money for testing purposes: PayPal SDK integration in the application Add PayPal SDK in build.gradle dependencies: //PayPal compile 'com.paypal.sdk:paypal-android-sdk:2.16.0' Store the API key in the android manifest file: <meta-data android:name="com.paypal.android.API_KEY" android:value="${PAYPAL_CLIENT_ID}"/> Get the API key in the fragment where PayPal payment is required: private lateinit var PAYPAL_API_KEY: String PAYPAL_API_KEY = activity?.packageManager?.getApplicati<meta-data android:name="com.paypal.android.API_KEY" android:value="${PAYPAL_CLIENT_ID}"/>onInfo(activity?.packageName, PackageManager.GET_META_DATA) ?.metaData?.getString(PAYPAL_KEY).toString() Start PayPal services on create view: val payPalConfiguration = PayPalConfiguration() .environment(PayPalConfiguration.ENVIRONMENT_SANDBOX) .clientId(PAYPAL_API_KEY)val intent = Intent(context, PaymentActivity::class.java)intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, payPalConfiguration)activity?.startService(intent) Now start the Payment conditionally with same intent:  val payment = PayPalPayment(BigDecimal(amount.toString()), "USD", "Pay for tickets", PayPalPayment.PAYMENT_INTENT_SALE) intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment) startActivityForResult(intent, PAYPAL_REQUEST_CODE) Handle the result after payment is done: override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == PAYPAL_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { val paymentConfirmation = data?.getParcelableExtra<PaymentConfirmation>(PaymentActivity.EXTRA_RESULT_CONFIRMATION) if (paymentConfirmation != null) { val paymentInfo = paymentConfirmation.toJSONObject() val tokenId = paymentInfo.getJSONObject("response").getString("id") Timber.d(paymentInfo.toString(4)) // Send the token to server val charge = Charge(attendeeViewModel.getId().toInt(), tokenId, null) attendeeViewModel.completeOrder(charge) } } else if (resultCode == Activity.RESULT_CANCELED) Toast.makeText(context, "Payment canceled!", Toast.LENGTH_SHORT).show() else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) Toast.makeText(context, "Invalid Payment Configuration", Toast.LENGTH_SHORT).show() } } GIF In a nutshell With almost 250 million users worldwide, PayPal is an extremely popular platform for monetary transactions and it’s quite essential that every application an option to use it. Given the nature of Eventyay attendee and its pan-world appeal, I have added PayPal as a payment system for tickets to any event. Resources PayPal Android SDK guide: PayPal Android SDKPayPal SDK repo: PayPal-Android-SDK Tags PayPal, Android, Payments, FOSSASIA, GSoC, AndroidPayments, Kotlin

Continue ReadingAdd PayPal Payment integration in Open Event Attendee Application

Adding Preference Settings using Preference Fragment Compat

It is very much likely that one needs to add preferences to their app which span the entire application and therefore can be accessed anywhere in the app without storing anything in database or making global variables. For an instance, in Open Event Organizer App we added the preferences to store the privacy policy, cookie policy etc. The user can access these items in Settings Preference which in device settings. In this blog post we will see how to add preference settings to the app by storing the data in shared preferences. Specifications The benefit of storing the data in shared preference and not in local storage is that the access time for the data is drastically reduced and the data persists even when the app is closed. We will use this library which is built on top of official preference-v7 library. Firstly, we will make a preference resource layout file and add the preference for privacy policy and cookie policy in the preference screen. <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <Preference android:key="@string/privacy_policy_key" android:title="@string/privacy_policy" /> <Preference android:key="@string/cookie_policy_key" android:title="@string/cookie_policy" /> </PreferenceScreen> Make a separate preference fragment class named LegalPreferenceFragment which extends PreferenceFragmentCompat. Then we will override onCreatePreferenceFix() method. Inside this, we will create an instance of Preference Manager and set shared preference name for it and set the preference using the layout file. This enables us to use findPreference() method to retrieve the layout preferences by their key. After, retrieving the preference we will set onClick listener to launch activity with an intent to open browser for the url passed in data bundle. @Override public void onCreatePreferencesFix(@Nullable Bundle bundle, String rootKey) { PreferenceManager manager = getPreferenceManager(); manager.setSharedPreferencesName(Constants.FOSS_PREFS); setPreferencesFromResource(R.xml.legal_preferences, rootKey); findPreference(getString(R.string.privacy_policy_key)).setOnPreferenceClickListener(preference -> { BrowserUtils.launchUrl(getContext(), PRIVACY_POLICY_URL); return true; }); findPreference(getString(R.string.cookie_policy_key)).setOnPreferenceClickListener(preference -> { BrowserUtils.launchUrl(getContext(), COOKIE_POLICY_URL); return true; }); } References Preference Fragment Compat library by Takisoft https://github.com/Gericop/Android-Support-Preference-V7-Fix Android Preference Documentation https://developer.android.com/reference/android/preference/PreferenceGroup

Continue ReadingAdding Preference Settings using Preference Fragment Compat

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. app:markerInCenter - This defines the position of the round marker within the layout. Setting it to true, position it in center. 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 TimeLineView library by VipulAsri https://github.com/vipulasri/Timeline-View Android Documentation for RecyclerViewAdapter https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter Android Documentation for RecyclerViewView https://developer.android.com/reference/android/support/v7/widget/RecyclerView

Continue ReadingImplementing Timeline for Attendees Activity in Organizer App

Adding List Preference Settings using Preference Fragment Compat

In this blog post we will see how we can add a Preference List in Settings which will display a list of radio buttons in UI which user can choose from. In Open Event Orga App, the Organizer had a choice to switch between viewing Net Sales or Gross Sales in the App’s Dashboard. We decided to use a preference list to allow the user to select between using Net or Gross Sales. The benefit of using Preference List and not any other storage media (like SQLite) to store the information is that, Preference List stores the information as key-value pair in SharedPreferences which makes it easy to store and extract small amount of data with strong consistency guarantees and using less time. Let’s move on to the implementation. Implementation Firstly add the dependency in build.gradle. implementation "com.takisoft.fix:preference-v7:27.1.0.0" In the preferences layout file, we will use checkboxes. <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <CheckBoxPreference android:key="@string/gross_sales_key" android:title="@string/gross_sales" android:defaultValue="true" /> <CheckBoxPreference android:key="@string/net_sales_key" android:title="@string/net_sales" android:defaultValue="false" /> </PreferenceScreen> We will create SalesDataSettings class which extends PreferenceFragmentCompat and override onCreatePreferenceFix method. We will request PreferenceManager and set SharedPreferencesName. The manager will be used to store and retrieve key-value pairs from SharedPreferences. Using setPreferencesFromResource we will attach the layout file to the fragment. PreferenceManager manager = getPreferenceManager(); manager.setSharedPreferencesName(Constants.FOSS_PREFS); setPreferencesFromResource(R.xml.sales_data_display, rootKey); We are using CheckBox Preferences and modifying their behaviour to work as a Radio Preference List because Radio reference is not provided by Android. We are initializing two checkboxes and attaching a preference listener to unset all other checkboxes which one is selected. CheckBoxPreference netSales = (CheckBoxPreference) findPreference(NET_SALES); CheckBoxPreference grossSales = (CheckBoxPreference) findPreference(GROSS_SALES); Preference.OnPreferenceChangeListener listener = (preference, newValue) -> { String key = preference.getKey(); switch (key) { case GROSS_SALES: netSales.setChecked(false); break; case NET_SALES: grossSales.setChecked(false); break; default: break; } return (Boolean) newValue; }; netSales.setOnPreferenceChangeListener(listener); grossSales.setOnPreferenceChangeListener(listener); We can load SalesDataDisplay Fragment class when a preference button is clicked using fragment transactions as shown below. findPreference(getString(R.string.sales_data_display_key)).setOnPreferenceClickListener(preference -> { getFragmentManager().beginTransaction() .replace(R.id.fragment_container, SalesDataSettings.newInstance()) .addToBackStack(null) .commit(); return true; }); References Shared Preferences Documentation https://developer.android.com/reference/android/content/SharedPreferences Gericop Takisoft Android-Support-Preference-V7-Fix https://github.com/Gericop/Android-Support-Preference-V7-Fix Codebase for Open Event Organizer App https://github.com/fossasia/open-event-orga-app

Continue ReadingAdding List Preference Settings using Preference Fragment Compat

Performing Database Migrations using DbFlow

In Open Event Organizer Android App we decided to add database migrations for every change in database while development. Two of the reasons behind this - The users have some version of the app installed and then during development the developers had to modify the database, let’s say, add a table or modify existing ones. This makes the existing database incompatible with the new app. On adding database migration the upgradation of database takes place automatically and prevent the situation of reinstalling the app. Migrations makes it possible to rollback or upgrade to some particular database state. Thus, help in debugging certain changes in isolation. Let’s discuss the implementation details. Consider the scenario when a user adds a new table named SpeakersCall. For creating migration for this change we need to add migration class inside OrgaDatabase class annotated with @Database. We will break it down to look closely at each step. Use @Migration annotation in DbFlow library and specify the new database version (by incrementing the existing version) and the database class. Extend BaseMigration and override migrate method. The logic used inside the migrate method can be different for different tasks. In the present case we first need to delete any existing table (if exists) with the name SpeakersCall and then recreate that table in database. Create an array of java classes which are created/modified. We wrap the SQL query inside a Database Wrapper class which prevents it from running recursively. FlowManager uses reflection to look up and construct the generated database holder class used in defining the structure for all databases used in this application. So we will getModelAdapter for the class to be recreated and use creation query returned by Model Adapter and execute it. @Migration(version = 15, database = OrgaDatabase.class) public static class MigrationTo15 extends BaseMigration { @Override public void migrate(@NonNull DatabaseWrapper databaseWrapper) { Timber.d("Running migration for DB version 14"); Class<?>[] recreated = new Class[] {SpeakersCall.class}; for (Class<?> recreate: recreated) { ModelAdapter modelAdapter = FlowManager.getModelAdapter(recreate); databaseWrapper.execSQL(DROP_TABLE + modelAdapter.getTableName()); databaseWrapper.execSQL(modelAdapter.getCreationQuery()); } } } Similarly, we can write migration for changing a column of table(s).

Continue ReadingPerforming Database Migrations using DbFlow

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. Use Mockito.when to return Observables one by one from ORDERS_LIST whenever the method getOrders of the mock orderRepository is called. We will use Mockito.InOrder and pass orders, orderRepository and progress to check if they are called in a particular order. We will use .observeForever method to observe on LiveData objects and add a ArrayList on change. 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 Official Documentation for testing. https://developer.android.com/reference/android/arch/core/executor/testing/InstantTaskExecutorRule Official Documentation for JUnit.  https://junit.org/junit4/ Official documentation for Mockito.  http://site.mockito.org/ Open Event Organizer App codebase.  https://github.com/fossasia/open-event-orga-app

Continue ReadingTesting the ViewModels in Open Event Organizer App

Swipe to Check In/Out in Open Event Organizer App

Open Event Organizer App didn’t provide any option for the Event Organizer to view the list of Attendees present under an Order and check them in/out the event. Therefore, we designed a system such that the Organizer can just swipe the attendee present under an order to check them in or out. In this blog post, I will discuss how we implemented this functionality in Open Event Organizer App without using any third party libraries. Specifications We will create a separate class SwipeController.java which extends ItemTouchHelper.SimpleCallback and provide the swiping functionalities to our plain old recyclerview. We will call the super constructor with ItemTouchHelper.LEFT and ItemTouchHelper.RIGHT as arguments to provide left as well as right movements in each recyclerview list item. The bitmaps and paint object initialized here will be used later in onDraw. public SwipeController(OrderDetailViewModel orderDetailViewModel, OrderAttendeesAdapter orderAttendeesAdapter, Context context) { super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT); this.orderDetailViewModel = orderDetailViewModel; this.orderAttendeesAdapter = orderAttendeesAdapter; closeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.close); doneIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.done); paintGreen.setColor(context.getResources().getColor(R.color.light_green_500)); paintRed.setColor(context.getResources().getColor(R.color.red_500)); } Next, we will override getMovementFlags method. This method decides the allowed movement directions for each recyclerview item. The deciding logic is that, if an attendee is checked in then the allowed movement is left to check out and if an attendee is checked out then the allowed movement is right to check in. If neither of the above case, then both movements are allowed. @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = 0; If (orderDetailViewModel.getCheckedInStatus( viewHolder.getAdapterPosition()) == null) makeMovementFlags(dragFlags, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT); if (orderDetailViewModel.getCheckedInStatus( viewHolder.getAdapterPosition())) { return makeMovementFlags(dragFlags, ItemTouchHelper.LEFT); } else { return makeMovementFlags(dragFlags, ItemTouchHelper.RIGHT); } } The onChildDraw method involves the code doing actual drawing. The variables used in code are discussed below. ActionState - Checks the state of the recycler view item. We proceed with the below logic if the item is being swiped. dX - The distance by which the item is swiped. Positive for left and negative for right. Background - Background of the viewholder. Rectangular in shape and dimensions changed with change in dX. IconDest - Calculates the position where the icons (close icon or done icon) is placed in canvas Canvas - Java Canvas on which the drawing is done. We set the background and draw the bitmaps on their location in canvas. @Override public void onChildDraw(Canvas canvas, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { View itemView = viewHolder.itemView; float height = (float) itemView.getBottom() - (float) itemView.getTop(); float width = height / 3; RectF background; Paint paint; Bitmap icon; RectF iconDest; if (dX > 0) { background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom()); paint = paintGreen; icon = doneIcon; iconDest = new RectF((float) itemView.getLeft() + width, (float) itemView.getTop() + width, (float) itemView.getLeft() + 2 * width, (float) itemView.getBottom() - width); } else { background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom()); paint = paintRed; icon = closeIcon; iconDest = new RectF((float) itemView.getRight() - 2 * width, (float) itemView.getTop() +…

Continue ReadingSwipe to Check In/Out in Open Event Organizer App