/WallR2.0

Wallpaper changer app showcasing MVP, RxJava, Dagger 2 and Clean Architecture

Primary LanguageC++

Explore Wallpapers

Did you know that an average user checks their device more than 80 times a day? Make each time a real pleasure with beautiful HD wallpapers from WallR. Let your device be a treat to your eyes every-time you check it.

Table of Contents

Introduction

WallR is an open-source Android wallpaper app written in Kotlin, keeping in mind proper coding guidelines and app architecture so that it is easy to maintain and scale.

Salient features of the app :-

Screenshots

Explore Wallpapers Wallpaper Categories Collections

Search Wallpapers Minimal Wallpapers Wallpaper Details

App Details

Architecture

A proper app architecture implements the SOLID principles and ensures :-

  • Easy scalability as features are independent of each other.
  • Easy maintainance since the classes are decoupled in nature thus we need to make changes only to the desired class and it would be reflected everywhere.
  • Testability, since the abstraction layers are easy to mock and test.

Thus, for WallR, Clean Architecture with MVP was chosen as the architecture.

Clean Architecture with MVP

In Clean Architecture, the code is separated into layers in an onion shape with one dependency rule: The inner layers should not know anything about the outer layers. Inner layers contain business logic, whereas the outer layers contain implementation and the middle layer contain Interface Adapters. Each ring represent one layer of abstraction.


A diagram representing different layers, components and how they communicate with each other in the app :-


Presentation Layer

  • MVP (Model View Presenter) is suitable for the presentation layer.
  • Views are dumb and implement Passive View pattern. It is a set of interfaces that could be implemented by any Android view, such as Activities, Fragments, Adapters or Custom Views.
  • Presenter serve as a middleman between views (abstractions over Android specific components) and the business logic (Interactors/Use Cases). They handle user interactions, invoke appropriate business logic and send the data to the UI for rendering.
  • Presenter does not depend on Android classes hence improves testability.

Domain Layer

  • A simple example of Use Case would be "Fetch new wallpapers". Each Use Case is a reusable component that executes a specific business logic. It fetches the data from a repository, executes the business logic and returns the result to the presenter.

Data Layer (Database & API)

  • Repository is responsible to create an abstraction of the data sources from which the Use Cases get the data to act upon.
  • Business logic shouldn’t know where the data comes from.

Dependency Injection

It is a software design pattern that implements inversion of control for resolving dependencies. Implementing proper dependency injection in our apps allows us to have :

  • Testable classes as dependencies which are injected from outside the class can be easily mocked
  • Re-usable and interchangeable components.
  • Scoped dependencies so that classes can share the same dependency state as and when required without having to create a new instance every time.

Dagger 2

  • Dagger 2 is a dependency injection framework, which makes it easier to manage the dependencies between the classes in our app.

In this project the dependency graph is constructed via :


The Main Activity Subcomponent has the Fragment Provider Module which in-turn creates the fragment subcomponents.

The various dependency scopes used in this app are :

  • Per Application - This is similar to the Singleton scope where the dependency lasts for the entire lifetime of the application.
  • Per Activity - This is the scope where the dependency lasts as long as the activity lasts.
  • Per Fragment - Where the dependency is attached to the lifecycle of the fragment.
  • Per Service - Where the dependency is attached to the lifecycle of the service.

A diagramatic representation od the Dependency graph for this project :-



Multi-threading

To provide the users with a fast and responsive app, heavy work such as network calls, database operations, file operations or other background tasks need to be done on threads other than the UI Thread. This is where multi-threading comes into play.
Schedulers like Computation, IO, Android Main Thread have been used to effectively juggle between background and foreground activities. However, all of this is done using a layer of abstraction for the background and foreground schedulers so that they can be easilty tested.

RxJava 2

RxJava is used to do all the heavy lifting in seperate background threads and to return the result of those operations to the UI Thread so that they can be used or displayed to the user without any stutters or lags in the app.
In this project the various reactive streams used are :

In order to encash on the multi-threading capability of RxJava, the various schedulers that are used are :

Autodispose is used to dispose off the various streams.

Remote Data Source

In this project, two sources of wallpaper have been used :-

  • Unsplash API which is used when an user searches for any specific wallpaper tag
  • Firebase Realtime Database which is used to provides a cached copy of the various categories of wallpapers from Unsplash due to the limited number of api requests available from unsplash derectly.

Unsplash API

The Unsplash API is a modern JSON API that surfaces all of the info required for displaying various wallpapers to the users.

The JSON response obtained from the Unsplash API using Retrofit, is trimmed down to the following data model :

To convert the data into the above model, the GSON Converter Factory is used which maps the data into the model using the SerializedName provided with each field.

Firebase Realtime Database

The Firebase Realtime Database is used to cache images from Unsplash as the number of API requests available in the Unsplash API is limited. Thus the app fetches all the wallpaper data required for shocasing the default wallpapers from Firebase itself. The basic structure of the firebase database for this project looks like :-

The data model to which the JSON response from the Firebase Realtime Database is mapped, looks like :-

To convert the data into the above model, gson is used which maps the data into the model using the variable names.

Data Persistence

Data Persistence is used to remember the user preferences and wether the user is pro or not using the shared preferences.
The Room database is used to run the automatic wallpaper changer which uses a locally saved collection of images and changes the device's wallpaper from time to time.

Room Database

In WallR, the database is used behind an abstraction so that it can be tested and also future changes can be easily incorporated.
The database looks like :-

Shared Preferences

The shared preferences are used under a layer of abstraction which helps us to test the logic and also makes provision for easy integration of api changes in the future.
The layer of abstraction also allows us to easily migrate from Shared Preferences to any other storing framework as changes under the layer will be reflested all througout the app.

Networking

Network operations are heavy tasks which should be performed in background threads so that the user does not face any app not responding errors due to the UI Thread getting clogged up. Thus I have used Retrofit with RxJava.

Retrofit

Retrofit is also used behind an abstraction so that it can be tested and easily replaced with any other framework if needed.
RxJava Call Adapter is used to return the responses wrapped in reactive streams.
The GSON Converter Factory is used which map the response data into various entity models to be consumed.
Retrofit deep down uses the OkHttp library for HTTP requests.

Image Loading

Image loading can be from various sources of data like a file path or a bitmap or a drawable resource or it may also happen that the image needs to be processed befoer being used and glide caters to all that with very minimal boiler-plate code with proper caching mechanisms and hence was suitable for this project.

Glide

Glide is used behind an image loader abstraction so that any other image loading library can be easily used instead of glide if required.

Testing

TDD (Test Driven Development) is an effective way of developing the applicaion by incrementally adding the code and writing tests. It ensures us that the addition of new features have not mistakenly broken anything else and gives us more confidence to push out code.
While writing tests we often come across certain edge cases which were not thought of while writing the normal code thus helps us build a more robust and stable application.

Unit Tests

Unit tests in this project are written using the Mockito framework and run using the JUnit runner. These tests are very fast in nature and help us quickly test our codebase for any breaking change.

The following unit test code coverage values reflect lower than actual coverage values due to certain issues in calculation of code coverage in kotlin. More reports of such issues can be found here.


Overall Coverage


Presentation Layer Coverage


Domain Layer Coverage


Data Layer Coverage

Instrumentation Tests

User interface (UI) testing lets you ensure that your app meets its functional requirements and achieves a high standard of quality such that it is more likely to be successfully adopted by users.
One approach to UI testing is to simply have a human tester perform a set of user operations on the target app and verify that it is behaving correctly. However, this manual approach can be time-consuming, tedious, and error-prone. A more efficient approach is to write UI tests such that user actions are performed in an automated way. The automated approach allows us to run tests quickly and reliably in a repeatable manner.

App Features from an User's Perspective

  • Daily new wallpapers
  • More than 10 Categories of wallpapers
  • Search for wallpaper of your choice from a collection of over 100k+ images
  • Minimal wallpapers
  • Create your own Material or Gradient or Plasma wallpaper
  • Quick set wallpaper
  • Edit wallpapers before setting them
  • Download wallpaper to device
  • Crystallize any wallpaper
  • Share wallpapers with your contacts
  • Preview full screen wallpaper before setting
  • Add wallpapers to collection for future use
  • Enable automatic wallpaper changer to automatically change wallpapers periodically
  • Add any external image to collection to use it as a wallpaper or to crystallize it

Project Setup Notes

  • The google-services.json file has been purposely ignored by git due to security purposes. Please login to Firebase console and create a new project and obtain your own google-services.json file and paste it at app/src/debug or app/src/release directory as per your build flavour to set it up and running.
    For more information, please refer the docs.

  • This project can be directly built from the command line using the command :

    • gradle build for Windows OS.
    • ./gradlew build for Linux/Mac OS.
  • The unit tests can be direclty run from the command line using the command :

    • gradle test for Windows OS.
    • ./gradlew test for Linux/Mac OS.

Pro Tip : If your build fails, please ensure that your JAVA_HOME value in environment variables is set to JDK V8. Please refer to this gradle issue.

Acclamations

WallR was selected as one of the best customization apps by Android Authority, Android Police. It also recieved huge number of warm and positive feedbacks and reviews at XDA Developers Community.

Get it on Google Play

Libraries

RxJava 2 - helps with multi-threading.
Autodispose - used for disposing off observeables created using RxJava.
Unsplash - the source of every wallpaper used in the app.
Retrofit - used for networking.
Dagger 2 - used for dependency injection.
Mockito, - helps with testing by mocking classes.
JUnit 4 - helps in running tests.
Toasty - a library for showing fancy toasts to the user.
WaveSwipeRefreshLayout - a custom viewgroup which provides a fancy way to refreshing items on swipe.
Android-SpinKit - provides fancy loading animations.
SmartTabLayout - a custom material tab layout.
Material Dialogs - easy to use material dialogs.
Glide - used for image loading.
Recyclerview Animator - used to animate recycler view items.
Firebase - used for caching images from unsplash.
Sliding up panel - provides a sliding up bottom panel.
Circle ImageView - a custom imageview.
TapTarget View - used for showing hints to the user.
Material Spinner - a custom material spinner.
Rx ImagePicker - an image picker library wrapped with rxjava.
Photoview - a custom imageview with pinch zoom funtionality.
Ucrop - an image processing library which provides various editing options.
Guillotine-Menu - a custom navigation drawer.
Colorpicker view - a custom view which helps the user to choose a color in-order to generate custom wallpapers.


About the Author

Abhriya Roy

Android Developer with 2 years of experience in building apps that look and feel great. Enthusiastic towards writing clean and maintainable code. Open source contributor.

LinkedIn   Twitter   Stack Overflow   Angel List


License

Copyright 2019 Abhriya Roy

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.