Read more about the article Automatic handling of view/data interactions in Open Event Orga App
Abstract 3d white geometric background. White seamless texture with shadow. Simple clean white background texture. 3D Vector interior wall panel pattern.

Automatic handling of view/data interactions in Open Event Orga App

During the development of Open Event Orga Application (Github Repo), we have strived to minimize duplicate code wherever possible and make the wrappers and containers around data and views intelligent and generic. When it comes to loading the data into views, there are several common interactions and behaviours that need to be replicated in each controller (or presenter in case of MVP architecture as used in our project). These interactions involve common ceremony around data loading and setting patterns and should be considered as boilerplate code. Let’s look at some of the common interactions on views: Loading Data While loading data, there are 3 scenarios to be considered: Data loading succeeded - Pass the data to view Data loading failed - Show appropriate error message Show progress bar on starting of the data loading and hide when completed If instead of loading a single object, we load a list of them, then the view may be emptiable, meaning you’ll have to show the empty view if there are no items. Additionally, there may be a success message too, and if we are refreshing the data, there will be a refresh complete message as well. These use cases present in each of the presenter cause a lot of duplication and can be easily handled by using Transformers from RxJava to compose common scenarios on views. Let’s see how we achieved it. Generify the Views The first step in reducing repetition in code is to use Generic classes. And as the views used in Presenters can be any class such as Activity or Fragment, we need to create some interfaces which will be implemented by these classes so that the functionality can be implementation agnostic. We broke these scenarios into common uses and created disjoint interfaces such that there is little to no dependency between each one of these contracts. This ensures that they can be extended to more contracts in future and can be used in any View without the need to break them down further. When designing contracts, we should always try to achieve fundamental blocks of building an API rather than making a big complete contract to be filled by classes. The latter pattern makes it hard for this contract to be generally used in all classes as people will refrain from implementing all its methods for a small functionality and just write their own function for it. If there is a need for a class to make use of a huge contract, we can still break it into components and require their composition using Java Generics, which we have done in our Transformers. First, let’s see our contracts. Remember that the names of these Contracts are opinionated and up to the developer. There is no rule in naming interfaces, although adjectives are preferred as they clearly denote that it is an interface describing a particular behavior and not a concrete class: Emptiable A view which contains a list of items and thus can be empty public interface Emptiable<T>…

Continue ReadingAutomatic handling of view/data interactions in Open Event Orga App

Adding Sentry Integration in Open Event Orga Android App

Sentry is a service that allows you to track events, issues and crashes in your apps and provide deep insights with context about them. This blog post will discuss how we implemented it in Open Event Orga App (Github Repo). Configuration First, we need to include the gradle dependency in build.gradle compile 'io.sentry:sentry-android:1.3.0' Now, our project uses proguard for release builds which obfuscates the code and removes unnecessary class to shrink the app. For the crash events to make sense in Sentry dashboard, we need proguard mappings to be uploaded every time release build is generated. Thankfully, this is automatically handled by sentry through its gradle plugin, so to include it, we add this in our project level build.gradle in dependencies block classpath 'io.sentry:sentry-android-gradle-plugin:1.3.0'   And then apply the plugin by writing this at top of our app/build.gradle apply plugin: 'io.sentry.android.gradle'   And then configure the options for automatic proguard configuration and mappings upload sentry { // Disables or enables the automatic configuration of proguard // for Sentry. This injects a default config for proguard so // you don't need to do it manually. autoProguardConfig true // Enables or disables the automatic upload of mapping files // during a build. If you disable this you'll need to manually // upload the mapping files with sentry-cli when you do a release. autoUpload false }   We have set the autoUpload to false as we wanted Sentry to be an optional dependency to the project. If we turn it on, the build will crash if sentry can’t find the configuration, which we don’t want to happen. Now, as we want Sentry to configurable, we need to set Sentry DSN as one of the configuration options. The easiest way to externalize configuration is to use environment variables. There are other methods to do it given in the official documentation for config https://docs.sentry.io/clients/java/config/ Lastly, for proguard configuration, we also need 3 other config options, namely: defaults.project=your-project defaults.org=your-organisation auth.token=your-auth-token   For getting the auth token, you need to go to https://sentry.io/api/ Now, the configuration is complete and we’ll move to the code Implementation First, we need to initialise the sentry instance for all further actions to be valid. This is to be done when the app starts, so we add it in onCreate method Application class of our project by calling this method // Sentry DSN must be defined as environment variable // https://docs.sentry.io/clients/java/config/#setting-the-dsn-data-source-name Sentry.init(new AndroidSentryClientFactory(getApplicationContext()));   Now, we’re all set to send crash reports and other events to our Sentry server. This would have required a lot of refactoring if we didn’t use Timber for logging. We are using default debug tree for debug build and a custom Timber tree for release builds. if (BuildConfig.DEBUG) Timber.plant(new Timber.DebugTree()); else Timber.plant(new ReleaseLogTree());   The ReleaseLogTree extends Timber.Tree which is an abstract class requiring you to override this function: @Override protected void log(int priority, String tag, String message, Throwable throwable) { }   This function is called whenever there is a log event through Timber and this…

Continue ReadingAdding Sentry Integration in Open Event Orga Android App