├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── manuelvicnt
│ │ └── com
│ │ └── rxjava_android_structure
│ │ ├── ApplicationTest.java
│ │ └── networking
│ │ └── registration
│ │ └── RegistrationAPIServiceTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── accountResponse.txt
│ │ ├── gamesResponse.txt
│ │ ├── loginResponse.txt
│ │ └── registrationResponse.txt
│ ├── java
│ │ └── manuelvicnt
│ │ │ └── com
│ │ │ └── rxjava_android_structure
│ │ │ ├── HomeActivity.java
│ │ │ ├── LandingActivity.java
│ │ │ ├── base
│ │ │ └── BaseFragment.java
│ │ │ ├── data
│ │ │ ├── AuthenticationManager.java
│ │ │ ├── DataManager.java
│ │ │ └── PrivateSharedPreferencesManager.java
│ │ │ ├── home
│ │ │ ├── HomeFragment.java
│ │ │ └── HomeViewModel.java
│ │ │ ├── login
│ │ │ ├── LoginFragment.java
│ │ │ └── LoginViewModel.java
│ │ │ ├── model
│ │ │ └── UserData.java
│ │ │ ├── networking
│ │ │ ├── AuthenticationRequestManager.java
│ │ │ ├── UserDataRequestManager.java
│ │ │ ├── account
│ │ │ │ ├── AccountAPIService.java
│ │ │ │ ├── AccountRequest.java
│ │ │ │ ├── AccountResponse.java
│ │ │ │ ├── IAccountAPI.java
│ │ │ │ └── exception
│ │ │ │ │ └── AccountTechFailureException.java
│ │ │ ├── games
│ │ │ │ ├── GamesAPIService.java
│ │ │ │ ├── GamesRequest.java
│ │ │ │ ├── GamesResponse.java
│ │ │ │ ├── IGamesAPI.java
│ │ │ │ └── exception
│ │ │ │ │ └── GamesTechFailureException.java
│ │ │ ├── login
│ │ │ │ ├── ILoginAPI.java
│ │ │ │ ├── LoginAPIService.java
│ │ │ │ ├── LoginRequest.java
│ │ │ │ ├── LoginResponse.java
│ │ │ │ └── exception
│ │ │ │ │ ├── LoginInternalException.java
│ │ │ │ │ └── LoginTechFailureException.java
│ │ │ ├── mock
│ │ │ │ ├── MockHttpClient.java
│ │ │ │ └── RestAdapterFactory.java
│ │ │ ├── registration
│ │ │ │ ├── IRegistrationAPI.java
│ │ │ │ ├── RegistrationAPIService.java
│ │ │ │ ├── RegistrationRequest.java
│ │ │ │ ├── RegistrationResponse.java
│ │ │ │ └── exception
│ │ │ │ │ ├── RegistrationInternalException.java
│ │ │ │ │ ├── RegistrationNicknameAlreadyExistsException.java
│ │ │ │ │ └── RegistrationTechFailureException.java
│ │ │ └── userdata
│ │ │ │ └── UserDataResponse.java
│ │ │ └── registration
│ │ │ ├── RegistrationFragment.java
│ │ │ └── RegistrationViewModel.java
│ └── res
│ │ ├── layout
│ │ ├── activity_home.xml
│ │ ├── activity_landing.xml
│ │ ├── fragment_home.xml
│ │ ├── fragment_login.xml
│ │ └── fragment_registration.xml
│ │ ├── menu
│ │ └── menu_startup.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── manuelvicnt
│ └── com
│ └── rxjava_android_structure
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | # RxJava Android Structure
2 |
3 | Implementation of the structure explained in this blog in Medium
4 | https://medium.com/@manuelvicnt/rxjava-android-mvvm-app-structure-with-retrofit-a5605fa32c00
5 |
6 | How to use RxJava in a organised way with Retrofit as the networking library.
7 |
8 | ### Happy Path
9 | There are some things to have into account when exploring the project:
10 | - RegistrationAPIService throws more than one exception for different networking errors (all are handled in the RegistrationFragment).
11 | - RegistrationAPIService gets the status code from a failure response.
12 | - LoginAPIService gets the status code from a successful response.
13 | - After Registration, we're going to log the user in.
14 | - After Login, we're going to get the user data.
15 | - We allow Pull to refresh in the Home Activity (this means we will have to create different Subjects/Subscriber per request).
16 | - We process the user data information when both request come. If one of them fails, we don't want to update the data.
17 |
18 | ### Set up
19 | When the registration is successful, it stores a value in SharedPreferences (that's how it goes to Login or Registration). To force registration, you can uninstall the app and install it again or clear cache data in the App info (Settings > Apps > Select App).
20 |
21 | ### Unit Testing
22 | RegistrationAPIService is tested. The rest of the classes can be tested in the same way. Be aware of threading problems.
23 |
24 | ### Sad Paths
25 | You can change the response of the network request with the files stored in the assets folder. Change the 200 response for a 400, for example. That gives you the ability of exploring different sad paths.
26 |
27 | One good example would be: Change the accountResponse.txt to fail (400 response instead of 200). Run the app from registration, it will fail in the account response and the login screen will appear.
28 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'me.tatarka.retrolambda'
3 |
4 | android {
5 | compileSdkVersion 22
6 | buildToolsVersion "22.0.1"
7 |
8 | defaultConfig {
9 | applicationId "manuelvicnt.com.rxjava_android_structure"
10 | minSdkVersion 16
11 | targetSdkVersion 22
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | compile fileTree(dir: 'libs', include: ['*.jar'])
25 |
26 | compile 'com.android.support:appcompat-v7:22.2.0'
27 | compile 'com.android.support:design:22.2.0'
28 |
29 | compile 'com.jakewharton:butterknife:7.0.1'
30 | compile 'com.squareup.retrofit:retrofit:1.9.0'
31 | compile 'io.reactivex:rxandroid:1.0.1'
32 | compile 'io.reactivex:rxjava:1.0.14'
33 |
34 | androidTestCompile 'org.mockito:mockito-core:1.9.5'
35 | androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
36 | androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
37 | androidTestCompile ('junit:junit:4.11') {
38 | exclude module: 'hamcrest-core'
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/josevvivo/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/manuelvicnt/com/rxjava_android_structure/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/manuelvicnt/com/rxjava_android_structure/networking/registration/RegistrationAPIServiceTest.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.networking.registration;
2 |
3 | import android.content.Context;
4 | import android.test.InstrumentationTestCase;
5 |
6 | import org.junit.After;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.mockito.Mock;
10 | import org.mockito.MockitoAnnotations;
11 |
12 | import java.util.ArrayList;
13 |
14 | import manuelvicnt.com.rxjava_android_structure.data.PrivateSharedPreferencesManager;
15 | import manuelvicnt.com.rxjava_android_structure.networking.registration.exception.RegistrationNicknameAlreadyExistsException;
16 | import manuelvicnt.com.rxjava_android_structure.networking.registration.exception.RegistrationTechFailureException;
17 | import retrofit.RestAdapter;
18 | import retrofit.RetrofitError;
19 | import retrofit.client.Response;
20 | import rx.Observable;
21 | import rx.observers.TestSubscriber;
22 |
23 | import static org.mockito.Matchers.any;
24 | import static org.mockito.Matchers.anyObject;
25 | import static org.mockito.Mockito.doReturn;
26 | import static org.mockito.Mockito.verify;
27 |
28 | /**
29 | * Created by ManuelVivo on 03/10/15.
30 | */
31 | public class RegistrationAPIServiceTest extends InstrumentationTestCase {
32 |
33 | @Mock IRegistrationAPI mockRegistrationAPI;
34 | @Mock RestAdapter mockRestAdapter;
35 | @Mock PrivateSharedPreferencesManager mockPrivateSharedPreferencesManager;
36 |
37 | private RegistrationResponse registrationResponse;
38 | private RegistrationRequest registrationRequest;
39 |
40 | private RegistrationAPIService registrationAPIService;
41 |
42 | @Before
43 | protected void setUp() throws Exception {
44 | super.setUp();
45 |
46 | MockitoAnnotations.initMocks(this);
47 |
48 | registrationResponse = new RegistrationResponse();
49 | registrationRequest = new RegistrationRequest("", "");
50 | registrationAPIService = new RegistrationAPIService(mockRestAdapter, mockPrivateSharedPreferencesManager);
51 |
52 | doReturn(mockRegistrationAPI).when(mockRestAdapter).create(any(Class.class));
53 | registrationAPIService.setRegistrationAPI(mockRegistrationAPI);
54 | }
55 |
56 | @After
57 | public void tearDown() {
58 |
59 | registrationResponse = null;
60 | registrationRequest = null;
61 | registrationAPIService = null;
62 | }
63 |
64 | @Test
65 | public void testRegister_ReturnsRegistrationResponse() {
66 |
67 | doReturn(Observable.just(registrationResponse)).
68 | when(mockRegistrationAPI).register(any(RegistrationRequest.class));
69 |
70 | TestSubscriber testSubscriber = new TestSubscriber();
71 |
72 | registrationAPIService.register(registrationRequest)
73 | .subscribe(testSubscriber);
74 |
75 | RegistrationResponse result = (RegistrationResponse) testSubscriber.getOnNextEvents().get(0);
76 | assertEquals(registrationResponse, result);
77 | }
78 |
79 | @Test
80 | public void testRegister_SetsRequestingToTrueWhenSubscribed() {
81 |
82 | // Avoid threading problems, it finishes as soon as someone is subscribed.
83 | doReturn(Observable.never()).when(mockRegistrationAPI).register(anyObject());
84 |
85 | registrationAPIService.register(registrationRequest).subscribe();
86 |
87 | assertTrue(registrationAPIService.isRequestingRegistration());
88 | }
89 |
90 | @Test
91 | public void testRegister_SetsRequestingToFalseWhenOnError() {
92 |
93 | // We have to wrap it around a try-catch since is going to throw the exception
94 | // when onError is called
95 | doReturn(Observable.error(null)).when(mockRegistrationAPI).register(anyObject());
96 |
97 | try {
98 | registrationAPIService.register(registrationRequest).subscribe();
99 | } catch (Exception e) {
100 | // Do Nothing
101 | }
102 |
103 | assertFalse(registrationAPIService.isRequestingRegistration());
104 | }
105 |
106 | @Test
107 | public void testRegister_SetsRequestingToFalseWhenOnComplete() {
108 |
109 | // Avoid threading problems, it finishes as soon as someone is subscribed.
110 | doReturn(Observable.empty()).when(mockRegistrationAPI).register(anyObject());
111 |
112 | registrationAPIService.register(registrationRequest).subscribe();
113 |
114 | assertFalse(registrationAPIService.isRequestingRegistration());
115 | }
116 |
117 | @Test
118 | public void testRegister_throwsRegistrationNicknameAlreadyExistsExceptionWhen401Error() {
119 |
120 | RetrofitError retrofitError = RetrofitError.httpError("", new Response("", 401, "", new ArrayList<>(), null), null, null);
121 |
122 | doReturn(Observable.error(retrofitError)).when(mockRegistrationAPI).register(anyObject());
123 |
124 | TestSubscriber testSubscriber = new TestSubscriber();
125 | registrationAPIService.register(registrationRequest).subscribe(testSubscriber);
126 |
127 | Throwable resultError = (Throwable) testSubscriber.getOnErrorEvents().get(0);
128 | assertTrue(resultError instanceof RegistrationNicknameAlreadyExistsException);
129 | }
130 |
131 | @Test
132 | public void testRegister_throwsRegistrationTechFailureExceptionWhenOtherError() {
133 |
134 | RetrofitError retrofitError = RetrofitError.httpError("", new Response("", 400, "", new ArrayList<>(), null), null, null);
135 |
136 | doReturn(Observable.error(retrofitError)).when(mockRegistrationAPI).register(anyObject());
137 |
138 | TestSubscriber testSubscriber = new TestSubscriber();
139 | registrationAPIService.register(registrationRequest).subscribe(testSubscriber);
140 |
141 | Throwable resultError = (Throwable) testSubscriber.getOnErrorEvents().get(0);
142 | assertTrue(resultError instanceof RegistrationTechFailureException);
143 | }
144 |
145 | @Test
146 | public void testRegister_storesUserNicknameOnSharedPreferencesWhenResponseSuccessful() {
147 |
148 | String expected = "nickname";
149 | RegistrationRequest registrationRequest = new RegistrationRequest(expected, "");
150 |
151 | doReturn(Observable.just(registrationResponse)).when(mockRegistrationAPI).register(registrationRequest);
152 |
153 | // Threading problems here. We have to make sure that it's called onNext before checking it.
154 | // One way of doing it is forcing the Observable to finish, using a testSubscriber and calling onCompleted
155 | TestSubscriber testSubscriber = new TestSubscriber();
156 | registrationAPIService.register(registrationRequest).subscribe(testSubscriber);
157 | testSubscriber.onCompleted();
158 |
159 | verify(mockPrivateSharedPreferencesManager).storeUserNickname(expected);
160 | }
161 |
162 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/assets/accountResponse.txt:
--------------------------------------------------------------------------------
1 | {
2 | "Header": {
3 | "Code": 200,
4 | "Message": "Success"
5 | },
6 | "Body": {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/assets/gamesResponse.txt:
--------------------------------------------------------------------------------
1 | {
2 | "Header": {
3 | "Code": 200,
4 | "Message": "Success"
5 | },
6 | "Body": {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/assets/loginResponse.txt:
--------------------------------------------------------------------------------
1 | {
2 | "Header": {
3 | "Code": 200,
4 | "Message": "Success"
5 | },
6 | "Body": {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/assets/registrationResponse.txt:
--------------------------------------------------------------------------------
1 | {
2 | "Header": {
3 | "Code": 200,
4 | "Message": "Success"
5 | },
6 | "Body": {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/HomeActivity.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.Toolbar;
6 |
7 | import butterknife.Bind;
8 | import butterknife.ButterKnife;
9 | import manuelvicnt.com.rxjava_android_structure.base.BaseFragment;
10 | import manuelvicnt.com.rxjava_android_structure.home.HomeFragment;
11 | import manuelvicnt.com.rxjava_android_structure.registration.RegistrationFragment;
12 |
13 | public class HomeActivity extends AppCompatActivity {
14 |
15 | @Bind(R.id.toolbar) Toolbar toolbar;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 |
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_landing);
22 |
23 | ButterKnife.bind(this);
24 |
25 | setSupportActionBar(toolbar);
26 |
27 | if (savedInstanceState != null) {
28 | return;
29 | }
30 |
31 | setTitle("Home");
32 | HomeFragment homeFragment = new HomeFragment();
33 | getSupportFragmentManager().beginTransaction()
34 | .add(R.id.fragment_container, homeFragment).commit();
35 | }
36 |
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/LandingActivity.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.Toolbar;
7 |
8 | import butterknife.Bind;
9 | import butterknife.ButterKnife;
10 | import manuelvicnt.com.rxjava_android_structure.data.PrivateSharedPreferencesManager;
11 | import manuelvicnt.com.rxjava_android_structure.login.LoginFragment;
12 | import manuelvicnt.com.rxjava_android_structure.registration.RegistrationFragment;
13 |
14 | public class LandingActivity extends AppCompatActivity {
15 |
16 | @Bind(R.id.toolbar) Toolbar toolbar;
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 |
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_landing);
23 |
24 | ButterKnife.bind(this);
25 |
26 | setSupportActionBar(toolbar);
27 |
28 | if (savedInstanceState != null) {
29 | return;
30 | }
31 |
32 |
33 | String userNickname = PrivateSharedPreferencesManager.getInstance(getApplicationContext()).getUserNickname();
34 | Fragment initialFragment;
35 |
36 | if (userNickname == null || userNickname.isEmpty()) {
37 |
38 | setTitle("Registration");
39 | initialFragment = new RegistrationFragment();
40 | } else {
41 |
42 | setTitle("Login");
43 | initialFragment = new LoginFragment();
44 | }
45 |
46 | getSupportFragmentManager().beginTransaction()
47 | .add(R.id.fragment_container, initialFragment).commit();
48 |
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/base/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.base;
2 |
3 | import android.app.ProgressDialog;
4 | import android.support.v4.app.Fragment;
5 |
6 | /**
7 | * Created by ManuelVivo on 03/10/15.
8 | */
9 | public abstract class BaseFragment extends Fragment {
10 |
11 | protected ProgressDialog progressDialog;
12 |
13 | @Override
14 | public void onResume() {
15 |
16 | super.onResume();
17 | subscribeForNetworkRequests();
18 | }
19 |
20 | @Override
21 | public void onPause() {
22 |
23 | super.onPause();
24 | unsubscribeFromNetworkRequests();
25 | }
26 |
27 | protected void hideProgressDialog() {
28 |
29 | if (progressDialog != null && progressDialog.isShowing()) {
30 | progressDialog.dismiss();
31 | }
32 | }
33 |
34 | protected abstract void subscribeForNetworkRequests();
35 | protected abstract void unsubscribeFromNetworkRequests();
36 | protected abstract void reconnectWithNetworkRequests();
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/data/AuthenticationManager.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.data;
2 |
3 | /**
4 | * Created by ManuelVivo on 03/10/15.
5 | */
6 | public class AuthenticationManager {
7 |
8 | private static AuthenticationManager instance;
9 |
10 | private String nickname;
11 | private String password;
12 | private boolean userLogged;
13 |
14 | private AuthenticationManager() {
15 |
16 | }
17 |
18 | public static AuthenticationManager getInstance() {
19 |
20 | synchronized (AuthenticationManager.class) {
21 | if (instance == null) {
22 | instance = new AuthenticationManager();
23 | }
24 |
25 | return instance;
26 | }
27 | }
28 |
29 | public String getNickname() {
30 | return nickname;
31 | }
32 |
33 | public void setNickname(String nickname) {
34 | this.nickname = nickname;
35 | }
36 |
37 | public String getPassword() {
38 | return password;
39 | }
40 |
41 | public void setPassword(String password) {
42 | this.password = password;
43 | }
44 |
45 | public void logUserIn() {
46 | userLogged = true;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/data/DataManager.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.data;
2 |
3 | import manuelvicnt.com.rxjava_android_structure.model.UserData;
4 |
5 | /**
6 | * Created by ManuelVivo on 03/10/15.
7 | */
8 | public class DataManager {
9 |
10 | private static DataManager instance;
11 | private UserData userData;
12 |
13 | private DataManager() {
14 |
15 | userData = new UserData();
16 | }
17 |
18 | public static DataManager getInstance() {
19 |
20 | synchronized (DataManager.class) {
21 | if (instance == null) {
22 | instance = new DataManager();
23 | }
24 |
25 | return instance;
26 | }
27 | }
28 |
29 | public UserData getUserData() {
30 |
31 | return userData;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/data/PrivateSharedPreferencesManager.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.data;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | /**
7 | * Created by ManuelVivo on 03/10/15.
8 | */
9 | public class PrivateSharedPreferencesManager {
10 |
11 | public static final String SHARED_PREFERENCES_KEY = "com.manuelvicnt.rxjava_android_structure";
12 | public static final String NICKNAME_KEY = "nickname";
13 |
14 | private static PrivateSharedPreferencesManager instance;
15 |
16 | private SharedPreferences privateSharedPreferences;
17 |
18 | private PrivateSharedPreferencesManager(Context context) {
19 |
20 | this.privateSharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
21 | }
22 |
23 | public static PrivateSharedPreferencesManager getInstance(Context context) {
24 |
25 | synchronized (PrivateSharedPreferencesManager.class) {
26 | if (instance == null) {
27 | instance = new PrivateSharedPreferencesManager(context);
28 | }
29 | return instance;
30 | }
31 | }
32 |
33 | private void storeStringInSharedPreferences(String key, String content) {
34 |
35 | SharedPreferences.Editor editor = privateSharedPreferences.edit();
36 | editor.putString(key, content);
37 | editor.apply();
38 | }
39 |
40 | private String getStringFromSharedPreferences(String key) {
41 |
42 | return privateSharedPreferences.getString(key, "");
43 | }
44 |
45 | public void storeUserNickname(String nickname) {
46 |
47 | storeStringInSharedPreferences(NICKNAME_KEY, nickname);
48 | }
49 |
50 | public String getUserNickname() {
51 |
52 | return getStringFromSharedPreferences(NICKNAME_KEY);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/manuelvicnt/com/rxjava_android_structure/home/HomeFragment.java:
--------------------------------------------------------------------------------
1 | package manuelvicnt.com.rxjava_android_structure.home;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.widget.SwipeRefreshLayout;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import butterknife.Bind;
11 | import butterknife.ButterKnife;
12 | import manuelvicnt.com.rxjava_android_structure.R;
13 | import manuelvicnt.com.rxjava_android_structure.base.BaseFragment;
14 | import manuelvicnt.com.rxjava_android_structure.networking.UserDataRequestManager;
15 | import rx.Subscriber;
16 | import rx.Subscription;
17 | import rx.android.schedulers.AndroidSchedulers;
18 |
19 | /**
20 | * Created by ManuelVivo on 03/10/15.
21 | */
22 | public class HomeFragment extends BaseFragment {
23 |
24 | private HomeViewModel homeViewModel;
25 | private Subscription userDataSubscription;
26 |
27 | @Bind(R.id.user_data) TextView userDataText;
28 | @Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefreshLayout;
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 |
34 | UserDataRequestManager userDataRequestManager =
35 | UserDataRequestManager.getInstance(getActivity().getApplicationContext());
36 | homeViewModel = new HomeViewModel(userDataRequestManager);
37 | }
38 |
39 | @Override
40 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
41 | Bundle savedInstanceState) {
42 |
43 | View rootView = inflater.inflate(R.layout.fragment_home, container, false);
44 | ButterKnife.bind(this, rootView);
45 |
46 | showRandomSuccessfulMessage();
47 |
48 | setupRefreshLayout();
49 | return rootView;
50 | }
51 |
52 | @Override
53 | protected void subscribeForNetworkRequests() {
54 |
55 | userDataSubscription = homeViewModel.getUserDataSubject()
56 | .observeOn(AndroidSchedulers.mainThread())
57 | .subscribe(new UserDataSubscriber());
58 | }
59 |
60 | @Override
61 | protected void reconnectWithNetworkRequests() {
62 |
63 | userDataSubscription = homeViewModel.createUserDataSubject()
64 | .observeOn(AndroidSchedulers.mainThread())
65 | .subscribe(new UserDataSubscriber());
66 | }
67 |
68 | @Override
69 | protected void unsubscribeFromNetworkRequests() {
70 |
71 | if (userDataSubscription != null) {
72 | userDataSubscription.unsubscribe();
73 | }
74 | }
75 |
76 | private void setupRefreshLayout() {
77 |
78 | swipeRefreshLayout.setOnRefreshListener(() -> homeViewModel.getUserData());
79 | }
80 |
81 | private void showRandomSuccessfulMessage() {
82 |
83 | showMessage(homeViewModel.generateRandomMessage());
84 | }
85 |
86 | private void showMessage(String message) {
87 |
88 | // Because we subscribe on the Main Thread, we can do this
89 | userDataText.setText(message);
90 | }
91 |
92 | private void hideRefreshLayout() {
93 |
94 | swipeRefreshLayout.setRefreshing(false);
95 | }
96 |
97 | private class UserDataSubscriber extends Subscriber