| @@ -0,0 +1,102 @@ | ||
| package space.dotcat.assistant.repository.authRepository; | ||
|
|
||
| import android.content.SharedPreferences; | ||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.BuildConfig; | ||
| import space.dotcat.assistant.repository.authRepository.localAuthDataSource.LocalAuthSource; | ||
|
|
||
| import static junit.framework.Assert.assertEquals; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class LocalAuthSourceTest { | ||
|
|
||
| private static final String URL = "https://10.10.10.10"; | ||
|
|
||
| private static final String TOKEN = "TOKEN"; | ||
|
|
||
| private static final String KEY = "KEY"; | ||
|
|
||
| private static final String VALUE = "VALUE"; | ||
|
|
||
| private static final String DEF_VALUE = "DEF_VALUE"; | ||
|
|
||
| private SharedPreferences mSharedPreferences; | ||
|
|
||
| private LocalAuthSource mLocalAuthSource; | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mSharedPreferences = AppDelegate.getInstance().getAppComponent().getFakeSharedPref(); | ||
|
|
||
| mLocalAuthSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalAuthSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| SharedPreferences.Editor editor = mSharedPreferences.edit(); | ||
|
|
||
| editor.clear(); | ||
|
|
||
| editor.commit(); | ||
|
|
||
| mSharedPreferences = null; | ||
| mLocalAuthSource = null; | ||
| } | ||
|
|
||
| @Test | ||
| public void testSaveUrl() { | ||
| mLocalAuthSource.saveUrl(URL); | ||
|
|
||
| String url = mLocalAuthSource.getUrl(); | ||
|
|
||
| assertEquals(URL, url); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetUrlWhenUrlDidNotSavedBefore() { | ||
| String url = mLocalAuthSource.getUrl(); | ||
|
|
||
| assertEquals(BuildConfig.URL_DEFAULT_VALUE, url); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSaveToken() { | ||
| mLocalAuthSource.saveToken(TOKEN); | ||
|
|
||
| assertEquals(mLocalAuthSource.getToken(), TOKEN); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetTokenWhenTokenDidNotSavedBefore() { | ||
| String token = mLocalAuthSource.getToken(); | ||
|
|
||
| assertEquals(BuildConfig.TOKEN_DEFAULT_VALUE, token); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetSummaryByKeyWhenExist() { | ||
| SharedPreferences.Editor editor = mSharedPreferences.edit(); | ||
|
|
||
| editor.putString(KEY, VALUE); | ||
|
|
||
| editor.commit(); | ||
|
|
||
| String summary = mLocalAuthSource.getSummaryByKey(KEY, VALUE); | ||
|
|
||
| assertEquals(VALUE, summary); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetSummaryByKeyWithDefault() { | ||
| String summary = mLocalAuthSource.getSummaryByKey(KEY, DEF_VALUE); | ||
|
|
||
| assertEquals(DEF_VALUE, summary); | ||
| } | ||
| } |
| @@ -0,0 +1,74 @@ | ||
| package space.dotcat.assistant.repository.authRepository; | ||
|
|
||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.api.RequestMatcher; | ||
| import space.dotcat.assistant.content.ApiError; | ||
| import space.dotcat.assistant.content.Authorization; | ||
| import space.dotcat.assistant.content.AuthorizationAnswer; | ||
| import space.dotcat.assistant.repository.authRepository.localAuthDataSource.LocalAuthSource; | ||
| import space.dotcat.assistant.repository.authRepository.remoteDataSource.RemoteAuthSource; | ||
|
|
||
|
|
||
| import static junit.framework.Assert.assertEquals; | ||
| import static junit.framework.Assert.assertNotNull; | ||
| import static junit.framework.Assert.assertTrue; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class RemoteAuthSourceTest { | ||
|
|
||
| private RemoteAuthSource mRemoteAuthSource; | ||
|
|
||
| private LocalAuthSource mLocalAuthSource; | ||
|
|
||
| private final static String TOKEN = "90ff4ba085545c1735ab6c29a916f9cb8c0b7222"; | ||
|
|
||
| private final static Authorization AUTHORIZATION_INFO = new Authorization("login", "pass"); | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mRemoteAuthSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeRemoteAuthSource(); | ||
|
|
||
| mLocalAuthSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalAuthSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| mLocalAuthSource.deleteToken(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testRemoteAuthSourceCreated() { | ||
| assertNotNull(mRemoteAuthSource); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSuccessAuth() { | ||
| AuthorizationAnswer authorizationAnswer = mRemoteAuthSource.authUser(AUTHORIZATION_INFO) | ||
| .blockingGet(); | ||
|
|
||
| assertEquals(TOKEN, authorizationAnswer.getToken()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testErrorAuth() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR); | ||
|
|
||
| mRemoteAuthSource.authUser(AUTHORIZATION_INFO) | ||
| .test() | ||
| .assertError(ApiError.class); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDestroyService() { | ||
| mRemoteAuthSource.destroyApiService(); | ||
|
|
||
| assertTrue(mRemoteAuthSource.isApiDestroyed()); | ||
| } | ||
| } |
| @@ -0,0 +1,104 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository; | ||
|
|
||
| import android.arch.core.executor.testing.InstantTaskExecutorRule; | ||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource.LocalRoomsSource; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class LocalRoomsSourceTest { | ||
|
|
||
| @Rule | ||
| public InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule(); | ||
|
|
||
| private LocalRoomsSource mLocalRoomsSource; | ||
|
|
||
| private final List<space.dotcat.assistant.content.Room> ROOMS = createRoomsList(); | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mLocalRoomsSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalRoomsSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| mLocalRoomsSource.deleteRoomsSync(); | ||
|
|
||
| mLocalRoomsSource = null; | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetRoomsWhenThereIsNoRoomsInDb() { | ||
| mLocalRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertNoValues(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSaveAndGetRooms() { | ||
| mLocalRoomsSource.addRoomsSync(ROOMS); | ||
|
|
||
| mLocalRoomsSource.getRooms() | ||
| .test() | ||
| .assertNoErrors() | ||
| .assertValue(list-> list.get(0).getId().equals("id1")); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReplaceRoomsDuringInsert() { | ||
| mLocalRoomsSource.addRoomsSync(ROOMS); | ||
|
|
||
| space.dotcat.assistant.content.Room room = new space.dotcat.assistant.content.Room("corridor", | ||
| "id1", "path"); | ||
|
|
||
| mLocalRoomsSource.addRoomsSync(Collections.singletonList(room)); | ||
|
|
||
| mLocalRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .filter(room1-> room1.getId().equals("id1")) | ||
| .test() | ||
| .assertValue(roomInstance-> roomInstance.getFriendlyName().equals("corridor")); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDeleteAllRooms() { | ||
| mLocalRoomsSource.addRoomsSync(ROOMS); | ||
|
|
||
| mLocalRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(2); | ||
|
|
||
| mLocalRoomsSource.deleteRoomsSync(); | ||
|
|
||
| mLocalRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(0); | ||
| } | ||
|
|
||
| private List<space.dotcat.assistant.content.Room> createRoomsList() { | ||
| space.dotcat.assistant.content.Room room1 = new space.dotcat.assistant.content.Room(); | ||
|
|
||
| room1.setId("id1"); | ||
|
|
||
| space.dotcat.assistant.content.Room room2 = new space.dotcat.assistant.content.Room(); | ||
|
|
||
| room2.setId("id2"); | ||
|
|
||
| return Arrays.asList(room1, room2); | ||
| } | ||
| } |
| @@ -0,0 +1,80 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository; | ||
|
|
||
| import android.support.test.InstrumentationRegistry; | ||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.api.RequestMatcher; | ||
| import space.dotcat.assistant.content.ApiError; | ||
| import space.dotcat.assistant.di.appComponent.DaggerAppComponent; | ||
| import space.dotcat.assistant.di.appComponent.SharedPreferencesModule; | ||
| import space.dotcat.assistant.repository.authRepository.localAuthDataSource.LocalAuthSource; | ||
| import space.dotcat.assistant.repository.roomsRepository.remoteRoomsDataSource.RemoteRoomsSource; | ||
| import space.dotcat.assistant.utils.RxJavaTestRule; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class RemoteRoomsSourceTest { | ||
|
|
||
| @Rule | ||
| public RxJavaTestRule mRxJavaTestRule = new RxJavaTestRule(); | ||
|
|
||
| private LocalAuthSource mLocalAuthSource; | ||
|
|
||
| private RemoteRoomsSource mRemoteRoomsSource; | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mLocalAuthSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalAuthSource(); | ||
|
|
||
| mRemoteRoomsSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeRemoteRoomsSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| mLocalAuthSource.deleteToken(); | ||
|
|
||
| mLocalAuthSource = null; | ||
|
|
||
| mRemoteRoomsSource = null; | ||
| } | ||
|
|
||
| @Test | ||
| public void getRoomsSuccessfully() { | ||
| mRemoteRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(6) | ||
| .assertNoErrors() | ||
| .assertValueAt(0, room -> room.getFriendlyName().equals("Corridor")) | ||
| .assertValueAt(1, room -> room.getFriendlyName().equals("Kitchen")) | ||
| .assertValueAt(2, room -> room.getFriendlyName().equals("Bathroom")); | ||
| } | ||
|
|
||
| @Test | ||
| public void getEmptyRooms() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR_EMPTY_ROOMS); | ||
|
|
||
| mRemoteRoomsSource.getRooms() | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertNoErrors() | ||
| .assertValueCount(0); | ||
| } | ||
|
|
||
| @Test | ||
| public void getRoomsWithError() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR); | ||
|
|
||
| mRemoteRoomsSource.getRooms() | ||
| .test() | ||
| .assertError(ApiError.class); | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,142 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository; | ||
|
|
||
| import android.arch.core.executor.testing.InstantTaskExecutorRule; | ||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.content.Thing; | ||
| import space.dotcat.assistant.repository.thingsRepository.localThingsDataSource.LocalThingsSource; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class LocalThingsSourceTest { | ||
|
|
||
| @Rule | ||
| public InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule(); | ||
|
|
||
| private LocalThingsSource mLocalThingsSource; | ||
|
|
||
| public final String ROOM_ID = "R1"; | ||
|
|
||
| private final List<Thing> THINGS = createThings(); | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mLocalThingsSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalThingsSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| mLocalThingsSource.deleteAllThings(); | ||
|
|
||
| mLocalThingsSource = null; | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetThingsWhenThereIsNoThingsLocaly() { | ||
| mLocalThingsSource.getThingsById(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(0); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetThings() { | ||
| mLocalThingsSource.addThingsSync(THINGS); | ||
|
|
||
| mLocalThingsSource.getThingsById(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(3) | ||
| .assertValueAt(0, thing -> !thing.getIsActive()) | ||
| .assertValueAt(1, thing -> !thing.getIsActive()) | ||
| .assertValueAt(2, Thing::getIsActive) ; | ||
| } | ||
|
|
||
| @Test | ||
| public void testDeleteThings() { | ||
| mLocalThingsSource.addThingsSync(THINGS); | ||
|
|
||
| mLocalThingsSource.getThingsById(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueAt(0, thing -> !thing.getIsActive()); | ||
|
|
||
| mLocalThingsSource.deleteAllThings(); | ||
|
|
||
| mLocalThingsSource.getThingsById(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(0); | ||
| } | ||
|
|
||
| @Test | ||
| public void testUpdateThings() { | ||
| mLocalThingsSource.addThingsSync(THINGS); | ||
|
|
||
| Thing updatedThing = THINGS.get(0); | ||
| updatedThing.setActive(true); | ||
|
|
||
| mLocalThingsSource.updateThing(updatedThing); | ||
| } | ||
|
|
||
| @Test | ||
| public void testInsertThingsWithReplacing() { | ||
| mLocalThingsSource.addThingsSync(THINGS); | ||
|
|
||
| Thing thing = new Thing(); | ||
| thing.setId("id1"); | ||
| thing.setPlacement(ROOM_ID); | ||
| thing.setActive(true); | ||
|
|
||
| Thing thing1 = new Thing(); | ||
| thing1.setId("id2"); | ||
| thing1.setPlacement(ROOM_ID); | ||
| thing1.setActive(false); | ||
|
|
||
| mLocalThingsSource.addThingsSync(Arrays.asList(thing, thing1)); | ||
|
|
||
| mLocalThingsSource.getThingsById(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueAt(1, Thing::getIsActive) | ||
| .assertValueAt(2, t-> !t.getIsActive()); | ||
| } | ||
|
|
||
|
|
||
| private List<Thing> createThings() { | ||
| Thing door = new Thing(); | ||
|
|
||
| door.setPlacement(ROOM_ID); | ||
| door.setId("id"); | ||
| door.setActive(false); | ||
|
|
||
| Thing light = new Thing(); | ||
|
|
||
| light.setPlacement(ROOM_ID); | ||
| light.setId("id1"); | ||
| light.setActive(false); | ||
|
|
||
| Thing player = new Thing(); | ||
|
|
||
| player.setPlacement(ROOM_ID); | ||
| player.setId("id2"); | ||
| player.setActive(true); | ||
|
|
||
| Thing anotherThing = new Thing(); | ||
|
|
||
| anotherThing.setPlacement("PLACEMENT"); | ||
| anotherThing.setId("id4"); | ||
|
|
||
| return Arrays.asList(door, light, player, anotherThing); | ||
| } | ||
| } |
| @@ -0,0 +1,96 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository; | ||
|
|
||
| import android.support.test.runner.AndroidJUnit4; | ||
|
|
||
| import org.junit.After; | ||
| import org.junit.Before; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.AppDelegate; | ||
| import space.dotcat.assistant.api.RequestMatcher; | ||
| import space.dotcat.assistant.content.ApiError; | ||
| import space.dotcat.assistant.content.CommandArgs; | ||
| import space.dotcat.assistant.content.Message; | ||
| import space.dotcat.assistant.repository.authRepository.localAuthDataSource.LocalAuthSource; | ||
| import space.dotcat.assistant.repository.thingsRepository.remoteThingsDataSource.RemoteThingsSource; | ||
| import space.dotcat.assistant.utils.RxJavaTestRule; | ||
|
|
||
| @RunWith(AndroidJUnit4.class) | ||
| public class RemoteThingsSourceTest { | ||
|
|
||
| @Rule | ||
| public RxJavaTestRule mRxJavaTestRule = new RxJavaTestRule(); | ||
|
|
||
| private LocalAuthSource mLocalAuthSource; | ||
|
|
||
| private RemoteThingsSource mRemoteThingsSource; | ||
|
|
||
| private final String ROOM_ID = "R1"; | ||
|
|
||
| private final String THING_ID = "D1"; | ||
|
|
||
| private final Message MESSAGE = new Message("toggle", new CommandArgs()); | ||
|
|
||
| @Before | ||
| public void init() { | ||
| mLocalAuthSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeLocalAuthSource(); | ||
|
|
||
| mRemoteThingsSource = AppDelegate.getInstance().plusDataLayerComponent().getFakeRemoteThingsSource(); | ||
| } | ||
|
|
||
| @After | ||
| public void clear() { | ||
| mLocalAuthSource.deleteToken(); | ||
|
|
||
| mRemoteThingsSource = null; | ||
|
|
||
| mLocalAuthSource = null; | ||
| } | ||
|
|
||
| @Test | ||
| public void testLoadThingsByPlacementId() { | ||
| mRemoteThingsSource.loadThingsByPlacementId(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .filter(thing -> thing.getPlacement().equals(ROOM_ID)) | ||
| .test() | ||
| .assertValueCount(2); | ||
| } | ||
|
|
||
| @Test | ||
| public void testLoadEmptyThings() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR_EMPTY_THINGS); | ||
|
|
||
| mRemoteThingsSource.loadThingsByPlacementId(ROOM_ID) | ||
| .flatMap(Flowable::fromIterable) | ||
| .test() | ||
| .assertValueCount(0); | ||
| } | ||
|
|
||
| @Test | ||
| public void testLoadThingsWithError() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR); | ||
|
|
||
| mRemoteThingsSource.loadThingsByPlacementId(ROOM_ID) | ||
| .test() | ||
| .assertError(ApiError.class); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDoActionSuccessfully() { | ||
| mRemoteThingsSource.doActionOnThing(THING_ID, MESSAGE) | ||
| .test() | ||
| .assertValue(m-> m.getMessage().equals("accepted")); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDoActionWithError() { | ||
| mLocalAuthSource.saveToken(RequestMatcher.ERROR); | ||
|
|
||
| mRemoteThingsSource.doActionOnThing(THING_ID, MESSAGE) | ||
| .test() | ||
| .assertError(ApiError.class); | ||
| } | ||
| } |
| @@ -1,98 +1,78 @@ | ||
| package space.dotcat.assistant.api; | ||
|
|
||
| import android.content.SharedPreferences; | ||
| import android.support.annotation.NonNull; | ||
|
|
||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import okhttp3.OkHttpClient; | ||
| import retrofit2.Retrofit; | ||
| import retrofit2.converter.gson.GsonConverterFactory; | ||
| import space.dotcat.assistant.BuildConfig; | ||
|
|
||
| public final class ApiFactory { | ||
|
|
||
| private volatile ApiService mService; | ||
|
|
||
| private SharedPreferences mSharedPreferences; | ||
|
|
||
| private OkHttpFactory mOkHttpFactory; | ||
|
|
||
| private ErrorParser mErrorParser; | ||
|
|
||
| public ApiFactory(SharedPreferences sharedPreferences, OkHttpFactory okHttpFactory, | ||
| ErrorParser errorParser) { | ||
| mSharedPreferences = sharedPreferences; | ||
|
|
||
| mOkHttpFactory = okHttpFactory; | ||
|
|
||
| mErrorParser = errorParser; | ||
| } | ||
|
|
||
| @NonNull | ||
| public ApiService getApiService() { | ||
| ApiService service = mService; | ||
|
|
||
| if(service == null) { | ||
| synchronized (ApiFactory.class) { | ||
| service = mService; | ||
|
|
||
| if(service == null) { | ||
| service = mService = buildRetrofit().create(ApiService.class); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return service; | ||
| } | ||
|
|
||
| @NonNull | ||
| private Retrofit buildRetrofit() { | ||
| String base_url = mSharedPreferences.getString(BuildConfig.URL_KEY, | ||
| BuildConfig.URL_DEFAULT_VALUE); | ||
|
|
||
| OkHttpClient okHttpClient = mOkHttpFactory.provideClient(); | ||
|
|
||
| return new Retrofit.Builder() | ||
| .baseUrl(base_url) | ||
| .client(okHttpClient) | ||
| .addConverterFactory(GsonConverterFactory.create()) | ||
| .addCallAdapterFactory(new RxJavaAdapterWithErrorHandling(mErrorParser)) | ||
| .build(); | ||
| } | ||
|
|
||
| public void recreate() { | ||
| mOkHttpFactory.recreate(); | ||
| mService = buildRetrofit().create(ApiService.class); | ||
| } | ||
|
|
||
| public boolean isServiceDeleted() { | ||
| return mService == null; | ||
| } | ||
|
|
||
| public void deleteInstance() { | ||
| mOkHttpFactory.deleteClient(); | ||
| mService = null; | ||
| } | ||
| } |
| @@ -0,0 +1,30 @@ | ||
| package space.dotcat.assistant.api; | ||
|
|
||
|
|
||
| import java.io.IOException; | ||
| import java.lang.annotation.Annotation; | ||
|
|
||
|
|
||
| import okhttp3.ResponseBody; | ||
| import retrofit2.Converter; | ||
| import retrofit2.Response; | ||
| import retrofit2.Retrofit; | ||
| import space.dotcat.assistant.content.ApiError; | ||
|
|
||
| public class BasicErrorParser extends ErrorParser { | ||
|
|
||
| public ApiError parseError(Retrofit retrofit, Response response) { | ||
| Converter<ResponseBody, ApiError> converter = retrofit | ||
| .responseBodyConverter(ApiError.class, new Annotation[0]); | ||
|
|
||
| ApiError error; | ||
|
|
||
| try { | ||
| error = converter.convert(response.errorBody()); | ||
| } catch (IOException e) { | ||
| return null; | ||
| } | ||
|
|
||
| return error; | ||
| } | ||
| } |
| @@ -1,32 +1,25 @@ | ||
| package space.dotcat.assistant.api; | ||
|
|
||
|
|
||
|
|
||
| import retrofit2.Response; | ||
| import retrofit2.Retrofit; | ||
| import space.dotcat.assistant.content.ApiError; | ||
|
|
||
| /** | ||
| * Base class for error parser which will be provided to Handling Error RxJava2 Factory | ||
| */ | ||
|
|
||
| public abstract class ErrorParser { | ||
|
|
||
| /** | ||
| * Try to parse error from Retrofit response into ApiError. If response can not be parsed, it will | ||
| * return null | ||
| * | ||
| * @param retrofit retrofit instance that you used for request | ||
| * @param response response that contains possible error body | ||
| * @return parsed error from response error body | ||
| */ | ||
|
|
||
| public abstract ApiError parseError(Retrofit retrofit, Response response); | ||
| } |
| @@ -3,9 +3,8 @@ | ||
|
|
||
| import com.google.gson.annotations.SerializedName; | ||
|
|
||
|
|
||
| public class AuthorizationAnswer { | ||
|
|
||
| @SerializedName("message") | ||
| private String mMessage; | ||
| @@ -0,0 +1,34 @@ | ||
| package space.dotcat.assistant.content; | ||
|
|
||
| import android.arch.persistence.room.TypeConverter; | ||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collector; | ||
|
|
||
| public class StringsConverter { | ||
|
|
||
| @TypeConverter | ||
| public String fromListToString(List<String> stringList) { | ||
| StringBuilder stringBuilder = new StringBuilder(); | ||
|
|
||
| if(stringList == null) | ||
| return ""; | ||
|
|
||
| for(String s : stringList) { | ||
| stringBuilder.append(s); | ||
| stringBuilder.append(" "); | ||
| } | ||
|
|
||
| return stringBuilder.toString(); | ||
| } | ||
|
|
||
| @TypeConverter | ||
| public List<String> fromStringToList(@NonNull String string) { | ||
| String [] array = string.split(" "); | ||
|
|
||
| return Arrays.asList(array); | ||
| } | ||
|
|
||
| } |
| @@ -10,7 +10,7 @@ | ||
| public class ThingResponse { | ||
|
|
||
| @SerializedName("things") | ||
| private List<Thing> mThings; | ||
|
|
||
| private ThingResponse(@NonNull List<Thing> things) { | ||
| mThings = things; | ||
| @@ -3,9 +3,7 @@ | ||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| public class Url { | ||
|
|
||
| private String mUrl; | ||
|
|
||
| @@ -0,0 +1,18 @@ | ||
| package space.dotcat.assistant.repository; | ||
|
|
||
|
|
||
| import android.arch.persistence.room.Database; | ||
| import android.arch.persistence.room.RoomDatabase; | ||
|
|
||
| import space.dotcat.assistant.content.Room; | ||
| import space.dotcat.assistant.content.Thing; | ||
| import space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource.RoomsDao; | ||
| import space.dotcat.assistant.repository.thingsRepository.localThingsDataSource.ThingsDao; | ||
|
|
||
| @Database(entities = {Room.class, Thing.class}, version = 2, exportSchema = false) | ||
| public abstract class AppDatabase extends RoomDatabase { | ||
|
|
||
| public abstract RoomsDao roomsDao(); | ||
|
|
||
| public abstract ThingsDao thingsDao(); | ||
| } |
| @@ -0,0 +1,77 @@ | ||
| package space.dotcat.assistant.repository.authRepository; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import io.reactivex.Completable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Authorization; | ||
| import space.dotcat.assistant.content.AuthorizationAnswer; | ||
|
|
||
| public interface AuthRepository { | ||
|
|
||
| /** | ||
| * Save particular ulr into a phone's disk memory | ||
| * | ||
| * @param url - address of server which you want to connect with | ||
| */ | ||
|
|
||
| void saveUrl(@NonNull String url); | ||
|
|
||
| /** | ||
| * Get url value from the disk. Returns default value if url was not been saved | ||
| * | ||
| * @return address of server which you are now connected with | ||
| */ | ||
|
|
||
| String getUrl(); | ||
|
|
||
| /** | ||
| * Save particular token into a phone's disk memory | ||
| * | ||
| * @param token - user token | ||
| */ | ||
|
|
||
| void saveToken(@NonNull String token); | ||
|
|
||
| /** | ||
| * Get token from the disk. Returns default value if token was not saved | ||
| * | ||
| * @return user token | ||
| */ | ||
|
|
||
| String getToken(); | ||
|
|
||
| /** | ||
| * Delete user token from a phone's memory disk | ||
| * | ||
| */ | ||
|
|
||
| void deleteToken(); | ||
|
|
||
| /** | ||
| * Get preference summary by given key, return default value as well if summary was not saved | ||
| * | ||
| * @param key - preference key | ||
| * @param defaultValue - default value for summary | ||
| * @return - summary for preference | ||
| */ | ||
|
|
||
| String getSummaryByKey(@NonNull String key, @NonNull String defaultValue); | ||
|
|
||
| /** | ||
| * Try to authorize as a user | ||
| * | ||
| * @param authorizationInfo - object that contains login and password | ||
| * @return - single with auth answer from the server | ||
| */ | ||
|
|
||
| Single<AuthorizationAnswer> authUser(@NonNull Authorization authorizationInfo); | ||
|
|
||
| /** | ||
| * Destroy api service instance in order to create it again when it be needed | ||
| * | ||
| */ | ||
|
|
||
| void destroyApiService(); | ||
| } |
| @@ -0,0 +1,72 @@ | ||
| package space.dotcat.assistant.repository.authRepository; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Authorization; | ||
| import space.dotcat.assistant.content.AuthorizationAnswer; | ||
| import space.dotcat.assistant.repository.authRepository.localAuthDataSource.LocalAuthSource; | ||
| import space.dotcat.assistant.repository.authRepository.remoteDataSource.RemoteAuthSource; | ||
|
|
||
| public class AuthRepositoryImpl implements AuthRepository { | ||
|
|
||
| private LocalAuthSource mLocalAuthSource; | ||
|
|
||
| private RemoteAuthSource mRemoteAuthSource; | ||
|
|
||
| public AuthRepositoryImpl(LocalAuthSource authRepository, RemoteAuthSource remoteAuthSource) { | ||
| mLocalAuthSource = authRepository; | ||
|
|
||
| mRemoteAuthSource = remoteAuthSource; | ||
| } | ||
|
|
||
| @Override | ||
| public void saveUrl(@NonNull String url) { | ||
| mLocalAuthSource.saveUrl(url); | ||
| } | ||
|
|
||
| @Override | ||
| public String getUrl() { | ||
| return mLocalAuthSource.getUrl(); | ||
| } | ||
|
|
||
| @Override | ||
| public void saveToken(@NonNull String token) { | ||
| mLocalAuthSource.saveToken(token); | ||
| } | ||
|
|
||
| @Override | ||
| public String getToken() { | ||
| return mLocalAuthSource.getToken(); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteToken() { | ||
| mLocalAuthSource.deleteToken(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getSummaryByKey(@NonNull String key, @NonNull String defaultValue) { | ||
| return mLocalAuthSource.getSummaryByKey(key, defaultValue); | ||
| } | ||
|
|
||
| @Override | ||
| public Single<AuthorizationAnswer> authUser(@NonNull Authorization authorizationInfo) { | ||
| return mRemoteAuthSource.authUser(authorizationInfo) | ||
| .flatMap(answer -> { | ||
| String token = answer.getToken(); | ||
|
|
||
| mLocalAuthSource.saveToken(token); | ||
|
|
||
| mRemoteAuthSource.destroyApiService(); | ||
|
|
||
| return Single.just(answer); | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public void destroyApiService() { | ||
| mRemoteAuthSource.destroyApiService(); | ||
| } | ||
| } |
| @@ -0,0 +1,19 @@ | ||
| package space.dotcat.assistant.repository.authRepository.localAuthDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| public interface LocalAuthSource { | ||
|
|
||
| void saveUrl(@NonNull String url); | ||
|
|
||
| String getUrl(); | ||
|
|
||
| void saveToken(@NonNull String token); | ||
|
|
||
| String getToken(); | ||
|
|
||
| void deleteToken(); | ||
|
|
||
| String getSummaryByKey(@NonNull String key, @NonNull String defaultValue); | ||
| } |
| @@ -0,0 +1,62 @@ | ||
| package space.dotcat.assistant.repository.authRepository.localAuthDataSource; | ||
|
|
||
|
|
||
| import android.content.SharedPreferences; | ||
| import android.support.annotation.NonNull; | ||
|
|
||
| import space.dotcat.assistant.BuildConfig; | ||
|
|
||
| public class LocalAuthSourceImpl implements LocalAuthSource { | ||
|
|
||
| private SharedPreferences mSharedPreferences; | ||
|
|
||
| private static final String URL_DEFAULT_VALUE = "https://api.ks-cube.tk/"; | ||
|
|
||
| private static final String TOKEN_DEFAULT_VALUE = ""; | ||
|
|
||
| public LocalAuthSourceImpl(SharedPreferences sharedPreferences) { | ||
| mSharedPreferences = sharedPreferences; | ||
| } | ||
|
|
||
| @Override | ||
| public void saveUrl(@NonNull String url) { | ||
| SharedPreferences.Editor editor = mSharedPreferences.edit(); | ||
|
|
||
| editor.putString(BuildConfig.URL_KEY, url); | ||
|
|
||
| editor.apply(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getUrl() { | ||
| return mSharedPreferences.getString(BuildConfig.URL_KEY, URL_DEFAULT_VALUE); | ||
| } | ||
|
|
||
| @Override | ||
| public void saveToken(@NonNull String token) { | ||
| SharedPreferences.Editor editor = mSharedPreferences.edit(); | ||
|
|
||
| editor.putString(BuildConfig.TOKEN_KEY, token); | ||
|
|
||
| editor.apply(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getToken() { | ||
| return mSharedPreferences.getString(BuildConfig.TOKEN_KEY, TOKEN_DEFAULT_VALUE); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteToken() { | ||
| SharedPreferences.Editor editor = mSharedPreferences.edit(); | ||
|
|
||
| editor.remove(BuildConfig.TOKEN_KEY); | ||
|
|
||
| editor.apply(); | ||
| } | ||
|
|
||
| @Override | ||
| public String getSummaryByKey(@NonNull String key, @NonNull String defaultValue) { | ||
| return mSharedPreferences.getString(key, defaultValue); | ||
| } | ||
| } |
| @@ -0,0 +1,30 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository; | ||
|
|
||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Completable; | ||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Room; | ||
|
|
||
| public interface RoomRepository { | ||
|
|
||
| /** | ||
| * | ||
| * Method is responsible for giving observable flowable of rooms. Firstly trying to load data | ||
| * from db, if there is no available rooms localy then fetching data via remote api. If some | ||
| * error occurs, then this method will return merged Flowable from local rooms and delayed error | ||
| * | ||
| * @return observable Flowable list of rooms | ||
| */ | ||
|
|
||
| Flowable<List<Room>> getRooms(); | ||
|
|
||
| /** | ||
| * Refresh local data source asynchronously via remote api | ||
| * | ||
| * @return flowable which contains list of rooms | ||
| */ | ||
|
|
||
| Flowable<List<Room>> refreshRooms(); | ||
| } |
| @@ -0,0 +1,71 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository; | ||
|
|
||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Completable; | ||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Room; | ||
| import space.dotcat.assistant.content.RoomResponse; | ||
| import space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource.LocalRoomsSource; | ||
| import space.dotcat.assistant.repository.roomsRepository.remoteRoomsDataSource.RemoteRoomsSource; | ||
| import space.dotcat.assistant.utils.RxUtils; | ||
|
|
||
| public class RoomRepositoryImpl implements RoomRepository { | ||
|
|
||
| private LocalRoomsSource mLocalRoomsSource; | ||
|
|
||
| private RemoteRoomsSource mRemoteRoomsSource; | ||
|
|
||
| private Flowable<List<Room>> mCacheRooms; | ||
|
|
||
| public RoomRepositoryImpl(LocalRoomsSource localRoomsSource, RemoteRoomsSource remoteRoomsSource) { | ||
| mLocalRoomsSource = localRoomsSource; | ||
| mRemoteRoomsSource = remoteRoomsSource; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Room>> getRooms() { | ||
| if(mCacheRooms != null) | ||
| return mCacheRooms; | ||
|
|
||
| return mCacheRooms = mLocalRoomsSource.getRooms() | ||
| .flatMap(rooms-> { | ||
| if(rooms.isEmpty()) { | ||
| return loadRoomsRemotelyAndSaveToDb().onErrorResumeNext(throwable -> { | ||
| return Flowable.mergeDelayError(mLocalRoomsSource.getRooms(), | ||
| Flowable.error(throwable)); | ||
| }); | ||
| } | ||
|
|
||
| return Flowable.just(rooms); | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Room>> refreshRooms() { | ||
| return loadRoomsRemotelyAndSaveToDb(); | ||
| } | ||
|
|
||
| /** | ||
| * Trying to load rooms from remote server. If successfully loaded then it add sync all rooms from response | ||
| * to database. | ||
| * | ||
| * @return list of room from remote data source | ||
| */ | ||
|
|
||
| private Flowable<List<Room>> loadRoomsRemotelyAndSaveToDb() { | ||
| return mRemoteRoomsSource.getRooms() | ||
| .doOnNext(rooms-> { | ||
| if(rooms.isEmpty()) { | ||
| return; | ||
| } | ||
|
|
||
| mLocalRoomsSource.deleteRoomsSync(); | ||
|
|
||
| mLocalRoomsSource.addRoomsSync(rooms); | ||
| }); | ||
| } | ||
| } |
| @@ -0,0 +1,33 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource; | ||
|
|
||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Room; | ||
| import space.dotcat.assistant.repository.roomsRepository.RoomRepository; | ||
|
|
||
| public interface LocalRoomsSource { | ||
|
|
||
| /** | ||
| * Return observable list of rooms from the db | ||
| * | ||
| * @return flowable with rooms | ||
| */ | ||
|
|
||
| Flowable<List<Room>> getRooms(); | ||
|
|
||
| /** | ||
| * Add rooms to your db | ||
| * | ||
| * @param rooms that you want to add into your database | ||
| */ | ||
|
|
||
| void addRoomsSync(List<Room> rooms); | ||
|
|
||
| /** | ||
| * Delete all rooms from db | ||
| */ | ||
|
|
||
| void deleteRoomsSync(); | ||
| } |
| @@ -0,0 +1,35 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Room; | ||
|
|
||
| public class LocalRoomsSourceImpl implements LocalRoomsSource { | ||
|
|
||
| private RoomsDao mRoomsDao; | ||
|
|
||
| public LocalRoomsSourceImpl(@NonNull RoomsDao roomsDao) { | ||
| mRoomsDao = roomsDao; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Room>> getRooms() { | ||
| return mRoomsDao.getRooms(); | ||
| } | ||
|
|
||
| @Override | ||
| public void addRoomsSync(List<Room> rooms) { | ||
| mRoomsDao.addRoomsSync(rooms); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteRoomsSync() { | ||
| mRoomsDao.deleteRoomsSync(); | ||
| } | ||
| } |
| @@ -0,0 +1,40 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository.localRoomsDataSource; | ||
|
|
||
| import android.arch.persistence.room.Dao; | ||
| import android.arch.persistence.room.Delete; | ||
| import android.arch.persistence.room.Insert; | ||
| import android.arch.persistence.room.OnConflictStrategy; | ||
| import android.arch.persistence.room.Query; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Room; | ||
|
|
||
| @Dao | ||
| public interface RoomsDao { | ||
|
|
||
| /** | ||
| * | ||
| * @return flowable which contains list of rooms from database | ||
| */ | ||
|
|
||
| @Query("Select * from Rooms") | ||
| Flowable<List<Room>> getRooms(); | ||
|
|
||
| /** | ||
| * Notice that this method inserts rooms in a sync way. Take care about it. | ||
| * | ||
| * @param rooms that you want to insert into your database. | ||
| */ | ||
|
|
||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | ||
| void addRoomsSync(List<Room> rooms); | ||
|
|
||
| /** | ||
| * Delete all rooms from database. Notice that this method deletes rooms in a sync way. | ||
| */ | ||
|
|
||
| @Query("Delete from Rooms") | ||
| void deleteRoomsSync(); | ||
| } |
| @@ -0,0 +1,18 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository.remoteRoomsDataSource; | ||
|
|
||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Room; | ||
| import space.dotcat.assistant.content.RoomResponse; | ||
|
|
||
| public interface RemoteRoomsSource { | ||
| /** | ||
| * Return list of rooms from remote api | ||
| * | ||
| * @return list of rooms | ||
| */ | ||
| Flowable<List<Room>> getRooms(); | ||
| } |
| @@ -0,0 +1,30 @@ | ||
| package space.dotcat.assistant.repository.roomsRepository.remoteRoomsDataSource; | ||
|
|
||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.api.ApiFactory; | ||
| import space.dotcat.assistant.api.ApiService; | ||
| import space.dotcat.assistant.content.Room; | ||
| import space.dotcat.assistant.content.RoomResponse; | ||
|
|
||
| public class RemoteRoomsSourceImpl implements RemoteRoomsSource { | ||
|
|
||
| private ApiFactory mApiFactory; | ||
|
|
||
| public RemoteRoomsSourceImpl(ApiFactory apiFactory) { | ||
| mApiFactory = apiFactory; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Room>> getRooms() { | ||
| return mApiFactory.getApiService() | ||
| .rooms() | ||
| .toFlowable() | ||
| .map(RoomResponse::getRooms); | ||
| } | ||
| } |
| @@ -0,0 +1,54 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Completable; | ||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Message; | ||
| import space.dotcat.assistant.content.ResponseActionMessage; | ||
| import space.dotcat.assistant.content.Thing; | ||
|
|
||
| public interface ThingRepository { | ||
|
|
||
| /** | ||
| * Get things by placement's id. It checks if there are things locally, if not it will load it from | ||
| * the server and save to db | ||
| * | ||
| * @param id - placement's id | ||
| * @return flowable which contains list of things | ||
| */ | ||
|
|
||
| Flowable<List<Thing>> getThingsById(@NonNull String id); | ||
|
|
||
| /** | ||
| * Reload things from the remote api and update local things | ||
| * | ||
| * @param id placement's id | ||
| * @return flowable which contains list of things from the remote api | ||
| */ | ||
|
|
||
| Flowable<List<Thing>> refreshThings(@NonNull String id); | ||
|
|
||
| /** | ||
| * Do action with particular thing | ||
| * | ||
| * @param id - thing id | ||
| * @param message - info what should you do with thing | ||
| * @return single with answer from the server | ||
| */ | ||
|
|
||
| Single<ResponseActionMessage> doAction(@NonNull String id, @NonNull Message message); | ||
|
|
||
| /** | ||
| * Update thing in database | ||
| * | ||
| * @param thing - new updated thing | ||
| * @return - completable with info was update successful or not | ||
| */ | ||
|
|
||
| Completable updateThing(@NonNull Thing thing); | ||
| } |
| @@ -0,0 +1,75 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Completable; | ||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Message; | ||
| import space.dotcat.assistant.content.ResponseActionMessage; | ||
| import space.dotcat.assistant.content.Thing; | ||
| import space.dotcat.assistant.repository.thingsRepository.localThingsDataSource.LocalThingsSource; | ||
| import space.dotcat.assistant.repository.thingsRepository.remoteThingsDataSource.RemoteThingsSource; | ||
| import space.dotcat.assistant.utils.RxUtils; | ||
|
|
||
| public class ThingRepositoryImpl implements ThingRepository { | ||
|
|
||
| private LocalThingsSource mLocalThingsSource; | ||
|
|
||
| private RemoteThingsSource mRemoteThingsSource; | ||
|
|
||
| @Inject | ||
| public ThingRepositoryImpl(LocalThingsSource localThingsSource, RemoteThingsSource remoteThingsSource) { | ||
| mLocalThingsSource = localThingsSource; | ||
| mRemoteThingsSource = remoteThingsSource; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Thing>> getThingsById(@NonNull String id) { | ||
| return mLocalThingsSource.getThingsById(id) | ||
| .flatMap(things -> { | ||
| if(things.isEmpty()) { | ||
| return loadThingsAndSaveToDb(id).onErrorResumeNext(throwable-> { | ||
| Flowable<List<Thing>> localThings = mLocalThingsSource.getThingsById(id); | ||
|
|
||
| return Flowable.mergeDelayError(localThings, Flowable.error(throwable)); | ||
| }); | ||
| } | ||
|
|
||
| return Flowable.just(things); | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Thing>> refreshThings(@NonNull String id) { | ||
| return loadThingsAndSaveToDb(id); | ||
| } | ||
|
|
||
| @Override | ||
| public Single<ResponseActionMessage> doAction(@NonNull String id, @NonNull Message message) { | ||
| return mRemoteThingsSource.doActionOnThing(id, message); | ||
| } | ||
|
|
||
| @Override | ||
| public Completable updateThing(@NonNull Thing thing) { | ||
| return Completable.fromAction(()-> mLocalThingsSource.updateThing(thing)); | ||
| } | ||
|
|
||
| private Flowable<List<Thing>> loadThingsAndSaveToDb(@NonNull String id) { | ||
| return mRemoteThingsSource.loadThingsByPlacementId(id) | ||
| .doOnNext(things -> { | ||
| if(things.isEmpty()) { | ||
| return; | ||
| } | ||
|
|
||
| mLocalThingsSource.deleteAllThings(); | ||
|
|
||
| mLocalThingsSource.addThingsSync(things); | ||
| }); | ||
| } | ||
| } |
| @@ -0,0 +1,24 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository.localThingsDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Thing; | ||
|
|
||
| public interface LocalThingsSource { | ||
|
|
||
| Flowable<List<Thing>> getThingsById(@NonNull String id); | ||
|
|
||
| void addThingsSync(@NonNull List<Thing> things); | ||
|
|
||
| void deleteAllThings(); | ||
|
|
||
| void updateThing(@NonNull Thing thing); | ||
|
|
||
| void deleteThingById(String id); | ||
|
|
||
| void insertThing(Thing thing); | ||
| } |
| @@ -0,0 +1,51 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository.localThingsDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Thing; | ||
|
|
||
| public class LocalThingsSourceImpl implements LocalThingsSource { | ||
|
|
||
| private ThingsDao mThingsDao; | ||
|
|
||
| @Inject | ||
| public LocalThingsSourceImpl(ThingsDao thingsDao) { | ||
| mThingsDao = thingsDao; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Thing>> getThingsById(@NonNull String id) { | ||
| return mThingsDao.getThingsById(id); | ||
| } | ||
|
|
||
| @Override | ||
| public void addThingsSync(@NonNull List<Thing> things) { | ||
| mThingsDao.insertThings(things); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteAllThings() { | ||
| mThingsDao.deleteAllThings(); | ||
| } | ||
|
|
||
| @Override | ||
| public void updateThing(@NonNull Thing thing) { | ||
| mThingsDao.updateThing(thing); | ||
| } | ||
|
|
||
| @Override | ||
| public void deleteThingById(String id) { | ||
| mThingsDao.deleteThingById(id); | ||
| } | ||
|
|
||
| @Override | ||
| public void insertThing(Thing thing) { | ||
| mThingsDao.insertThing(thing); | ||
| } | ||
| } |
| @@ -0,0 +1,69 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository.localThingsDataSource; | ||
|
|
||
| import android.arch.persistence.room.Dao; | ||
| import android.arch.persistence.room.Delete; | ||
| import android.arch.persistence.room.Insert; | ||
| import android.arch.persistence.room.OnConflictStrategy; | ||
| import android.arch.persistence.room.Query; | ||
| import android.arch.persistence.room.Update; | ||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import space.dotcat.assistant.content.Thing; | ||
|
|
||
| @Dao | ||
| public interface ThingsDao { | ||
|
|
||
| /** | ||
| * Get things by particular placement id from local database. | ||
| * This is observable query, so each time when database will be updated, | ||
| * flowable's onNext will be called | ||
| * | ||
| * @param id placement id | ||
| * @return flowable which contains list of things from database. | ||
| */ | ||
|
|
||
| @Query("Select * from Things where thing_placement = :id") | ||
| Flowable<List<Thing>> getThingsById(String id); | ||
|
|
||
|
|
||
| /** | ||
| * Insert things into database. If some conflict occurs, thing will be completely replaced | ||
| * | ||
| * @param things list of things that you want to insert into database. | ||
| */ | ||
|
|
||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | ||
| void insertThings(List<Thing> things); | ||
|
|
||
|
|
||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | ||
| void insertThing(Thing thing); | ||
|
|
||
| /** | ||
| * Delete all things from database | ||
| */ | ||
| @Query("Delete from Things") | ||
| void deleteAllThings(); | ||
|
|
||
| /** | ||
| * Delete particular thing by its id | ||
| * | ||
| * @param id thing id ,that you want to delete | ||
| */ | ||
|
|
||
| @Query("Delete from Things where thing_id = :id") | ||
| void deleteThingById(String id); | ||
|
|
||
|
|
||
| /** | ||
| * Update your thing | ||
| * | ||
| * @param thing thing that you want to update | ||
| */ | ||
|
|
||
| @Update | ||
| void updateThing(Thing thing); | ||
| } |
| @@ -0,0 +1,19 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository.remoteThingsDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.content.Message; | ||
| import space.dotcat.assistant.content.ResponseActionMessage; | ||
| import space.dotcat.assistant.content.Thing; | ||
|
|
||
| public interface RemoteThingsSource { | ||
|
|
||
| Flowable<List<Thing>> loadThingsByPlacementId(@NonNull String id); | ||
|
|
||
| Single<ResponseActionMessage> doActionOnThing(@NonNull String id, @NonNull Message message); | ||
| } |
| @@ -0,0 +1,40 @@ | ||
| package space.dotcat.assistant.repository.thingsRepository.remoteThingsDataSource; | ||
|
|
||
|
|
||
| import android.support.annotation.NonNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import io.reactivex.Flowable; | ||
| import io.reactivex.Single; | ||
| import space.dotcat.assistant.api.ApiFactory; | ||
| import space.dotcat.assistant.content.Message; | ||
| import space.dotcat.assistant.content.ResponseActionMessage; | ||
| import space.dotcat.assistant.content.Thing; | ||
| import space.dotcat.assistant.content.ThingResponse; | ||
|
|
||
| public class RemoteThingsSourceImpl implements RemoteThingsSource { | ||
|
|
||
| private ApiFactory mApiFactory; | ||
|
|
||
| @Inject | ||
| public RemoteThingsSourceImpl(ApiFactory apiFactory) { | ||
| mApiFactory = apiFactory; | ||
| } | ||
|
|
||
| @Override | ||
| public Flowable<List<Thing>> loadThingsByPlacementId(@NonNull String id) { | ||
| return mApiFactory.getApiService() | ||
| .things(id) | ||
| .toFlowable() | ||
| .map(ThingResponse::getThings); | ||
| } | ||
|
|
||
| @Override | ||
| public Single<ResponseActionMessage> doActionOnThing(@NonNull String id, @NonNull Message message) { | ||
| return mApiFactory.getApiService() | ||
| .action(id, message); | ||
| } | ||
| } |
| @@ -5,7 +5,7 @@ | ||
|
|
||
| import space.dotcat.assistant.screen.general.LoadingView; | ||
|
|
||
| public interface AuthViewContract extends LoadingView { | ||
|
|
||
| void showRoomList(); | ||
|
|
||
| @@ -18,6 +18,7 @@ public class RoomDetailsHolder extends RecyclerView.ViewHolder { | ||
|
|
||
| public RoomDetailsHolder(View itemView) { | ||
| super(itemView); | ||
|
|
||
| ButterKnife.bind(this, itemView); | ||
| } | ||
|
|
||