From a9d1051aa8bc40929a1a885e9c8444a955a087ce Mon Sep 17 00:00:00 2001 From: esensar Date: Wed, 7 Dec 2016 02:14:08 +0100 Subject: [PATCH] Initial commit --- .gitignore | 9 + .idea/compiler.xml | 22 ++ .idea/copyright/profiles_settings.xml | 3 + .idea/dictionaries/ensar.xml | 3 + .idea/encodings.xml | 6 + .idea/gradle.xml | 19 ++ .idea/misc.xml | 96 +++++++ .idea/modules.xml | 9 + .idea/runConfigurations.xml | 12 + app/.gitignore | 1 + app/build.gradle | 44 +++ app/proguard-rules.pro | 17 ++ .../realtimetalk/ExampleInstrumentedTest.java | 26 ++ app/src/main/AndroidManifest.xml | 38 +++ .../smarthomies/realtimetalk/RTTActivity.java | 98 +++++++ .../com/smarthomies/realtimetalk/RTTApp.java | 19 ++ .../smarthomies/realtimetalk/RTTFragment.java | 26 ++ .../managers/AuthenticationManager.java | 63 +++++ .../managers/ContactsManager.java | 24 ++ .../realtimetalk/models/db/User.java | 51 ++++ .../network/AuthenticationResponse.java | 18 ++ .../models/network/LoginRequest.java | 35 +++ .../models/network/RegistrationRequest.java | 54 ++++ .../models/network/SearchRequest.java | 18 ++ .../models/network/UsersResponse.java | 22 ++ .../realtimetalk/network/APIErrorHandler.java | 55 ++++ .../network/AuthenticationAPI.java | 24 ++ .../realtimetalk/network/ContactsAPI.java | 20 ++ .../network/NetworkingConstants.java | 14 + .../realtimetalk/network/RestClient.java | 60 ++++ .../network/exceptions/APIException.java | 17 ++ .../exceptions/BadAPIRequestException.java | 15 + .../network/exceptions/NetworkException.java | 15 + .../RemoteResourceNotFoundException.java | 15 + .../ResourceForbiddenException.java | 15 + .../exceptions/UnauthorizedException.java | 15 + .../services/AuthenticationAPIService.java | 62 ++++ .../services/ContactsAPIService.java | 45 +++ .../realtimetalk/utils/NavigationSubject.java | 28 ++ .../realtimetalk/utils/RTTAppHelper.java | 55 ++++ .../realtimetalk/utils/RTTErrorUtil.java | 33 +++ .../realtimetalk/utils/RTTUtil.java | 92 ++++++ .../viewmodels/LoginViewModel.java | 138 +++++++++ .../viewmodels/MainViewModel.java | 44 +++ .../viewmodels/RegistrationViewModel.java | 265 ++++++++++++++++++ .../viewmodels/SearchViewModel.java | 87 ++++++ .../viewmodels/UserViewModel.java | 57 ++++ .../views/activities/LoginActivity.java | 92 ++++++ .../views/activities/MainActivity.java | 143 ++++++++++ .../activities/RegistrationActivity.java | 53 ++++ .../views/activities/SearchActivity.java | 117 ++++++++ .../bindingutils/BindingAdapters.java | 34 +++ .../bindingutils/OnErrorChangedCallback.java | 29 ++ .../views/adapters/UsersAdapter.java | 96 +++++++ .../views/fragments/MainViewModelHolder.java | 20 ++ .../fragments/SearchViewModelHolder.java | 26 ++ .../views/fragments/ViewModelHolder.java | 17 ++ .../PasswordRegistrationFragment.java | 94 +++++++ .../registration/RegistrationFragment.java | 16 ++ .../RegistrationViewModelHolder.java | 21 ++ .../UserInfoRegistrationFragment.java | 33 +++ .../UserNameRegistrationFragment.java | 32 +++ .../main/res/drawable-v21/ic_menu_camera.xml | 12 + .../main/res/drawable-v21/ic_menu_gallery.xml | 9 + .../main/res/drawable-v21/ic_menu_manage.xml | 9 + .../main/res/drawable-v21/ic_menu_send.xml | 9 + .../main/res/drawable-v21/ic_menu_share.xml | 9 + .../res/drawable-v21/ic_menu_slideshow.xml | 9 + app/src/main/res/drawable/button_normal.xml | 6 + app/src/main/res/drawable/side_nav_bar.xml | 9 + app/src/main/res/layout/activity_login.xml | 144 ++++++++++ app/src/main/res/layout/activity_main.xml | 35 +++ .../main/res/layout/activity_registration.xml | 36 +++ app/src/main/res/layout/activity_search.xml | 44 +++ app/src/main/res/layout/app_bar_main.xml | 45 +++ app/src/main/res/layout/content_main.xml | 29 ++ app/src/main/res/layout/nav_header_main.xml | 47 ++++ .../main/res/layout/password_registration.xml | 124 ++++++++ .../main/res/layout/search_user_list_item.xml | 48 ++++ .../res/layout/user_info_registration.xml | 120 ++++++++ app/src/main/res/layout/user_list_item.xml | 34 +++ .../res/layout/user_name_registration.xml | 122 ++++++++ .../main/res/menu/activity_main_drawer.xml | 9 + app/src/main/res/menu/main.xml | 9 + app/src/main/res/menu/search.xml | 11 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 8721 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 5390 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 16979 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 27220 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 43919 bytes app/src/main/res/values-v21/styles.xml | 8 + app/src/main/res/values-w820dp/dimens.xml | 6 + app/src/main/res/values/colors.xml | 16 ++ app/src/main/res/values/dimens.xml | 12 + app/src/main/res/values/drawables.xml | 8 + app/src/main/res/values/strings.xml | 48 ++++ app/src/main/res/values/styles.xml | 22 ++ .../realtimetalk/ExampleUnitTest.java | 17 ++ build.gradle | 23 ++ gradle.properties | 17 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 +++++++++++ gradlew.bat | 90 ++++++ readme.md | 5 + settings.gradle | 1 + 106 files changed, 4095 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/dictionaries/ensar.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/smarthomies/realtimetalk/ExampleInstrumentedTest.java create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/RTTActivity.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/RTTFragment.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/network/LoginRequest.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/network/SearchRequest.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/models/network/UsersResponse.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/BadAPIRequestException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/NetworkException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/RemoteResourceNotFoundException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ResourceForbiddenException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/UnauthorizedException.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/utils/NavigationSubject.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/utils/RTTUtil.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/viewmodels/RegistrationViewModel.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/LoginActivity.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/RegistrationActivity.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/SearchActivity.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/BindingAdapters.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/OnErrorChangedCallback.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/adapters/UsersAdapter.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/SearchViewModelHolder.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ViewModelHolder.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/PasswordRegistrationFragment.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationFragment.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationViewModelHolder.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserInfoRegistrationFragment.java create mode 100644 app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserNameRegistrationFragment.java create mode 100644 app/src/main/res/drawable-v21/ic_menu_camera.xml create mode 100644 app/src/main/res/drawable-v21/ic_menu_gallery.xml create mode 100644 app/src/main/res/drawable-v21/ic_menu_manage.xml create mode 100644 app/src/main/res/drawable-v21/ic_menu_send.xml create mode 100644 app/src/main/res/drawable-v21/ic_menu_share.xml create mode 100644 app/src/main/res/drawable-v21/ic_menu_slideshow.xml create mode 100644 app/src/main/res/drawable/button_normal.xml create mode 100644 app/src/main/res/drawable/side_nav_bar.xml create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_registration.xml create mode 100644 app/src/main/res/layout/activity_search.xml create mode 100644 app/src/main/res/layout/app_bar_main.xml create mode 100644 app/src/main/res/layout/content_main.xml create mode 100644 app/src/main/res/layout/nav_header_main.xml create mode 100644 app/src/main/res/layout/password_registration.xml create mode 100644 app/src/main/res/layout/search_user_list_item.xml create mode 100644 app/src/main/res/layout/user_info_registration.xml create mode 100644 app/src/main/res/layout/user_list_item.xml create mode 100644 app/src/main/res/layout/user_name_registration.xml create mode 100644 app/src/main/res/menu/activity_main_drawer.xml create mode 100644 app/src/main/res/menu/main.xml create mode 100644 app/src/main/res/menu/search.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/values-v21/styles.xml create mode 100644 app/src/main/res/values-w820dp/dimens.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/drawables.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/test/java/com/smarthomies/realtimetalk/ExampleUnitTest.java create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 readme.md create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/dictionaries/ensar.xml b/.idea/dictionaries/ensar.xml new file mode 100644 index 0000000..20d3bae --- /dev/null +++ b/.idea/dictionaries/ensar.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0e23f8e --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..aa61ddb --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + Android + + + + + + + + + + + + + + + + + + + + + + + + + 1.7 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d3f4144 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..c8749e3 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,44 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 24 + buildToolsVersion "24.0.3" + defaultConfig { + applicationId "com.smarthomies.realtimetalk" + minSdkVersion 16 + targetSdkVersion 24 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + dataBinding { + enabled = true + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:24.2.1' + compile 'com.android.support:design:24.2.1' + compile 'com.google.code.gson:gson:2.8.0' + compile 'com.squareup.retrofit2:retrofit:2.1.0' + compile 'com.squareup.okhttp:okhttp:2.6.0' + compile 'com.squareup.okhttp:okhttp-urlconnection:2.6.0' + compile 'com.squareup.retrofit2:converter-gson:2.1.0' + compile 'com.jakewharton.rxbinding:rxbinding:0.4.0' + compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' + compile 'io.reactivex:rxandroid:1.2.1' + compile 'io.reactivex:rxjava:1.2.0' + compile 'com.squareup.okhttp3:logging-interceptor:3.3.1' + compile 'com.squareup.picasso:picasso:2.5.2' + testCompile 'junit:junit:4.12' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..223e22c --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/ensar/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/smarthomies/realtimetalk/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/smarthomies/realtimetalk/ExampleInstrumentedTest.java new file mode 100644 index 0000000..5057c00 --- /dev/null +++ b/app/src/androidTest/java/com/smarthomies/realtimetalk/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.smarthomies.realtimetalk; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.smarthomies.realtimetalk", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..522444a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/smarthomies/realtimetalk/RTTActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/RTTActivity.java new file mode 100644 index 0000000..078ec8c --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/RTTActivity.java @@ -0,0 +1,98 @@ +package com.smarthomies.realtimetalk; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.util.Pair; + +import com.smarthomies.realtimetalk.utils.NavigationSubject; + +import rx.functions.Action1; +import rx.subscriptions.CompositeSubscription; + +/** + * Created by ensar on 23/10/16. + */ +public abstract class RTTActivity extends AppCompatActivity { + public static final String TAG = RTTActivity.class.getSimpleName(); + + public static final String EXTRA_BUNDLE = "EXTRA_BUNDLE"; + + private CompositeSubscription compositeSubscription; + + @Override + protected void onResume() { + super.onResume(); + compositeSubscription = new CompositeSubscription(); + compositeSubscription.add(NavigationSubject.getInstance().subscribe(new Action1, Bundle>>() { + @Override + public void call(Pair, Bundle> classBundlePair) { + if(classBundlePair == null) { + finish(); + return; + } + Class activity = classBundlePair.first; + Bundle bundle = classBundlePair.second; + Intent intent = new Intent(RTTActivity.this, activity); + intent.putExtra(EXTRA_BUNDLE, bundle); + startActivity(intent); + } + })); + compositeSubscription.add(NavigationSubject.getFragmentNavigationInstance().subscribe(new Action1, Bundle>>() { + @Override + public void call(Pair, Bundle> classBundlePair) { + RTTActivity.this.startFragment(classBundlePair.first, RTTActivity.this.getFragmentContainerRId(),classBundlePair.second); + } + })); + subscribeToSubjects(); + } + + @Override + protected void onPause() { + super.onPause(); + if (compositeSubscription != null && !compositeSubscription.isUnsubscribed()) { + compositeSubscription.unsubscribe(); + } + unsubscribeFromSubjects(); + } + + protected void subscribeToSubjects() {} + + protected void unsubscribeFromSubjects() {} + + protected int getFragmentContainerRId() { + return 0; + } + + public void startFragment(Class fragmentClass, int containerId, Bundle bundle) { + String fragmentName = fragmentClass.getCanonicalName(); + RTTFragment fragment; + + try { + FragmentManager fm = getSupportFragmentManager(); + + // Find fragment by name + fragment = (RTTFragment) fm.findFragmentByTag(fragmentName); + + if(fragment == null) { + // Create new fragment + fragment = fragmentClass.newInstance(); + + // Check for bundle + if (bundle != null) { + fragment.setArguments(bundle); + } + } + + fm.beginTransaction().replace(containerId, fragment, fragmentName).commit(); + fm.executePendingTransactions(); + + } catch (Exception e) { + Log.e(TAG, "Error starting fragment : " + fragmentName, e); + throw new RuntimeException(e); + } + } + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java b/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java new file mode 100644 index 0000000..5cebd71 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java @@ -0,0 +1,19 @@ +package com.smarthomies.realtimetalk; + +import android.app.Application; + +import com.smarthomies.realtimetalk.utils.RTTAppHelper; + +/** + * Created by ensar on 31/10/16. + */ +public class RTTApp extends Application { + public static final String TAG = RTTApp.class.getSimpleName(); + + @Override + public void onCreate() { + super.onCreate(); + + RTTAppHelper.getInstance().initWithContext(getApplicationContext()); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/RTTFragment.java b/app/src/main/java/com/smarthomies/realtimetalk/RTTFragment.java new file mode 100644 index 0000000..eaaa737 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/RTTFragment.java @@ -0,0 +1,26 @@ +package com.smarthomies.realtimetalk; + +import android.support.v4.app.Fragment; + +/** + * Created by ensar on 23/10/16. + */ +public class RTTFragment extends Fragment { + public static final String TAG = RTTFragment.class.getSimpleName(); + + protected void subscribeToSubjects() {} + + protected void unsubscribeFromSubjects() {} + + @Override + public void onResume() { + super.onResume(); + subscribeToSubjects(); + } + + @Override + public void onPause() { + super.onPause(); + unsubscribeFromSubjects(); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java b/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java new file mode 100644 index 0000000..cd3fc8c --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java @@ -0,0 +1,63 @@ +package com.smarthomies.realtimetalk.managers; + +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.models.network.LoginRequest; +import com.smarthomies.realtimetalk.models.network.RegistrationRequest; +import com.smarthomies.realtimetalk.services.AuthenticationAPIService; +import com.smarthomies.realtimetalk.utils.RTTAppHelper; + +import rx.Observable; +import rx.functions.Action1; +import rx.functions.Func1; + +/** + * Created by ensar on 01/11/16. + */ +public class AuthenticationManager { + public static final String TAG = AuthenticationManager.class.getSimpleName(); + + public Observable loginUser(String username, String password) { + return AuthenticationAPIService.getInstance().login(getLoginRequest(username, password)).doOnNext(processAuthenticationResponse); + } + + public Observable registerUser(User user, final String username, final String password) { + RegistrationRequest registrationRequest = getRegistrationRequest(user, username, password); + return AuthenticationAPIService.getInstance().register(registrationRequest) + .flatMap(new Func1>() { + @Override + public Observable call(AuthenticationResponse authenticationResponse) { + return Observable.just(getLoginRequest(username, password)); + } + }) + .flatMap(requestLogin).doOnNext(processAuthenticationResponse); + } + + private Func1> requestLogin = new Func1>() { + @Override + public Observable call(LoginRequest request) { + return AuthenticationAPIService.getInstance().login(request); + } + }; + + private Action1 processAuthenticationResponse = new Action1() { + @Override + public void call(AuthenticationResponse authenticationResponse) { + RTTAppHelper.getInstance().saveToken(authenticationResponse.getToken()); + } + }; + + private RegistrationRequest getRegistrationRequest(User user, String username, String password) { + RegistrationRequest registrationRequest = new RegistrationRequest(); + registrationRequest.setUsername(username); + registrationRequest.setPassword(password); + registrationRequest.setEmail(user.getEmail()); + registrationRequest.setFirstName(user.getFirstName()); + registrationRequest.setLastName(user.getLastName()); + return registrationRequest; + } + + private LoginRequest getLoginRequest(String username, String password) { + return new LoginRequest(username, password); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java b/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java new file mode 100644 index 0000000..8dad977 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java @@ -0,0 +1,24 @@ +package com.smarthomies.realtimetalk.managers; + +import com.smarthomies.realtimetalk.models.network.SearchRequest; +import com.smarthomies.realtimetalk.models.network.UsersResponse; +import com.smarthomies.realtimetalk.services.ContactsAPIService; + +import rx.Observable; + +/** + * Created by ensar on 01/11/16. + */ +public class ContactsManager { + public static final String TAG = ContactsManager.class.getSimpleName(); + + public Observable searchForUsers(String searchString) { + return ContactsAPIService.getInstance().search(getSearchRequest(searchString)); + } + + private SearchRequest getSearchRequest(String searchString) { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.setSearch(searchString); + return searchRequest; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java b/app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java new file mode 100644 index 0000000..247db3f --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java @@ -0,0 +1,51 @@ +package com.smarthomies.realtimetalk.models.db; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by ensar on 01/11/16. + */ +public class User{ + public static final String TAG = User.class.getSimpleName(); + + @SerializedName("first_name") + private String firstName; + @SerializedName("last_name") + private String lastName; + @SerializedName("email") + private String email; + @SerializedName("profile_picture") + private String imageUrl; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java new file mode 100644 index 0000000..8c0f307 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java @@ -0,0 +1,18 @@ +package com.smarthomies.realtimetalk.models.network; + +/** + * Created by ensar on 31/10/16. + */ +public class AuthenticationResponse { + public static final String TAG = AuthenticationResponse.class.getSimpleName(); + + private String token; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/LoginRequest.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/LoginRequest.java new file mode 100644 index 0000000..07271e9 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/LoginRequest.java @@ -0,0 +1,35 @@ +package com.smarthomies.realtimetalk.models.network; + +/** + * Created by ensar on 31/10/16. + */ +public class LoginRequest { + public static final String TAG = LoginRequest.class.getSimpleName(); + + public LoginRequest() { + } + + public LoginRequest(String username, String password) { + this.username = username; + this.password = password; + } + + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java new file mode 100644 index 0000000..b19e0b8 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java @@ -0,0 +1,54 @@ +package com.smarthomies.realtimetalk.models.network; + +/** + * Created by ensar on 31/10/16. + */ +public class RegistrationRequest { + public static final String TAG = RegistrationRequest.class.getSimpleName(); + + private String username; + private String password; + private String email; + private String firstName; + private String lastName; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/SearchRequest.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/SearchRequest.java new file mode 100644 index 0000000..82a745d --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/SearchRequest.java @@ -0,0 +1,18 @@ +package com.smarthomies.realtimetalk.models.network; + +/** + * Created by ensar on 06/12/16. + */ +public class SearchRequest { + public static final String TAG = SearchRequest.class.getSimpleName(); + + private String search; + + public String getSearch() { + return search; + } + + public void setSearch(String search) { + this.search = search; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/UsersResponse.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/UsersResponse.java new file mode 100644 index 0000000..d82dfc6 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/UsersResponse.java @@ -0,0 +1,22 @@ +package com.smarthomies.realtimetalk.models.network; + +import com.smarthomies.realtimetalk.models.db.User; + +import java.util.List; + +/** + * Created by ensar on 06/12/16. + */ +public class UsersResponse { + public static final String TAG = UsersResponse.class.getSimpleName(); + + private List data; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java b/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java new file mode 100644 index 0000000..cd4b4a1 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java @@ -0,0 +1,55 @@ +package com.smarthomies.realtimetalk.network; + +import com.smarthomies.realtimetalk.network.exceptions.APIException; +import com.smarthomies.realtimetalk.network.exceptions.BadAPIRequestException; +import com.smarthomies.realtimetalk.network.exceptions.NetworkException; +import com.smarthomies.realtimetalk.network.exceptions.RemoteResourceNotFoundException; +import com.smarthomies.realtimetalk.network.exceptions.ResourceForbiddenException; +import com.smarthomies.realtimetalk.network.exceptions.UnauthorizedException; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import retrofit2.adapter.rxjava.HttpException; + +/** + * Created by ensar on 31/10/16. + */ +public class APIErrorHandler { + public static final String TAG = APIErrorHandler.class.getSimpleName(); + + public static void handleGeneralAPIErrors(Throwable throwable) + throws APIException { + if(throwable instanceof HttpException) { + HttpException httpException = ((HttpException)throwable); + switch (httpException.response().code()) { + case HttpURLConnection.HTTP_BAD_REQUEST: + throw new BadAPIRequestException(throwable); + case HttpURLConnection.HTTP_NOT_FOUND: + throw new RemoteResourceNotFoundException(throwable); + case HttpURLConnection.HTTP_FORBIDDEN: + throw new ResourceForbiddenException(throwable); + case HttpURLConnection.HTTP_UNAUTHORIZED: + throw new UnauthorizedException(throwable); + } + } + if(throwable instanceof IOException) { + throw new NetworkException(throwable); + } + } + + public static void handleLoginErrors(Throwable throwable) + throws APIException { + handleGeneralAPIErrors(throwable); + } + + public static void handleRegistrationErrors(Throwable throwable) + throws APIException { + handleGeneralAPIErrors(throwable); + } + + public static void handleSearchErrors(Throwable throwable) + throws APIException { + handleGeneralAPIErrors(throwable); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java new file mode 100644 index 0000000..92fed48 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java @@ -0,0 +1,24 @@ +package com.smarthomies.realtimetalk.network; + +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.models.network.LoginRequest; +import com.smarthomies.realtimetalk.models.network.RegistrationRequest; + +import retrofit2.http.Body; +import retrofit2.http.POST; +import retrofit2.http.PUT; +import rx.Observable; + +/** + * Created by ensar on 31/10/16. + */ + +public interface AuthenticationAPI { + + @POST(NetworkingConstants.API_LOGIN_ENDPOINT) + Observable login(@Body LoginRequest request); + + @PUT(NetworkingConstants.API_REGISTRATION_ENDPOINT) + Observable register(@Body RegistrationRequest request); + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java new file mode 100644 index 0000000..8442cd3 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java @@ -0,0 +1,20 @@ +package com.smarthomies.realtimetalk.network; + +import com.smarthomies.realtimetalk.models.network.SearchRequest; +import com.smarthomies.realtimetalk.models.network.UsersResponse; + +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import rx.Observable; + +/** + * Created by ensar on 15/11/16. + */ + +public interface ContactsAPI { + + @POST(NetworkingConstants.API_SEARCH_ENDPOINT) + Observable searchUsers(@Body SearchRequest searchRequest); + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java b/app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java new file mode 100644 index 0000000..b391909 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java @@ -0,0 +1,14 @@ +package com.smarthomies.realtimetalk.network; + +/** + * Created by ensar on 31/10/16. + */ +public class NetworkingConstants { + public static final String TAG = NetworkingConstants.class.getSimpleName(); + + public static final String API_BASE_URL = "https://realtimetalk.herokuapp.com/rest/"; + public static final String API_LOGIN_ENDPOINT = "prijava"; + public static final String API_REGISTRATION_ENDPOINT = "dodaj"; + public static final String API_CONTACTS_ENDPOINT = "contacts"; + public static final String API_SEARCH_ENDPOINT = "search"; +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java b/app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java new file mode 100644 index 0000000..e4f12e2 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java @@ -0,0 +1,60 @@ +package com.smarthomies.realtimetalk.network; + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +/** + * Created by ensar on 31/10/16. + */ +public class RestClient { + public static final String TAG = RestClient.class.getSimpleName(); + + private static RestClient instance; + + private Retrofit retrofit; + private AuthenticationAPI authenticationAPI; + private ContactsAPI contactsAPI; + + private RestClient() { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + // set your desired log level + logging.setLevel(HttpLoggingInterceptor.Level.BODY); + + OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + // add your other interceptors … + + // add logging as last interceptor + httpClient.addInterceptor(logging); // <-- this is the important line! + + retrofit = new Retrofit.Builder() + .baseUrl(NetworkingConstants.API_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .client(httpClient.build()) + .build(); + } + + public static RestClient getInstance() { + if(instance == null) { + instance = new RestClient(); + } + return instance; + } + + public AuthenticationAPI getAuthenticationAPI() { + if(authenticationAPI == null) { + authenticationAPI = retrofit.create(AuthenticationAPI.class); + } + return authenticationAPI; + } + + public ContactsAPI getContactsAPI() { + if(contactsAPI == null) { + contactsAPI = retrofit.create(ContactsAPI.class); + } + return contactsAPI; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIException.java new file mode 100644 index 0000000..cc309ce --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIException.java @@ -0,0 +1,17 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +import java.net.HttpURLConnection; + +/** + * Created by ensar on 01/11/16. + */ +public class APIException extends Exception { + public static final String TAG = APIException.class.getSimpleName(); + + public APIException() { + } + + public APIException(Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/BadAPIRequestException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/BadAPIRequestException.java new file mode 100644 index 0000000..3d4206d --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/BadAPIRequestException.java @@ -0,0 +1,15 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +/** + * Created by ensar on 01/11/16. + */ +public class BadAPIRequestException extends APIException { + public static final String TAG = BadAPIRequestException.class.getSimpleName(); + + public BadAPIRequestException() { + } + + public BadAPIRequestException(Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/NetworkException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/NetworkException.java new file mode 100644 index 0000000..ebc113d --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/NetworkException.java @@ -0,0 +1,15 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +/** + * Created by ensar on 01/11/16. + */ +public class NetworkException extends APIException { + public static final String TAG = NetworkException.class.getSimpleName(); + + public NetworkException() { + } + + public NetworkException(Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/RemoteResourceNotFoundException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/RemoteResourceNotFoundException.java new file mode 100644 index 0000000..2f75e55 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/RemoteResourceNotFoundException.java @@ -0,0 +1,15 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +/** + * Created by ensar on 01/11/16. + */ +public class RemoteResourceNotFoundException extends APIException { + public static final String TAG = RemoteResourceNotFoundException.class.getSimpleName(); + + public RemoteResourceNotFoundException(Throwable cause) { + super(cause); + } + + public RemoteResourceNotFoundException() { + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ResourceForbiddenException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ResourceForbiddenException.java new file mode 100644 index 0000000..730d26f --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ResourceForbiddenException.java @@ -0,0 +1,15 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +/** + * Created by ensar on 01/11/16. + */ +public class ResourceForbiddenException extends APIException { + public static final String TAG = ResourceForbiddenException.class.getSimpleName(); + + public ResourceForbiddenException() { + } + + public ResourceForbiddenException(Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/UnauthorizedException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/UnauthorizedException.java new file mode 100644 index 0000000..37f2628 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/UnauthorizedException.java @@ -0,0 +1,15 @@ +package com.smarthomies.realtimetalk.network.exceptions; + +/** + * Created by ensar on 14/11/16. + */ +public class UnauthorizedException extends APIException { + public static final String TAG = UnauthorizedException.class.getSimpleName(); + + public UnauthorizedException(Throwable cause) { + super(cause); + } + + public UnauthorizedException() { + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java b/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java new file mode 100644 index 0000000..49949c2 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java @@ -0,0 +1,62 @@ +package com.smarthomies.realtimetalk.services; + +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.models.network.LoginRequest; +import com.smarthomies.realtimetalk.models.network.RegistrationRequest; +import com.smarthomies.realtimetalk.network.APIErrorHandler; +import com.smarthomies.realtimetalk.network.RestClient; +import com.smarthomies.realtimetalk.network.exceptions.APIException; + +import rx.Observable; +import rx.exceptions.Exceptions; +import rx.functions.Action1; + +/** + * Created by ensar on 31/10/16. + */ +public class AuthenticationAPIService { + public static final String TAG = AuthenticationAPIService.class.getSimpleName(); + + private static AuthenticationAPIService instance; + + private AuthenticationAPIService() {} + + public static AuthenticationAPIService getInstance() { + if(instance == null) { + instance = new AuthenticationAPIService(); + } + return instance; + } + + public Observable login(LoginRequest request) { + return RestClient.getInstance().getAuthenticationAPI().login(request) + .doOnError(handleLoginErrors); + } + + public Observable register(RegistrationRequest request) { + return RestClient.getInstance().getAuthenticationAPI().register(request) + .doOnError(handleRegistrationErrors); + } + + private Action1 handleLoginErrors = new Action1() { + @Override + public void call(Throwable throwable) { + try { + APIErrorHandler.handleLoginErrors(throwable); + } catch (APIException apiException) { + throw Exceptions.propagate(apiException); + } + } + }; + + private Action1 handleRegistrationErrors = new Action1() { + @Override + public void call(Throwable throwable) { + try { + APIErrorHandler.handleRegistrationErrors(throwable); + } catch (APIException apiException) { + throw Exceptions.propagate(apiException); + } + } + }; +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java b/app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java new file mode 100644 index 0000000..45c2ef9 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java @@ -0,0 +1,45 @@ +package com.smarthomies.realtimetalk.services; + +import com.smarthomies.realtimetalk.models.network.SearchRequest; +import com.smarthomies.realtimetalk.models.network.UsersResponse; +import com.smarthomies.realtimetalk.network.APIErrorHandler; +import com.smarthomies.realtimetalk.network.RestClient; +import com.smarthomies.realtimetalk.network.exceptions.APIException; + +import rx.Observable; +import rx.exceptions.Exceptions; +import rx.functions.Action1; + +/** + * Created by ensar on 31/10/16. + */ +public class ContactsAPIService { + public static final String TAG = ContactsAPIService.class.getSimpleName(); + + private static ContactsAPIService instance; + + private ContactsAPIService() {} + + public static ContactsAPIService getInstance() { + if(instance == null) { + instance = new ContactsAPIService(); + } + return instance; + } + + public Observable search(SearchRequest request) { + return RestClient.getInstance().getContactsAPI().searchUsers(request) + .doOnError(handleSearchErrors); + } + + private Action1 handleSearchErrors = new Action1() { + @Override + public void call(Throwable throwable) { + try { + APIErrorHandler.handleSearchErrors(throwable); + } catch (APIException apiException) { + throw Exceptions.propagate(apiException); + } + } + }; +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/NavigationSubject.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/NavigationSubject.java new file mode 100644 index 0000000..fc4940c --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/NavigationSubject.java @@ -0,0 +1,28 @@ +package com.smarthomies.realtimetalk.utils; + + +import android.os.Bundle; +import android.util.Pair; + +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.RTTFragment; + +import rx.subjects.PublishSubject; + +/** + * Created by ensar on 04/11/16. + */ +public class NavigationSubject { + public static final String TAG = NavigationSubject.class.getSimpleName(); + + private static PublishSubject, Bundle>> navigationSubject = PublishSubject.create(); + private static PublishSubject, Bundle>> fragmentNavigationSubject = PublishSubject.create(); + + public static PublishSubject, Bundle>> getInstance() { + return navigationSubject; + } + + public static PublishSubject, Bundle>> getFragmentNavigationInstance() { + return fragmentNavigationSubject; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java new file mode 100644 index 0000000..0b7c161 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java @@ -0,0 +1,55 @@ +package com.smarthomies.realtimetalk.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Created by ensar on 31/10/16. + */ +public class RTTAppHelper { + public static final String TAG = RTTAppHelper.class.getSimpleName(); + + public static final String SHARED_PREFERENCES_USER_TOKEN = "SHARED_PREFERENCES_USER_TOKEN"; + + private static RTTAppHelper instance; + private Context context; + + private RTTAppHelper() { + + } + + public static RTTAppHelper getInstance() { + if(instance == null) { + instance = new RTTAppHelper(); + } + return instance; + } + + public void initWithContext(Context context) { + this.context = context; + } + + public void saveToken(String token) { + writeToSharedPrefs(SHARED_PREFERENCES_USER_TOKEN, token); + } + + public String getToken() { + return readFromSharedPrefs(SHARED_PREFERENCES_USER_TOKEN); + } + + private void writeToSharedPrefs(String key, String value) { + SharedPreferences.Editor editor = context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit(); + editor.putString(key, value); + editor.apply(); + } + + private String readFromSharedPrefs(String key) { + return readFromSharedPrefs(key, null); + } + + private String readFromSharedPrefs(String key, String defaultValue) { + SharedPreferences sharedPreferences = context.getSharedPreferences(TAG, Context.MODE_PRIVATE); + return sharedPreferences.getString(key, defaultValue); + } + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java new file mode 100644 index 0000000..ff43f8e --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java @@ -0,0 +1,33 @@ +package com.smarthomies.realtimetalk.utils; + +import android.util.Log; +import android.widget.Toast; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.network.exceptions.BadAPIRequestException; +import com.smarthomies.realtimetalk.network.exceptions.NetworkException; +import com.smarthomies.realtimetalk.network.exceptions.RemoteResourceNotFoundException; +import com.smarthomies.realtimetalk.network.exceptions.ResourceForbiddenException; +import com.smarthomies.realtimetalk.network.exceptions.UnauthorizedException; + +/** + * Created by ensar on 14/11/16. + */ +public class RTTErrorUtil { + public static final String TAG = RTTErrorUtil.class.getSimpleName(); + + public static int getErrorString(Throwable e) { + if (e instanceof BadAPIRequestException) { + return R.string.error_bad_request; + } else if (e instanceof RemoteResourceNotFoundException) { + return R.string.error_user_not_found; + } else if (e instanceof ResourceForbiddenException) { + return R.string.error_unknown; + } else if (e instanceof UnauthorizedException) { + return R.string.error_user_bad_credentials; + } else if (e instanceof NetworkException) { + return R.string.error_no_internet; + } + return 0; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTUtil.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTUtil.java new file mode 100644 index 0000000..31ebb2c --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTUtil.java @@ -0,0 +1,92 @@ +package com.smarthomies.realtimetalk.utils; + +import android.text.TextUtils; +import android.util.Patterns; + +import com.smarthomies.realtimetalk.R; + +import java.util.regex.Pattern; + +/** + * Created by ensar on 03/11/16. + */ +public class RTTUtil { + public static final String TAG = RTTUtil.class.getSimpleName(); + + private static final Pattern hasUppercase = Pattern.compile("\\p{javaUpperCase}"); + private static final Pattern hasLowercase = Pattern.compile("\\p{javaLowerCase}"); + private static final Pattern hasNumber = Pattern.compile("\\p{javaDigit}"); + private static final Pattern hasSpecialChar = Pattern.compile("\\p{javaLetterOrDigit}"); + + public static final int PASSWORD_MINIMUM_LENGTH = 6; + public static final int PASSWORD_MAXIMUM_LENGTH = 20; + + public static boolean isPasswordValid(String password) { + if (TextUtils.isEmpty(password)) { + return false; + } + + if (password.length() < 6 || password.length() > 20) { + return false; + } + + boolean hasUppercase = RTTUtil.hasUppercase.matcher(password).matches(); + boolean hasLowercase = RTTUtil.hasLowercase.matcher(password).matches(); + boolean hasNumber = RTTUtil.hasNumber.matcher(password).matches(); + boolean hasSpecialChar = RTTUtil.hasSpecialChar.matcher(password).matches(); + + if (!hasNumber || !(hasUppercase || hasLowercase)) { + return false; + } + + return true; + } + + public static int getRequiredFieldError(String value) { + if (TextUtils.isEmpty(value)) { + return R.string.error_required; + } + + return 0; + } + + public static int getPasswordError(String password) { + if (TextUtils.isEmpty(password)) { + return R.string.error_required; + } + + if (password.length() < 6 || password.length() > 20) { + return R.string.error_password_short; + } + + + boolean hasUppercase = RTTUtil.hasUppercase.matcher(password).matches(); + boolean hasLowercase = RTTUtil.hasLowercase.matcher(password).matches(); + boolean hasNumber = RTTUtil.hasNumber.matcher(password).matches(); + boolean hasSpecialChar = RTTUtil.hasSpecialChar.matcher(password).matches(); + +// if (!hasNumber || !(hasUppercase || hasLowercase)) { +// return R.string.error_password_invalid; +// } + + return 0; + } + + public static int getPasswordConfirmationError(String password, String passwordConfirmation) { + int confirmationError = getPasswordError(passwordConfirmation); + + if(confirmationError == 0 && !passwordConfirmation.equalsIgnoreCase(password)) { + return R.string.error_password_confirmation; + } + + return confirmationError; + } + + public static boolean isEmailValid(String email) { + Pattern emailPattern = Patterns.EMAIL_ADDRESS; + return emailPattern.matcher(email).matches(); + } + + + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java new file mode 100644 index 0000000..0a7974b --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java @@ -0,0 +1,138 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.ObservableField; +import android.os.Bundle; +import android.util.Pair; +import android.view.View; + +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.managers.AuthenticationManager; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.utils.NavigationSubject; +import com.smarthomies.realtimetalk.utils.RTTUtil; +import com.smarthomies.realtimetalk.views.activities.MainActivity; +import com.smarthomies.realtimetalk.views.activities.RegistrationActivity; + +import rx.functions.Action0; +import rx.functions.Action1; +import rx.schedulers.Schedulers; +import rx.subjects.AsyncSubject; + +/** + * Created by ensar on 01/11/16. + */ +public class LoginViewModel extends BaseObservable { + public static final String TAG = LoginViewModel.class.getSimpleName(); + + private AsyncSubject loginSubject; + + private ObservableField username = new ObservableField<>(); + private ObservableField usernameError = new ObservableField<>(); + private ObservableField password = new ObservableField<>(); + private ObservableField passwordError = new ObservableField<>(); + + private ObservableField requestInProgress = new ObservableField<>(); + + public LoginViewModel() { + requestInProgress.set(false); + loginSubject = AsyncSubject.create(); + } + + public AsyncSubject createLoginSubject() { + loginSubject = AsyncSubject.create(); + return loginSubject; + } + + public AsyncSubject getLoginSubject() { + return loginSubject; + } + + private void loginUser() { + requestInProgress.set(true); + new AuthenticationManager().loginUser(username.get(), password.get()).subscribeOn(Schedulers.io()).subscribe(loginSubject); + } + + private boolean validateFields() { + clearErrors(); + + passwordError.set(RTTUtil.getPasswordError(password.get())); + usernameError.set(RTTUtil.getRequiredFieldError(username.get())); + + return passwordError.get() == 0 && usernameError.get() == 0; + } + + private void clearErrors() { + usernameError.set(0); + passwordError.set(0); + } + + public View.OnClickListener onLoginClick() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { +// if(validateFields()) { +// loginUser(); +// } + onLoginDone(); + } + }; + } + + public View.OnClickListener onRegisterClick() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(RegistrationActivity.class, null)); + } + }; + } + + public void onLoginDone() { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(MainActivity.class, null)); + } + + public void onRequestCompleted() { + requestInProgress.set(false); + } + + public ObservableField getUsername() { + return username; + } + + public void setUsername(ObservableField username) { + this.username = username; + } + + public ObservableField getUsernameError() { + return usernameError; + } + + public void setUsernameError(ObservableField usernameError) { + this.usernameError = usernameError; + } + + public ObservableField getPassword() { + return password; + } + + public void setPassword(ObservableField password) { + this.password = password; + } + + public ObservableField getPasswordError() { + return passwordError; + } + + public void setPasswordError(ObservableField passwordError) { + this.passwordError = passwordError; + } + + public ObservableField getRequestInProgress() { + return requestInProgress; + } + + public void setRequestInProgress(ObservableField requestInProgress) { + this.requestInProgress = requestInProgress; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java new file mode 100644 index 0000000..a947573 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java @@ -0,0 +1,44 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.Bindable; +import android.os.Bundle; +import android.util.Pair; +import android.view.View; + +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.utils.NavigationSubject; +import com.smarthomies.realtimetalk.views.activities.MainActivity; +import com.smarthomies.realtimetalk.views.activities.SearchActivity; + +/** + * Created by ensar on 15/11/16. + */ +public class MainViewModel extends BaseObservable { + public static final String TAG = MainViewModel.class.getSimpleName(); + + UserViewModel userViewModel; + + public void setUser(User user) { + userViewModel = new UserViewModel(user); + } + + @Bindable + public UserViewModel getUserViewModel() { + return userViewModel; + } + + public void setUserViewModel(UserViewModel userViewModel) { + this.userViewModel = userViewModel; + } + + public View.OnClickListener onSearchClick() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(SearchActivity.class, null)); + } + }; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/RegistrationViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/RegistrationViewModel.java new file mode 100644 index 0000000..2f316c7 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/RegistrationViewModel.java @@ -0,0 +1,265 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.ObservableField; +import android.os.Bundle; +import android.util.Pair; +import android.view.View; + +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.RTTFragment; +import com.smarthomies.realtimetalk.managers.AuthenticationManager; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.utils.NavigationSubject; +import com.smarthomies.realtimetalk.utils.RTTUtil; +import com.smarthomies.realtimetalk.views.activities.MainActivity; +import com.smarthomies.realtimetalk.views.fragments.registration.PasswordRegistrationFragment; +import com.smarthomies.realtimetalk.views.fragments.registration.UserNameRegistrationFragment; + +import rx.schedulers.Schedulers; +import rx.subjects.AsyncSubject; + +/** + * Created by ensar on 01/11/16. + */ +public class RegistrationViewModel extends BaseObservable { + public static final String TAG = RegistrationViewModel.class.getSimpleName(); + + private AsyncSubject registrationSubject; + + private ObservableField username = new ObservableField<>(); + private ObservableField firstName = new ObservableField<>(); + private ObservableField lastName = new ObservableField<>(); + private ObservableField email = new ObservableField<>(); + private ObservableField usernameError = new ObservableField<>(); + private ObservableField firstNameError = new ObservableField<>(); + private ObservableField lastNameError = new ObservableField<>(); + private ObservableField emailError = new ObservableField<>(); + private ObservableField password = new ObservableField<>(); + private ObservableField passwordConfirmation = new ObservableField<>(); + private ObservableField passwordError = new ObservableField<>(); + private ObservableField passwordConfirmationError = new ObservableField<>(); + + public RegistrationViewModel() { + requestInProgress.set(false); + registrationSubject = AsyncSubject.create(); + } + + public AsyncSubject createRegistrationSubject() { + registrationSubject = AsyncSubject.create(); + return registrationSubject; + } + + public AsyncSubject getRegistrationSubject() { + return registrationSubject; + } + + private void registerUser() { + requestInProgress.set(true); + User user = new User(); + user.setEmail(email.get()); + user.setFirstName(firstName.get()); + user.setLastName(lastName.get()); + new AuthenticationManager().registerUser(user, username.get(), password.get()).subscribeOn(Schedulers.io()).subscribe(registrationSubject); + } + + public View.OnClickListener onRegisterClick() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + if(validatePassword()) { + registerUser(); + } + } + }; + } + + public View.OnClickListener onExitClick() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationSubject.getInstance().onNext(null); + } + }; + } + + public View.OnClickListener onNamesNext() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + if(validateNames()) { + NavigationSubject.getFragmentNavigationInstance().onNext(new Pair, Bundle>(PasswordRegistrationFragment.class, null)); + } + } + }; + } + + public View.OnClickListener onInfoNext() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + if(validateInfo()) { + NavigationSubject.getFragmentNavigationInstance().onNext(new Pair, Bundle>(UserNameRegistrationFragment.class, null)); + } + } + }; + } + + private boolean validateNames() { + clearNamesErrors(); + + firstNameError.set(RTTUtil.getRequiredFieldError(firstName.get())); + lastNameError.set(RTTUtil.getRequiredFieldError(lastName.get())); + + return firstNameError.get() == 0 && lastNameError.get() == 0; + } + + private boolean validateInfo() { + clearInfoErrors(); + + usernameError.set(RTTUtil.getRequiredFieldError(username.get())); + emailError.set(RTTUtil.getRequiredFieldError(email.get())); + + return usernameError.get() == 0 && emailError.get() == 0; + } + + private boolean validatePassword() { + clearPasswordErrors(); + + passwordError.set(RTTUtil.getPasswordError(password.get())); + passwordConfirmationError.set(RTTUtil.getPasswordConfirmationError(password.get(), passwordConfirmation.get())); + + return passwordError.get() == 0 && passwordConfirmationError.get() == 0; + } + + private void clearInfoErrors() { + usernameError.set(0); + emailError.set(0); + } + + private void clearNamesErrors() { + firstNameError.set(0); + lastNameError.set(0); + } + + private void clearPasswordErrors() { + passwordError.set(0); + passwordConfirmationError.set(0); + } + + public void onRegistrationDone() { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(MainActivity.class, null)); + } + + public void onRequestCompleted() { + requestInProgress.set(false); + } + + + private ObservableField requestInProgress = new ObservableField<>(); + + public ObservableField getUsername() { + return username; + } + + public void setUsername(ObservableField username) { + this.username = username; + } + + public ObservableField getFirstName() { + return firstName; + } + + public void setFirstName(ObservableField firstName) { + this.firstName = firstName; + } + + public ObservableField getLastName() { + return lastName; + } + + public void setLastName(ObservableField lastName) { + this.lastName = lastName; + } + + public ObservableField getEmail() { + return email; + } + + public void setEmail(ObservableField email) { + this.email = email; + } + + public ObservableField getUsernameError() { + return usernameError; + } + + public void setUsernameError(ObservableField usernameError) { + this.usernameError = usernameError; + } + + public ObservableField getFirstNameError() { + return firstNameError; + } + + public void setFirstNameError(ObservableField firstNameError) { + this.firstNameError = firstNameError; + } + + public ObservableField getLastNameError() { + return lastNameError; + } + + public void setLastNameError(ObservableField lastNameError) { + this.lastNameError = lastNameError; + } + + public ObservableField getEmailError() { + return emailError; + } + + public void setEmailError(ObservableField emailError) { + this.emailError = emailError; + } + + public ObservableField getPassword() { + return password; + } + + public void setPassword(ObservableField password) { + this.password = password; + } + + public ObservableField getPasswordConfirmation() { + return passwordConfirmation; + } + + public void setPasswordConfirmation(ObservableField passwordConfirmation) { + this.passwordConfirmation = passwordConfirmation; + } + + public ObservableField getPasswordError() { + return passwordError; + } + + public void setPasswordError(ObservableField passwordError) { + this.passwordError = passwordError; + } + + public ObservableField getPasswordConfirmationError() { + return passwordConfirmationError; + } + + public void setPasswordConfirmationError(ObservableField passwordConfirmationError) { + this.passwordConfirmationError = passwordConfirmationError; + } + + public ObservableField getRequestInProgress() { + return requestInProgress; + } + + public void setRequestInProgress(ObservableField requestInProgress) { + this.requestInProgress = requestInProgress; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java new file mode 100644 index 0000000..e0a622a --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java @@ -0,0 +1,87 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.Bindable; +import android.databinding.Observable; +import android.databinding.ObservableField; +import android.util.Log; + +import com.smarthomies.realtimetalk.managers.ContactsManager; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.models.network.UsersResponse; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/** + * Created by ensar on 15/11/16. + */ +public class SearchViewModel extends BaseObservable { + public static final String TAG = SearchViewModel.class.getSimpleName(); + + private ObservableField> users = new ObservableField<>(); + private ObservableField search = new ObservableField<>(); + + private rx.Observable rxSearch; + private Subscription subscription; + + public SearchViewModel() { + rxSearch = rx.Observable.create(new rx.Observable.OnSubscribe() { + @Override + public void call(final Subscriber subscriber) { + search.addOnPropertyChangedCallback(new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(Observable observable, int i) { + String query = ((ObservableField) observable).get(); + if(subscriber.isUnsubscribed()) { + + } else { + subscriber.onNext(query); + } + } + }); + } + }); + rxSearch = rxSearch.debounce(1000, TimeUnit.MILLISECONDS); + subscription = rxSearch.flatMap(new Func1>() { + @Override + public rx.Observable call(String s) { + return new ContactsManager().searchForUsers(s); + } + }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1() { + @Override + public void call(UsersResponse usersResponse) { + users.set(usersResponse.getData()); + } + }); + } + + public ObservableField> getUsers() { + return users; + } + + public void setUsers(ObservableField> users) { + this.users = users; + } + + public ObservableField getSearch() { + return search; + } + + public void setSearch(ObservableField search) { + this.search = search; + } + + public void clear() { + if(subscription != null && !subscription.isUnsubscribed()) { + subscription.unsubscribe(); + } + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java new file mode 100644 index 0000000..8d5e275 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java @@ -0,0 +1,57 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.Bindable; +import android.databinding.BindingAdapter; +import android.databinding.ObservableBoolean; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.models.db.User; +import com.squareup.picasso.Picasso; + +/** + * Created by ensar on 15/11/16. + */ +public class UserViewModel extends BaseObservable { + public static final String TAG = UserViewModel.class.getSimpleName(); + + private User model; + private ObservableBoolean state = new ObservableBoolean(); + + public UserViewModel(User model) { + this.model = model; + } + + @Bindable + public String getName() { + return model.getFirstName() + " " + model.getLastName(); + } + + @Bindable + public String getEmail() { + return model.getEmail(); + } + + @Bindable + public String getImageUrl() { + return model.getImageUrl(); + } + + public ObservableBoolean getState() { + return state; + } + + public View.OnClickListener changeContactState() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.d(TAG, "onClick: "); + state.set(!state.get()); + } + }; + } + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/LoginActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/LoginActivity.java new file mode 100644 index 0000000..bd2a8a7 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/LoginActivity.java @@ -0,0 +1,92 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.widget.Toast; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.databinding.ActivityLoginBinding; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.utils.RTTErrorUtil; +import com.smarthomies.realtimetalk.viewmodels.LoginViewModel; +import com.smarthomies.realtimetalk.views.activities.bindingutils.OnErrorChangedCallback; + + +import rx.Observable; +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.exceptions.CompositeException; +import rx.functions.Func0; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +public class LoginActivity extends RTTActivity { + + private LoginViewModel viewModel; + private Subscription loginSubscription; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_login); + viewModel = new LoginViewModel(); + binding.setViewModel(viewModel); + + viewModel.getPasswordError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilPassword)); + viewModel.getUsernameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilUsername)); + } + + @Override + protected void subscribeToSubjects() { + super.subscribeToSubjects(); + loginSubscription = viewModel.getLoginSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new LoginSubscriber()); + } + + @Override + protected void unsubscribeFromSubjects() { + super.unsubscribeFromSubjects(); + if(loginSubscription != null && !loginSubscription.isUnsubscribed()) { + loginSubscription.unsubscribe(); + } + } + + private void reconnectToLoginSubject() { + loginSubscription = viewModel.createLoginSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new LoginSubscriber()); + } + + private class LoginSubscriber extends Subscriber { + @Override + public void onCompleted() { + reconnectToLoginSubject(); + viewModel.onRequestCompleted(); + } + + @Override + public void onError(Throwable e) { + reconnectToLoginSubject(); + viewModel.onRequestCompleted(); + + if (e instanceof CompositeException) { + for (Throwable ex : ((CompositeException) e).getExceptions()) { + if(ex instanceof RuntimeException) { + handleException(ex.getCause()); + } + } + } else { + handleException(e); + } + } + + @Override + public void onNext(AuthenticationResponse authenticationResponse) { + viewModel.onLoginDone(); + } + } + + private void handleException(Throwable e) { + Toast.makeText(this, RTTErrorUtil.getErrorString(e), Toast.LENGTH_SHORT).show(); + } +} + diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java new file mode 100644 index 0000000..81c84e4 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java @@ -0,0 +1,143 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.support.design.widget.NavigationView; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.databinding.ActivityMainBinding; +import com.smarthomies.realtimetalk.databinding.AppBarMainBinding; +import com.smarthomies.realtimetalk.databinding.NavHeaderMainBinding; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.viewmodels.UserViewModel; +import com.smarthomies.realtimetalk.views.adapters.UsersAdapter; +import com.smarthomies.realtimetalk.views.fragments.MainViewModelHolder; +import com.smarthomies.realtimetalk.views.fragments.registration.RegistrationViewModelHolder; + +import java.util.ArrayList; + +public class MainActivity extends RTTActivity + implements NavigationView.OnNavigationItemSelectedListener { + + private MainViewModelHolder viewModelHolder; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Add retained to fragment manager + if(getSupportFragmentManager().findFragmentByTag(MainViewModelHolder.class.getName()) == null) { + viewModelHolder = new MainViewModelHolder(); + + getSupportFragmentManager() + .beginTransaction() + .add(viewModelHolder, MainViewModelHolder.class.getName()) + .commit(); + } + else { + viewModelHolder = (MainViewModelHolder)getSupportFragmentManager().findFragmentByTag(MainViewModelHolder.class.getName()); + } + + ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); + AppBarMainBinding appBarMainBinding = binding.appBarMain; + User user = new User(); + user.setFirstName("Ensar"); + user.setLastName("Sarajcic"); + user.setEmail("es.ensar@gmail.com"); + user.setImageUrl("https://avatars3.githubusercontent.com/u/2764831?v=3&s=460"); + viewModelHolder.getMainViewModel().setUser(user); + binding.setViewModel(viewModelHolder.getMainViewModel()); + appBarMainBinding.setViewModel(viewModelHolder.getMainViewModel()); + + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rvUsersList); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + UsersAdapter usersAdapter = new UsersAdapter(UsersAdapter.ViewType.LIST); + ArrayList users = new ArrayList(); + users.add(user); + usersAdapter.setUsers(users); + + recyclerView.setAdapter(usersAdapter); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + + + setSupportActionBar(toolbar); + + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( + this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); + drawer.setDrawerListener(toggle); + toggle.syncState(); + + binding.navView.setNavigationItemSelectedListener(this); + } + + @Override + public void onBackPressed() { + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + if (drawer.isDrawerOpen(GravityCompat.START)) { + drawer.closeDrawer(GravityCompat.START); + } else { + super.onBackPressed(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @SuppressWarnings("StatementWithEmptyBody") + @Override + public boolean onNavigationItemSelected(MenuItem item) { + // Handle navigation view item clicks here. + int id = item.getItemId(); + + if (id == R.id.nav_logout) { + // Handle the camera action + startActivity(new Intent(this, LoginActivity.class)); + finish(); + } + + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + return true; + } + + public MainViewModelHolder getViewModelHolder() { + return viewModelHolder; + } + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/RegistrationActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/RegistrationActivity.java new file mode 100644 index 0000000..cdb3d9d --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/RegistrationActivity.java @@ -0,0 +1,53 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.databinding.ActivityRegistrationBinding; +import com.smarthomies.realtimetalk.views.fragments.registration.RegistrationFragment; +import com.smarthomies.realtimetalk.views.fragments.registration.RegistrationViewModelHolder; +import com.smarthomies.realtimetalk.views.fragments.registration.UserInfoRegistrationFragment; + +public class RegistrationActivity extends RTTActivity { + + private RegistrationViewModelHolder viewModelHolder; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Add retained to fragment manager + if(getSupportFragmentManager().findFragmentByTag(RegistrationViewModelHolder.class.getName()) == null) { + viewModelHolder = new RegistrationViewModelHolder(); + + getSupportFragmentManager() + .beginTransaction() + .add(viewModelHolder, RegistrationViewModelHolder.class.getName()) + .commit(); + } + else { + viewModelHolder = (RegistrationViewModelHolder)getSupportFragmentManager().findFragmentByTag(RegistrationViewModelHolder.class.getName()); + } + + ActivityRegistrationBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_registration); + binding.setViewModel(viewModelHolder.getRegistrationViewModel()); + + startFragment(UserInfoRegistrationFragment.class, null); + + } + + @Override + protected int getFragmentContainerRId() { + return R.id.fragment_container; + } + + public void startFragment(Class clazz, Bundle bundle) { + startFragment(clazz, R.id.fragment_container, bundle); + } + + public RegistrationViewModelHolder getViewModelHolder() { + return viewModelHolder; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SearchActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SearchActivity.java new file mode 100644 index 0000000..efac856 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SearchActivity.java @@ -0,0 +1,117 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.app.SearchManager; +import android.content.Context; +import android.databinding.DataBindingUtil; +import android.databinding.Observable; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.view.GravityCompat; +import android.support.v4.view.MenuItemCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SearchView; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.databinding.ObservableField; +import java.util.List; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.databinding.ActivitySearchBinding; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.viewmodels.SearchViewModel; +import com.smarthomies.realtimetalk.views.adapters.UsersAdapter; +import com.smarthomies.realtimetalk.views.fragments.SearchViewModelHolder; + +import java.util.ArrayList; + +public class SearchActivity extends RTTActivity { + + private SearchViewModelHolder viewModelHolder; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Add retained to fragment manager + if(getSupportFragmentManager().findFragmentByTag(SearchViewModelHolder.class.getName()) == null) { + viewModelHolder = new SearchViewModelHolder(); + + getSupportFragmentManager() + .beginTransaction() + .add(viewModelHolder, SearchViewModelHolder.class.getName()) + .commit(); + } + else { + viewModelHolder = (SearchViewModelHolder)getSupportFragmentManager().findFragmentByTag(SearchViewModelHolder.class.getName()); + } + + ActivitySearchBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_search); + binding.setViewModel(viewModelHolder.getSearchViewModel()); + + final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rvSearchList); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + SearchViewModel searchViewModel = viewModelHolder.getSearchViewModel(); + searchViewModel.getUsers().addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(Observable observable, int i) { + List users = ((ObservableField>)observable).get(); + UsersAdapter usersAdapter = new UsersAdapter(UsersAdapter.ViewType.SEARCH); + usersAdapter.setUsers(users); + recyclerView.setAdapter(usersAdapter); + } + }); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + + setSupportActionBar(toolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.search, menu); + final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); + SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + getViewModelHolder().getSearchViewModel().getSearch().set(query); + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + getViewModelHolder().getSearchViewModel().getSearch().set(newText); + return false; + } + }); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.rvSearchList) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + public SearchViewModelHolder getViewModelHolder() { + return viewModelHolder; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/BindingAdapters.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/BindingAdapters.java new file mode 100644 index 0000000..5cc5f32 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/BindingAdapters.java @@ -0,0 +1,34 @@ +package com.smarthomies.realtimetalk.views.activities.bindingutils; + +import android.databinding.BindingAdapter; +import android.support.design.widget.NavigationView; +import android.view.LayoutInflater; +import android.widget.ImageView; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.viewmodels.UserViewModel; +import com.squareup.picasso.Picasso; + +import com.smarthomies.realtimetalk.databinding.NavHeaderMainBinding; + +/** + * Created by ensar on 17/11/16. + */ +public class BindingAdapters { + public static final String TAG = BindingAdapters.class.getSimpleName(); + + @BindingAdapter("android:src") + public static void setImageUrl(ImageView view, String url) { + Picasso.with(view.getContext()).load(url).placeholder(R.mipmap.ic_launcher).into(view); + } + + @BindingAdapter({"headerLayout"}) + public static void setHeaderViewModel(NavigationView view, UserViewModel userViewModel) { + NavHeaderMainBinding navHeaderMainBinding = NavHeaderMainBinding.inflate(LayoutInflater.from(view.getContext())); + navHeaderMainBinding.setViewModel(userViewModel); + navHeaderMainBinding.executePendingBindings(); + view.addHeaderView(navHeaderMainBinding.getRoot()); + } + + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/OnErrorChangedCallback.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/OnErrorChangedCallback.java new file mode 100644 index 0000000..5d28ca4 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/bindingutils/OnErrorChangedCallback.java @@ -0,0 +1,29 @@ +package com.smarthomies.realtimetalk.views.activities.bindingutils; + +import android.databinding.Observable; +import android.databinding.ObservableField; +import android.support.design.widget.TextInputLayout; +import android.text.TextUtils; + +/** + * Created by ensar on 01/11/16. + */ +public class OnErrorChangedCallback extends Observable.OnPropertyChangedCallback { + + private TextInputLayout layout; + + public OnErrorChangedCallback(TextInputLayout textInputLayout) { + layout = textInputLayout; + } + + @Override + public void onPropertyChanged(Observable observable, int i) { + Integer errorRId = ((ObservableField) observable).get(); + if (errorRId == 0) { + layout.setErrorEnabled(false); + } else { + layout.setErrorEnabled(true); + layout.setError(layout.getContext().getString(errorRId)); + } + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/adapters/UsersAdapter.java b/app/src/main/java/com/smarthomies/realtimetalk/views/adapters/UsersAdapter.java new file mode 100644 index 0000000..d9cb710 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/adapters/UsersAdapter.java @@ -0,0 +1,96 @@ +package com.smarthomies.realtimetalk.views.adapters; + +import android.databinding.DataBindingUtil; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.databinding.SearchUserListItemBinding; +import com.smarthomies.realtimetalk.databinding.UserListItemBinding; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.viewmodels.UserViewModel; + +import java.util.List; + +/** + * Created by ensar on 26/11/16. + */ +public class UsersAdapter extends RecyclerView.Adapter{ + public static final String TAG = UsersAdapter.class.getSimpleName(); + + public enum ViewType { + LIST, + SEARCH + } + + private List users; + private ViewType viewType; + + public UsersAdapter(ViewType viewType) { + this.viewType = viewType; + } + + @Override + public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (this.viewType) { + case LIST: + UserListItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.user_list_item, parent, false); + return new UserViewHolder(binding); + case SEARCH: + default: + SearchUserListItemBinding binding2 = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.search_user_list_item, parent, false); + return new UserViewHolder(binding2); + } + } + + @Override + public void onBindViewHolder(UserViewHolder holder, int position) { + switch (this.viewType) { + case LIST: + UserListItemBinding binding = holder.getBinding(); + binding.setViewModel(new UserViewModel(users.get(position))); + break; + case SEARCH: + SearchUserListItemBinding binding2 = holder.getSearchUserListItemBinding(); + binding2.setViewModel(new UserViewModel(users.get(position))); + break; + } + } + + @Override + public int getItemCount() { + return users.size(); + } + + public class UserViewHolder extends RecyclerView.ViewHolder { + private UserListItemBinding binding; + private SearchUserListItemBinding searchUserListItemBinding; + + public UserViewHolder(UserListItemBinding userListItemBinding) { + super(userListItemBinding.getRoot()); + this.binding = userListItemBinding; + } + + public UserViewHolder(SearchUserListItemBinding userListItemBinding) { + super(userListItemBinding.getRoot()); + this.searchUserListItemBinding = userListItemBinding; + } + + public UserListItemBinding getBinding() { + return binding; + } + + public SearchUserListItemBinding getSearchUserListItemBinding() { + return searchUserListItemBinding; + } + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java new file mode 100644 index 0000000..a6d2f80 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java @@ -0,0 +1,20 @@ +package com.smarthomies.realtimetalk.views.fragments; + +import com.smarthomies.realtimetalk.viewmodels.MainViewModel; + +/** + * Created by ensar on 15/11/16. + */ +public class MainViewModelHolder extends ViewModelHolder { + public static final String TAG = MainViewModelHolder.class.getSimpleName(); + + private MainViewModel mainViewModel = new MainViewModel(); + + public MainViewModel getMainViewModel() { + return mainViewModel; + } + + public void setMainViewModel(MainViewModel mainViewModel) { + this.mainViewModel = mainViewModel; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/SearchViewModelHolder.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/SearchViewModelHolder.java new file mode 100644 index 0000000..f3159aa --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/SearchViewModelHolder.java @@ -0,0 +1,26 @@ +package com.smarthomies.realtimetalk.views.fragments; + +import com.smarthomies.realtimetalk.viewmodels.SearchViewModel; + +/** + * Created by ensar on 15/11/16. + */ +public class SearchViewModelHolder extends ViewModelHolder { + public static final String TAG = SearchViewModelHolder.class.getSimpleName(); + + private SearchViewModel searchViewModel = new SearchViewModel(); + + public SearchViewModel getSearchViewModel() { + return searchViewModel; + } + + public void setSearchViewModel(SearchViewModel searchViewModel) { + this.searchViewModel = searchViewModel; + } + + @Override + public void onDestroy() { + super.onDestroy(); + searchViewModel.clear(); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ViewModelHolder.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ViewModelHolder.java new file mode 100644 index 0000000..a8aef7c --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ViewModelHolder.java @@ -0,0 +1,17 @@ +package com.smarthomies.realtimetalk.views.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; + +/** + * Created by ensar on 13/11/16. + */ +public abstract class ViewModelHolder extends Fragment { + public static final String TAG = ViewModelHolder.class.getSimpleName(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/PasswordRegistrationFragment.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/PasswordRegistrationFragment.java new file mode 100644 index 0000000..ec9cd03 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/PasswordRegistrationFragment.java @@ -0,0 +1,94 @@ +package com.smarthomies.realtimetalk.views.fragments.registration; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.databinding.PasswordRegistrationBinding; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.utils.RTTErrorUtil; +import com.smarthomies.realtimetalk.views.activities.bindingutils.OnErrorChangedCallback; + +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.exceptions.CompositeException; +import rx.schedulers.Schedulers; + +/** + * Created by ensar on 08/11/16. + */ +public class PasswordRegistrationFragment extends RegistrationFragment { + public static final String TAG = PasswordRegistrationFragment.class.getSimpleName(); + + private Subscription registrationSubscription; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + PasswordRegistrationBinding binding = DataBindingUtil.inflate(inflater, R.layout.password_registration, container, false); + View view = binding.getRoot(); + binding.setViewModel(getViewModel()); + + getViewModel().getPasswordError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilPassword)); + getViewModel().getPasswordConfirmationError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilConfirmPassword)); + + return view; + } + + @Override + protected void subscribeToSubjects() { + super.subscribeToSubjects(); + registrationSubscription = getViewModel().getRegistrationSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new RegistrationSubscriber()); + } + + @Override + protected void unsubscribeFromSubjects() { + super.unsubscribeFromSubjects(); + if(registrationSubscription != null && !registrationSubscription.isUnsubscribed()) { + registrationSubscription.unsubscribe(); + } + } + + private void reconnectToRegistrationSubject() { + registrationSubscription = getViewModel().createRegistrationSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new RegistrationSubscriber()); + } + + private void handleException(Throwable e) { + Toast.makeText(getContext(), RTTErrorUtil.getErrorString(e), Toast.LENGTH_SHORT).show(); + } + + private class RegistrationSubscriber extends Subscriber { + @Override + public void onCompleted() { + reconnectToRegistrationSubject(); + getViewModel().onRequestCompleted(); + } + + @Override + public void onError(Throwable e) { + reconnectToRegistrationSubject(); + getViewModel().onRequestCompleted(); + + if (e instanceof CompositeException) { + for (Throwable ex : ((CompositeException) e).getExceptions()) { + if(ex instanceof RuntimeException) { + handleException(ex.getCause()); + } + } + } else { + handleException(e); + } + } + + @Override + public void onNext(AuthenticationResponse authenticationResponse) { + getViewModel().onRegistrationDone(); + } + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationFragment.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationFragment.java new file mode 100644 index 0000000..3af151b --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationFragment.java @@ -0,0 +1,16 @@ +package com.smarthomies.realtimetalk.views.fragments.registration; + +import com.smarthomies.realtimetalk.RTTFragment; +import com.smarthomies.realtimetalk.viewmodels.RegistrationViewModel; +import com.smarthomies.realtimetalk.views.activities.RegistrationActivity; + +/** + * Created by ensar on 08/11/16. + */ +public class RegistrationFragment extends RTTFragment { + public static final String TAG = RegistrationFragment.class.getSimpleName(); + + protected RegistrationViewModel getViewModel() { + return ((RegistrationActivity) getActivity()).getViewModelHolder().getRegistrationViewModel(); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationViewModelHolder.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationViewModelHolder.java new file mode 100644 index 0000000..1478e96 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/RegistrationViewModelHolder.java @@ -0,0 +1,21 @@ +package com.smarthomies.realtimetalk.views.fragments.registration; + +import com.smarthomies.realtimetalk.viewmodels.RegistrationViewModel; +import com.smarthomies.realtimetalk.views.fragments.ViewModelHolder; + +/** + * Created by ensar on 13/11/16. + */ +public class RegistrationViewModelHolder extends ViewModelHolder { + public static final String TAG = RegistrationViewModelHolder.class.getSimpleName(); + + private RegistrationViewModel registrationViewModel = new RegistrationViewModel(); + + public RegistrationViewModel getRegistrationViewModel() { + return registrationViewModel; + } + + public void setRegistrationViewModel(RegistrationViewModel registrationViewModel) { + this.registrationViewModel = registrationViewModel; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserInfoRegistrationFragment.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserInfoRegistrationFragment.java new file mode 100644 index 0000000..00f9ed3 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserInfoRegistrationFragment.java @@ -0,0 +1,33 @@ +package com.smarthomies.realtimetalk.views.fragments.registration; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.databinding.UserInfoRegistrationBinding; +import com.smarthomies.realtimetalk.views.activities.bindingutils.OnErrorChangedCallback; + +/** + * Created by ensar on 08/11/16. + */ +public class UserInfoRegistrationFragment extends RegistrationFragment { + public static final String TAG = UserInfoRegistrationFragment.class.getSimpleName(); + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + UserInfoRegistrationBinding binding = DataBindingUtil.inflate(inflater, R.layout.user_info_registration, container, false); + View view = binding.getRoot(); + binding.setViewModel(getViewModel()); + + getViewModel().getUsernameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilUsername)); + getViewModel().getEmailError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilEmail)); + + + return view; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserNameRegistrationFragment.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserNameRegistrationFragment.java new file mode 100644 index 0000000..107f28a --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/registration/UserNameRegistrationFragment.java @@ -0,0 +1,32 @@ +package com.smarthomies.realtimetalk.views.fragments.registration; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.databinding.UserNameRegistrationBinding; +import com.smarthomies.realtimetalk.views.activities.bindingutils.OnErrorChangedCallback; + +/** + * Created by ensar on 08/11/16. + */ +public class UserNameRegistrationFragment extends RegistrationFragment { + public static final String TAG = UserNameRegistrationFragment.class.getSimpleName(); + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + UserNameRegistrationBinding binding = DataBindingUtil.inflate(inflater, R.layout.user_name_registration, container, false); + View view = binding.getRoot(); + binding.setViewModel(getViewModel()); + + getViewModel().getFirstNameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilFirstName)); + getViewModel().getLastNameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilLastName)); + + return view; + } +} diff --git a/app/src/main/res/drawable-v21/ic_menu_camera.xml b/app/src/main/res/drawable-v21/ic_menu_camera.xml new file mode 100644 index 0000000..0d9ea10 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_camera.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_gallery.xml b/app/src/main/res/drawable-v21/ic_menu_gallery.xml new file mode 100644 index 0000000..f6872c4 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_gallery.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_manage.xml b/app/src/main/res/drawable-v21/ic_menu_manage.xml new file mode 100644 index 0000000..c1be60b --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_manage.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ic_menu_send.xml b/app/src/main/res/drawable-v21/ic_menu_send.xml new file mode 100644 index 0000000..00c668c --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_send.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_share.xml b/app/src/main/res/drawable-v21/ic_menu_share.xml new file mode 100644 index 0000000..a28fb9e --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_slideshow.xml b/app/src/main/res/drawable-v21/ic_menu_slideshow.xml new file mode 100644 index 0000000..209aa64 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_slideshow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/button_normal.xml b/app/src/main/res/drawable/button_normal.xml new file mode 100644 index 0000000..dac9834 --- /dev/null +++ b/app/src/main/res/drawable/button_normal.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml new file mode 100644 index 0000000..458b4b0 --- /dev/null +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..d2692fa --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +