title: Network Awareness from Repository using RxJava
body: <p>I am having trouble to find a proper solution to make view/viewmodel aware of network status, specially using RxJava.</p>
  
  <p>I tried to follow <a href="https://developer.android.com/topic/libraries/architecture/guide.html#addendum" rel="nofollow noreferrer">Google's NetworkBoundResource</a> and <a href="https://medium.com/@iammert/offline-app-with-rxjava-2-and-room-ccd0b5c18101" rel="nofollow noreferrer">Iammert's networkBoundResouce</a> (also tried <a href="https://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample" rel="nofollow noreferrer">GithubBrowserSample</a>
  but I just don't really understand the code or how it should be encapsulated.</p>
  
  <p>I'm looking for how can I implement NetworkBoundResource with RxJava2 in a way that the data gets refreshed automatically with the Database.</p>
  
  <p>I will show what I currently have:</p>
  
  <p>A fragment with arch.lifecycle.ViewModel</p>
  
  <pre><code>public class LoginViewModel extends ViewModel {
  
      static final int SCREEN_JUST_BUTTONS = 0;
      static final int SCREEN_LOGIN_INPUT = 1;
      static final int SCREEN_PASS_INPUT = 2;
  
      // using a PublishSubject because we are not interested in the last object that was emitted
      // before subscribing. Like this we avoid displaying the snackbar multiple times
      @NonNull
      private final PublishSubject&lt;Integer&gt; snackbarText;
  
      private int SCREEN = 0;
  
      private LoginUiModel loginUiModel;
      @NonNull
      private BaseSchedulerProvider schedulerProvider;
      @NonNull
      private UserRepository userRepository;
  
      @Inject
      LoginViewModel(@NonNull final BaseSchedulerProvider schedulerProvider, @NonNull final UserRepository userRepository) {
          this.schedulerProvider = schedulerProvider;
          this.userRepository = userRepository;
          snackbarText = PublishSubject.create();
          disposables = new CompositeDisposable();
          loginUiModel = new LoginUiModel();
      }
  
      int getScreen() {
          return SCREEN;
      }
  
      void setScreen(final int screen) {
          this.SCREEN = screen;
      }
  
      @NonNull
      Observable&lt;Integer&gt; getSnackbarText() {
          return snackbarText;
      }
  
      LoginUiModel getLoginUiModel() {
          return loginUiModel;
      }
  
  
      public Single&lt;User&gt; login(final String login, final String password) {
          return userRepository.login(login, password)
                               .subscribeOn(schedulerProvider.io())
                               .observeOn(schedulerProvider.ui())
                               .doOnError(this::handleErrorLogin);
      }
  
      private void handleErrorLogin(final Throwable t) {
          if (t instanceof HttpException) {
              showSnackbar(R.string.Login_invalid_input);
          } else if (t instanceof IOException) {
              showSnackbar(R.string.Common_no_connection);
          } else {
              showSnackbar(R.string.Common_error_loading);
          }
      }
  
      private void showSnackbar(@StringRes int textId) {
          snackbarText.onNext(textId);
      }
  
      void forgotPassword() {
          if (isValidEmail(loginUiModel.getEmail())) {
              showSnackbar(R.string.Login_password_sent);
          } else {
              showSnackbar(R.string.Login_invalid_login);
          }
          //TODO;
      }
  
      boolean isValidEmail(@Nullable final String text) {
          return text != null &amp;&amp; text.trim().length() &gt; 3 &amp;&amp; text.contains("@");
      }
  }
  </code></pre>
  
  <p>and in my UserRepository:</p>
  
  <pre><code>@Singleton
  public class UserRepository {
  
      @NonNull
      private final UserDataSource userRemoteDataSource;
  
      @NonNull
      private final UserDataSource userLocalDataSource;
  
      @NonNull
      private final RxSharedPreferences preferences;
  
      @Inject
      UserRepository(@NonNull RxSharedPreferences rxSharedPreferences, @NonNull @Remote UserDataSource userRemoteDataSource, @NonNull @Local UserDataSource
              userLocalDataSource) {
          this.userRemoteDataSource = userRemoteDataSource;
          this.userLocalDataSource = userLocalDataSource;
          preferences = rxSharedPreferences;
      }
  
      @NonNull
      public Single&lt;User&gt; login(final String email, final String password) {
          return userRemoteDataSource
                  .login(email, password) // busca do webservice
                  .flatMap(userLocalDataSource::saveUser) // salva no banco local
                  .flatMap(user -&gt; userLocalDataSource.login(user.getEmail(), user.getPassword())) // busca usuário no banco local como SSOT
                  .doOnSuccess(this::saveUserId); // salva o ID do usuário nas preferências.
      }
  
      public Single&lt;User&gt; saveUser(String email, String password, String name, String phone, String company) {
          User user = new User(0, name, email, password, phone, company);
  
          return saveUser(user);
      }
  
      private Single&lt;User&gt; saveUser(final User user) {
          return userRemoteDataSource.saveUser(user)
                                     .flatMap(userLocalDataSource::saveUser)
                                     .doOnSuccess(this::saveUserId); // salva o ID do usuário nas preferências.
      }
  
      private void saveUserId(final User user) {
          preferences.getLong(Consts.KEY_USER_ID__long).set(user.getUserId());
      }
  
      public Flowable&lt;User&gt; getUserUsingSystemPreferences() {
          Preference&lt;Long&gt; userIdPref = preferences.getLong(Consts.KEY_USER_ID__long, -1L);
          if (userIdPref.get() &lt;= 0) {
              //nao deveria acontecer, mas se acontecer, reseta:
              userIdPref.delete();
              return Flowable.error(new RuntimeException("Not logged in."));
          } else if (!userIdPref.isSet()) {
              return Flowable.error(new RuntimeException("Not logged in."));
          } else {
              return userLocalDataSource.getUser(String.valueOf(userIdPref.get()));
              }
          }
      }
  </code></pre>
  
  <p>and my UserLocalDataSource:</p>
  
  <pre><code>@Singleton
  public class UserLocalDataSource implements UserDataSource {
  
      private final UserDao userDao;
  
      @Inject
      UserLocalDataSource(UserDao userDao) {
          this.userDao = get(userDao);
      }
  
      @NonNull
      @Override
      public Single&lt;User&gt; login(final String email, final String password) {
          return userDao.login(email, password);
      }
  
      @Override
      public Flowable&lt;User&gt; getUser(final String id) {
          return userDao.getUserById(Long.valueOf(id));
      }
  
      @Override
      public Single&lt;User&gt; saveUser(final User user) {
          return Single.create(e -&gt; {
              long id = userDao.insert(user);
              user.setUserId(id);
              e.onSuccess(user);
          });
      }
  
      @Override
      public Completable deleteUser(final String id) {
          return getUser(id).flatMapCompletable(user -&gt; Completable.create(e -&gt; {
              int numberOfDeletedRows = userDao.delete(user);
              if (numberOfDeletedRows &gt; 0) {
                  e.onComplete();
              } else {
                  e.onError(new Exception("Tried to delete a non existing user"));
              }
          }));
      }
  }
  </code></pre>
  
  <p>and the user remote data source:</p>
  
  <pre><code>public class UserRemoteDataSource implements UserDataSource {
  
      private final ISApiService apiService;
  
      @Inject
      UserRemoteDataSource(ISApiService apiService) {
          this.apiService = get(apiService);
      }
  
      @NonNull
      @Override
      public Single&lt;User&gt; login(final String email, final String password) {
          return apiService.login(email, password);
      }
  
      @Override
      public Flowable&lt;User&gt; getUser(final String id) {
          throw new RuntimeException("NO getUser endpoint");
      }
  
      @Override
      public Single&lt;User&gt; saveUser(final User user) {
          Map&lt;String, String&gt; params = new HashMap&lt;&gt;();
  
          params.put(ISApiConsts.EMAIL, user.getEmail());
          params.put(ISApiConsts.NAME, user.getName());
          params.put(ISApiConsts.PASSWORD, user.getPassword());
          params.put(ISApiConsts.PHONE, user.getPhone());
          params.put(ISApiConsts.COMPANY, user.getCompany());
  
          return apiService.signUp(params);
      }
  
      @Override
      public Completable deleteUser(final String id) {
          //can't delete user.
          return Completable.complete();
      }
  }
  </code></pre>
  
  <p>UserDao interface ( It is using Room)</p>
  
  <pre><code>@Dao
  public interface UserDao extends BaseDao&lt;User&gt; {
  
      @Query("SELECT * FROM user WHERE user_id = :id")
      Flowable&lt;User&gt; getUserById(long id);
  
      @Query("SELECT * FROM user WHERE email = :login AND password = :password")
      Single&lt;User&gt; login(String login, String password);
  
  }
  </code></pre>
  
  <p>What I want to know exactly is: How can I encapsulate my domain while still get access to possible problems like network and show it to the user using RxJava in the MVVM pattern?</p>