Chapters

Hide chapters

Advanced Android App Architecture

First Edition · Android 9 · Kotlin 1.3 · Android Studio 3.2

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

15. VIPER Sample
Written by Aldo Olivares

In the last chapter, you learned the theory behind VIPER. You learned how all of its layers work and why this architecture is an excellent option for building maintainable and scalable Android apps.

In this chapter, you’ll apply that knowledge to rebuild the WeWatch app using VIPER. Specifically, you’ll learn how to:

  • Create a Presenter that acts as a bridge between all of the components in your app.
  • Create an Interactor that communicates with your app’s backend.
  • Create a Router that handles the navigation between your Views.
  • Use Cicerone to implement your Routers.
  • Use Interfaces as contracts to implement the layers of VIPER.

Getting started

Using Android Studio, open the starter project for this chapter project by going to File ▸ New ▸ Import Project and selecting build.gradle in the root of the project.

The starter project contains the basic structure you’ll use for this app, and it contains the following packages:

  • Data: All of the backend code that your app needs to work correctly, including the Entities, a Repository and the Room database components.
  • Interactor: The interactors of your app. Package should currently be empty.
  • View: The activities, fragments and adapters.
  • Presenter: The Presenters of your app. Package should currently be empty.

Note: If you don’t see the interactor or presenter packages, then go ahead and just add them to the project by right clicking on the com.raywenderlich.wewatch package and selecting New ▸ Package.

For didactic purposes, this code was organized with a structure according to VIPER layer names, but in a real-world app you often want to structure your project in packages according to the different modules in your app such as MainModule, AddModule or SearchModule.

Take some time to familiarize yourself with the rest of the starter project and the features included out-of-the-box, like the adapters and layouts.

Once the starter project finishes loading and building, run the app on a device or emulator.

Currently, it’s an empty canvas, but that’s about to change!

Note: Before moving on to the next section, remember to get an API Key from TMDB API (https://developers.themoviedb.org/3/getting-started/introduction) and to substitute the value inside RetrofitClient.kt.

Defining your app’s contract

For this project, every module is represented as an Interface contract that needs to be implemented by several associated classes. The contract represents which VIPER layers you must implement in every module and the actions the layers need to perform.

Open MainContract.kt and review the contract that corresponds to Main Module:

interface MainContract {

  interface View {
    fun showLoading()
    fun hideLoading()
    fun showMessage(msg: String)
    fun displayMovieList(movieList: List<Movie>)
    fun deleteMoviesClicked()
  }

  interface Presenter {
    fun deleteMoviesClick(selectedMovies: HashSet<*>)
    fun onViewCreated()
    fun onDestroy()
    fun addMovieClick()
  }

  interface Interactor {
    fun loadMovieList(): LiveData<List<Movie>>
    fun delete(movie: Movie)
    fun getAllMovies()
  }

  interface InteractorOutput {
    fun onQuerySuccess(data: List<Movie>)
    fun onQueryError()
  }
}

Notice that a few interfaces represent some of the main components of the VIPER architecture pattern like View, Presenter and Interactor. In the next few sections, you’ll implement each module with the corresponding Interactors, Presenters and Views.

Implementing the Main Module

Now that you have the contract defined for the main module, it’s time to apply the contract to each individual component. You’ll start with the View.

The View

Open MainActivity.kt inside view/activities and make it so MainActivity implements MainContract.View:

class MainActivity : BaseActivity(), MainContract.View {

Android Studio immediately displays a warning informing you of some members that your class needs to implement. Press Control-I to get a complete list of the missing members. Select all of them and click OK.

Start by implementing showLoading() and hideLoading():

override fun showLoading() {
  moviesRecyclerView.isEnabled = false
  progressBar.visibility = View.VISIBLE
}

override fun hideLoading() {
  moviesRecyclerView.isEnabled = true
  progressBar.visibility = View.GONE
}

Note: Remember to use Alt-Enter on Windows or Option-Return on Mac to import any missing classes.

The above code hides or shows the progressBar and the moviesRecyclerView whenever the Presenter instructs it to do so.

Now, add the following inside showMessage():

toast(msg)

This method displays a simple message to the user using a toast message.

Add the following properties to MainActivity to hold a reference to the Presenter, which you’ll implement later, and the adapter for moviesRecyclerView:

lateinit var presenter: MainContract.Presenter
private lateinit var adapter: MovieListAdapter

Add this inside displayMoviesList():

adapter = MovieListAdapter(movieList)
moviesRecyclerView.adapter = adapter

This creates a new MovieListAdapter and assigns it to moviesRecyclerView once the data is received.

Now, add the following inside deleteMoviesClicked():

presenter.deleteMovies(adapter.selectedMovies)

This function tells the Presenter when the user wants to delete all of the movies marked as watched by passing the selected movies as a parameter to deleteMovies().

Add this code inside goToSearchActivity():

presenter.addMovie()

This tells the Presenter that you tapped + and you intend to add a new movie to your list.

Next, override the activity’s onResume() and onDestroy() to tell the Presenter when the View is created and when it gets destroyed. This way the Presenter can perform any initial set up tasks and remove all unnecessary references.

override fun onResume() {
  super.onResume()
  presenter.onViewCreated()
}

override fun onDestroy() {
  super.onDestroy()
  presenter.onDestroy()
}

Finally, add the following code to override onOptionsItemSelected():

override fun onOptionsItemSelected(item: MenuItem): Boolean {
  when (item.itemId) {
    R.id.action_delete -> this.deleteMoviesClicked()
    else -> toast(getString(R.string.error))
  }
  return super.onOptionsItemSelected(item)
}

This method calls deleteMoviesClicked() when the user taps the delete icon at the top.

With the View ready, it’s time to implement the Router.

The Router

As you learned in the previous chapter, the Router layer is in charge of the navigation across the multiple Views in your app. In a traditional VIPER implementation you would have a Presenter that receives actions from the View that are then translated as commands for the Interactor or the Router.

With this architecture in mind, it makes a lot of sense to create a Router that knows about all of the Views but at the same time lets the Presenter command the navigation. However, due to how Android is designed, you’ll always need an Intent and a call to startActivity() to move between different modules in your app. Therefore, it’s challenging to move the navigation logic from the Views to another component.

There are many workarounds to solve this problem, including the new Jetpack Navigation Controller. At the time of writing this chapter, the Navigation Controller was just released as a stable version, so there might still be some bugs. For that reason, you’ll use a different tool: Cicerone.

So, what is Cicerone? According to the documentation, Cicerone is a lightweight library for MVP designed to help you manage the navigation in your Android app.

It offers several benefits:

  • Unlike other libraries, Cicerone is not tied to fragments, which means that you can also use it with activities.
  • It’s easy to test compared to other libraries and frameworks.
  • It’s lifecycle-safe which makes it great to use with other lifecycle aware components such as the ViewModels from Google’s Architecture Components.

To use Cicerone in your app, you need to perform some initial setup tasks. First, add the required dependency to app/build.gradle in the dependencies block:

def ciceroneVersion = "2.1.0"
implementation "ru.terrakok.cicerone:cicerone:$ciceroneVersion"

Sync the app Gradle files and wait until the project finishes building.

Then, open App.kt inside the app’s root package and add the following property and method:

lateinit var cicerone: Cicerone<Router>

private fun initCicerone() {
  this.cicerone = Cicerone.create()
}

This is a simple function that initializes a Cicerone instance that should be available for all your classes.

Now, add the following line inside onCreate() to initialize your cicerone property when the app launches:

this.initCicerone()

That’s it! That’s everything you need to use Cicerone in your app. Now, you only need to make your Views aware of this new component.

Open MainActivity.kt and add the following properties:

//1
companion object {
  val TAG: String = "MainActivity"
}
//2
private val navigator: Navigator? by lazy {
  object : Navigator {
    //3
    override fun applyCommand(command: Command) {   // 2
      if (command is Forward) {
        forward(command)
      }
    }
    //4
    private fun forward(command: Forward) {
      when (command.screenKey) {
        AddMovieActivity.TAG -> startActivity(Intent(this@MainActivity, AddMovieActivity::class.java))
        else -> Log.e("Cicerone", "Unknown screen: " + command.screenKey)
      }
    }
  }
}

Taking each comment section in turn:

  1. Declare a companion object with a TAG to identify this View.
  2. Declare a Navigator instance that let’s your Router process a series of navigation commands usually coming from a Presenter.
  3. applyCommand() is a method defined in the Navigator interface that lets you perform a transition specified by a navigation command. The commands predefined by Cicerone are:
    • Forward: Opens a new View.
    • Back: Rolls back the last transition; it’s like pressing the back button.
    • BackTo: Rolls back to a specified View in your app.
    • Replace: Replaces the current View with a new one and starts a new transition chain.
  4. Maps the Forward command to a specified View in your app. In this case, you are specifying that if the forward command contains AddMovieActivity.TAG, then start AddMovieActivity.

You now need to set your navigator in the activity’s onResume() by adding the following line:

App.INSTANCE.cicerone.navigatorHolder.setNavigator(navigator)

You also need to remove it when your activity is no longer visible by overriding onPause(). Add the following line:

App.INSTANCE.cicerone.navigatorHolder.removeNavigator()

Now, add the following property to get an instance of your router:

private val router: Router? by lazy { App.INSTANCE.cicerone.router }

That’s it! Your Router is ready for use. Now it’s time to implement the Presenter.

The Presenter

Create a new package named presenter, and then add a new file named MainPresenter.kt. Replace everything inside with the following:

//1
class MainPresenter(private var view: MainContract.View?,
                    private var interactor: MainContract.Interactor?,
                    private val router: Router?) : MainContract.Presenter, MainContract.InteractorOutput {
  //2
  override fun addMovie() {
    router?.navigateTo(AddMovieActivity.TAG)
  }
  //3
  override fun deleteMovies(selectedMovies: HashSet<*>) {
    for (movie in selectedMovies) {
      interactor?.delete(movie as Movie)
    }
  }
  //4
  override fun onViewCreated() {
    view?.showLoading()
    interactor?.loadMovieList()?.observe((view as MainActivity), Observer { movieList ->
      if (movieList != null) {
        onQuerySuccess(movieList)
      } else {
        onQueryError()
      }
    })
  }
  //5
  override fun onDestroy() {
    view = null
    interactor = null
  }
  //6
  override fun onQuerySuccess(data: List<Movie>) {
    view?.hideLoading()
    view?.displayMovieList(data)
  }
  //7
  override fun onQueryError() {
    view?.hideLoading()
    view?.showMessage("Error Loading Data")
  }
}

Here’s the breakdown:

  1. MainPresenter implements MainContract.Presenter. This class takes a View, Router and an Interactor as constructor parameters.
  2. When the user taps + to add a new movie, you call addMovie(). This method uses router to navigate to AddMovieActivity.
  3. When the user presses the delete button to delete watched movies, you call deleteMovies(). You’ll use the interactor to remove movies from the Room database.
  4. You call onViewCreated() when the View is visible to the user. This method uses the loadMovieList() from the Interactor to retrieve a list of Movies. If the response is successful, you call onQuerySuccess(), otherwise you use onQuerryError() to send an error message to the user.
  5. Here, you use onDestroy() to remove the references to the View and Interactor.
  6. When the Interactor successfully returns a response, you call onQuerySuccess() to instruct the View to hide the progress bar and display a list of movies.
  7. When the Interactor is unable to retrieve the list of movies, you call onQueryError() to instruct the View to hide the progress bar and show an error message.

Note: In a real-world app you’d typically use a dependency injection library such as Dagger, Kodein or Koin to manage the dependencies.

The Interactor

Next, you need to implement the Interactor for the Main Module.

Create another new package named interactor and add a file named MainInteractor.kt. Replace everything inside with the following:

//1
class MainInteractor : MainContract.Interactor {
  //2
  private val movieList = MediatorLiveData<List<Movie>>()
  private val repository: MovieRepositoryImpl = MovieRepositoryImpl()
  //3
  init {
    getAllMovies()
  }
  //4
  override fun loadMovieList() = movieList
  //5
  override fun delete(movie: Movie) = repository.deleteMovie(movie)
  //6
  override fun getAllMovies() {
    movieList.addSource(repository.getSavedMovies()) { movies ->
      movieList.postValue(movies)
    }
  }
}

Here’s how it works:

  1. MainInteractor implements MainContract.Interactor.
  2. Here, you declare a movieList property that contains a LiveData list of movies and a repository property to hold a reference to MovieRepository.
  3. When the class is initialized, you immediately call getAllMovies().
  4. loadMovieList() returns a reference to movieList.
  5. delete() calls your repository’s deleteMovie() to delete the movie passed as a parameter.
  6. getAllMovies() adds your repository’s getSavedMovies() as a data source for movieList. When the data changes, the list automatically gets updated.

You may have noticed there’s an unresolved reference to AddMovie.TAG in both MainPresenter and MainActivity. For Cicerone to work correctly, you need to define TAGs on each activity.

Open AddMovieActivity.kt and add the following companion object to AddMovieActivity:

companion object {
  val TAG: String = "AddMovieActivity"
}

Then, open SearchMovieActivity.kt and add this one to SearchMovieActivity:

companion object {
  val TAG: String = "SearchMovieActivity"
}

The last step to finish the Main Module is to initialize MainPresenter inside MainActivity’s onCreate():

presenter = MainPresenter(this, MainInteractor(), router)

Now, call onViewCreated() inside onResume():

presenter.onViewCreated()

It’s time to implement the AddMovie module.

Implementing the AddMovie module

With the main module out the way, it’s time to apply the same architecture to the add movie module. You’ll again start with the View. Be sure to review the contract for the add movie module in the AddContract.kt file found at the root package.

The View

Open AddMovieActivity.kt inside view/activities, and make AddMovieActivity implement AddContract.View:

class AddMovieActivity : BaseActivity(), AddContract.View {

Press Control-I to implement the missing members.

Now that the View is ready, add the following properties for presenter, router and navigator:

var presenter: AddContract.Presenter? = null
private val router: Router? by lazy { App.INSTANCE.cicerone.router }

private val navigator: Navigator? by lazy {
  object : Navigator {
    override fun applyCommand(command: Command) {
      if (command is Back) {
        back()
      }
      if (command is Forward) {
        forward(command)
      }
    }

    private fun forward(command: Forward) {
      when (command.screenKey) {
        SearchMovieActivity.TAG -> startActivity(Intent(this@AddMovieActivity, SearchMovieActivity::class.java)
            .putExtra("title", titleEditText.text.toString()))
        MainActivity.TAG -> startActivity(Intent(this@AddMovieActivity, MainActivity::class.java)
            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK))
        else -> Log.e("Cicerone", "Unknown screen: " + command.screenKey)
      }
    }

    private fun back() {
      finish()
    }
  }
}

Just like the Main module, you need a navigator to process the commands that the router sends. The only difference is that, in this case, you have two possibilities for the forward command: SearchMovieActivity and MainMovieActivity.

To display a Snackbar with a message, replace the code inside showMessage():

addLayout.snack((msg), Snackbar.LENGTH_LONG) {
  action(getString(R.string.ok)) {
  }
}

To inform the Presenter that the user wants to add a new movie, add the following inside addMovieClicked():

presenter?.addMovies(titleEditText.text.toString(), yearEditText.text.toString())

Now, add the following code inside searchMovieClicked() to tell the Presenter that the user wants to query the TMDB API:

presenter?.searchMovies(titleEditText.text.toString())

Finally, override onResume(), onDestroy() and onPause():

override fun onResume() {
  super.onResume()
  App.INSTANCE.cicerone.navigatorHolder.setNavigator(navigator)
}

override fun onDestroy() {
  super.onDestroy()
  presenter?.onDestroy()
}

override fun onPause() {
  super.onPause()
  App.INSTANCE.cicerone.navigatorHolder.removeNavigator()
}

Similar to what you previously did for MainView, you need to set the navigator in onResume() and remove it in onPause().

The Presenter

Create a new file inside the presenter package, name it AddPresenter.kt and replace everything inside with the following:

//1
class AddPresenter(private var view: AddContract.View?,
                   private var interactor: AddContract.Interactor?,
                   private val router: Router?) : AddContract.Presenter {
  //2
  override fun onDestroy() {
    view = null
    interactor = null
  }
  //3    
  override fun addMovies(title: String, year: String) {
    if (title.isNotBlank()) {
      val movie = Movie(title = title, releaseDate = year)
      interactor?.addMovie(movie)
      router?.navigateTo(MainActivity.TAG)
    } else {
      view?.showMessage("You must enter a title")
    }
  }
  //4
  override fun searchMovies(title: String) {
    if (title.isNotBlank()) {
      router?.navigateTo(SearchMovieActivity.TAG)
    } else {
      view?.showMessage("You must enter a title")
    }
  }
}

Here’s the step-by-step:

  1. AddPresenter implements AddContract.Presenter and accepts a Router, an Interactor and a View as constructor parameters.
  2. onDestroy() removes the reference to the View and the Interactor.
  3. addMovies() uses the Interactor’s addMovie() to add a new movie to the Room database, passing in the title and year as a parameter. If title is blank, the View displays an error message to the user.
  4. searchMovies() uses router to navigate to SearchMovieActivity and passes title as an Extra value in the Intent. If title is empty, the View displays an error message as a Snackbar to the user.

The Interactor

Create a new file inside interactor, name it AddInteractor and replace everything inside with the following:

class AddInteractor : AddContract.Interactor {

  private val repository: MovieRepositoryImpl = MovieRepositoryImpl()

  override fun addMovie(movie: Movie) = repository.saveMovie(movie)

}

AddInteractor contains a single method, addMovie(), that uses the repository to add a new record in the Room database.

To create a new AddMoviePresenter instance, open AddMovieActivity.kt and add the following code inside onCreate():

presenter = AddPresenter(this, AddInteractor(), router)

Implementing SearchMovie

There’s only one module remaining: SearchMovie.

You’ve completed the main module and the add movie module, you’ll cap things off with the search module. Again, be sure to review the contract for the search module in the SearchContract.kt file found in the root package of the project. As usual, you’ll start by making changes to the View.

The View

Open SearchMovieActivity.kt inside view/activities and make SearchMovieActivity implement SearchContract.View:

class SearchMovieActivity : BaseActivity(), SearchContract.View {

Remember to implement all of the missing members by using the Control-I shortcut. Add the following properties for presenter, router and navigator:

private var presenter: SearchContract.Presenter? = null
private val router: Router? by lazy { App.INSTANCE.cicerone.router }

private val navigator: Navigator? by lazy {
  object : Navigator {
    override fun applyCommand(command: Command) {
      if (command is Back) {
        back()
      }
      if (command is Forward) {
        forward(command)
      }
    }

    private fun forward(command: Forward) {
      when (command.screenKey) {
        MainActivity.TAG -> startActivity(Intent(this@SearchMovieActivity, MainActivity::class.java)
            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK))
        else -> Log.e("Cicerone", "Unknown screen: " + command.screenKey)
      }
    }

    private fun back() {
      finish()
    }
  }
}

Just like the Main and AddMovie modules, you’ll need a Navigator for the Router and a SearchPresenter instance.

Update showLoading() and hideLoading(), like so:

override fun showLoading() {
  searchProgressBar.visibility = View.VISIBLE
  searchRecyclerView.isEnabled = false
}

override fun hideLoading() {
  searchProgressBar.visibility = View.GONE
  searchRecyclerView.isEnabled = true
}

These methods toggle the display property pf the progress bar and RecyclerView list of movies. When one is shown, the other is hidden. These are usually called before and after making a network call to retrieve the movies.

Add the following code inside showMessage():

searchLayout.snack(getString(R.string.network_error), Snackbar.LENGTH_INDEFINITE) {
  action(getString(R.string.ok)) {
    val title = intent.extras.getString("title")
    presenter?.searchMovies(title)
  }
}

This method displays a Snackbar with a network error message. When the user clicks OK, the Presenter tries to retrieve the list of movies again.

For displayMovieList(), add this code to attach a new adapter with the movie list data passed as a parameter:

adapter = SearchListAdapter(movieList) { movie -> presenter?.movieClicked(movie) }
searchRecyclerView.adapter = adapter

Add the following lines to displayConfirmation():

searchLayout.snack("Add ${movie?.title} to your list?", Snackbar.LENGTH_LONG) {
  action(getString(R.string.ok)) {
    presenter?.addMovieClicked(movie)
  }
}

Here, you’re displaying a confirmation message before asking the presenter to add a new movie to the database.

Finally, update onDestroy(), onPause() and onResume():

override fun onResume() {
  super.onResume()
  val title = intent.extras.getString("title")
  presenter?.searchMovies(title)
  App.INSTANCE.cicerone.navigatorHolder.setNavigator(navigator)
}

override fun onPause() {
  super.onPause()
  App.INSTANCE.cicerone.navigatorHolder.removeNavigator()
}

override fun onDestroy() {
  super.onDestroy()
  presenter?.onDestroy()
}

Like you did in MainActivity and AddMovieActivity, you use these methods to set the Navigator and to tell the Presenter when the View is visible to the user.

The Presenter

Create a new file inside presenter, name it SearchPresenter.kt and replace everything inside with the following:

//1
class SearchPresenter(private var view: SearchContract.View?, private var interactor: SearchContract.Interactor?, val router: Router?) : SearchContract.Presenter, SearchContract.InteractorOutput {
  //2
  override fun searchMovies(title: String) {
    view?.showLoading()
    interactor?.searchMovies(title)?.observe(view as SearchMovieActivity, Observer { movieList ->
      if (movieList == null) {
        onQueryError()
      } else {
        onQuerySuccess(movieList)
      }
    })
  }
  //3
  override fun addMovieClicked(movie: Movie?) {
    interactor?.addMovie(movie)
    router?.navigateTo(MainActivity.TAG)
  }
  //4
  override fun movieClicked(movie: Movie?) {
    view?.displayConfirmation(movie)
  }
  //5
  override fun onDestroy() {
    view = null
    interactor = null
  }
  //6
  override fun onQuerySuccess(data: List<Movie>) {
    view?.hideLoading()
    view?.displayMovieList(data)
  }
  //7
  override fun onQueryError() {
    view?.hideLoading()
    view?.showMessage("Error")
  }
}
  1. SearchPresenter implements SearchContract.Presenter and accepts a Router, a View and an Interactor as constructor parameters.
  2. searchMovies() uses the Interactor to retrieve a list of movies and observes it until there’s a response. If the response is successful, you pass the new list of movies as a parameter toonQuerySuccess(), otherwise you call onQueryError().
  3. addMovieClicked() uses the Interactor to save a new movie to the database and the Router to navigate to MainActivity.
  4. movieClicked() is a simple method that asks for confirmation from the user using the View.
  5. onDestroy() removes the references to the View and Interactor.
  6. onQuerySuccess() tells the View to display a new movie list to the user.
  7. onQueryError() displays an error message to the user.

The Interactor

Create a new file inside interactor, name it SearchInteractor.kt and replace everything inside with the following:

//1
class SearchInteractor : SearchContract.Interactor {
  //2
  private val repository: MovieRepositoryImpl = MovieRepositoryImpl()
  //3
  override fun searchMovies(title: String): LiveData<List<Movie>?> = repository.searchMovies(title)
  //4
  override fun addMovie(movie: Movie?) {
    movie?.let {
      repository.saveMovie(movie)
    }
  }
}
  1. SearchInteractor implements SearchContract.Interactor.
  2. Here, you create a new repository property for MovieRepository.
  3. searchMovies() uses repository to get a list of movies that match the title passed as a parameter.
  4. addMovie() uses repository to save a movie in the Room database.

Open SearchMovieActivity.kt and instantiate a new instance of SearchPresenter by adding the following line to onCreate():

presenter = SearchPresenter(this, SearchInteractor(), router)

That’s it! You’re ready to test your app and see it in action. Build and run, and you’ll see a fully functioning app.

Key points

  • View is the component that receives UI actions from the user and sends them to the Presenter.
  • Interactor is the component in charge of interacting with your backend such as your databases and web services.
  • Presenter is like the commander of your architecture. It’s in charge of coordinating your Views, Interactors and Routers.
  • Entity is the component that represents your app’s data. It’s usually represented as data classes in Kotlin.
  • Router is the component that manages the navigation between the Views in your app.

Where to go from here?

VIPER is a great architecture pattern that focuses on providing the maximum level of modularity for your Android projects. It allows you to create app’s that are maintainable, scalable and easy to test.

In the next chapter, you’ll learn how to test the architecture of your app by creating unit tests for your Presenters and using mockito to mock your Views and Interactors.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.