Initial commit

master
esensar 2016-12-07 02:14:08 +01:00
commit a9d1051aa8
106 changed files with 4095 additions and 0 deletions

9
.gitignore vendored 100644
View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

22
.idea/compiler.xml 100644
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View File

@ -0,0 +1,3 @@
<component name="ProjectDictionaryState">
<dictionary name="ensar" />
</component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

19
.idea/gradle.xml 100644
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.14.1" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

96
.idea/misc.xml 100644
View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="mavenHome" value="Bundled (Maven 3)" />
</MavenGeneralSettings>
</option>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
</expanded-state>
<selected-state>
<State>
<id>Android</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.7</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ScopeChooserConfigurable.UI">
<settings>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/RealTimeTalk.iml" filepath="$PROJECT_DIR$/RealTimeTalk.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

1
app/.gitignore vendored 100644
View File

@ -0,0 +1 @@
/build

44
app/build.gradle 100644
View File

@ -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'
}

17
app/proguard-rules.pro vendored 100644
View File

@ -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 *;
#}

View File

@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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());
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smarthomies.realtimetalk">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".RTTApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".views.activities.LoginActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".views.activities.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".views.activities.SearchActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:parentActivityName=".views.activities.MainActivity"/>
<activity android:name=".views.activities.RegistrationActivity"
android:theme="@style/AppTheme.NoActionBar"/>
</application>
</manifest>

View File

@ -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<Pair<Class<? extends RTTActivity>, Bundle>>() {
@Override
public void call(Pair<Class<? extends RTTActivity>, 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<Pair<Class<? extends RTTFragment>, Bundle>>() {
@Override
public void call(Pair<Class<? extends RTTFragment>, 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<? extends RTTFragment> 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);
}
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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<AuthenticationResponse> loginUser(String username, String password) {
return AuthenticationAPIService.getInstance().login(getLoginRequest(username, password)).doOnNext(processAuthenticationResponse);
}
public Observable<AuthenticationResponse> registerUser(User user, final String username, final String password) {
RegistrationRequest registrationRequest = getRegistrationRequest(user, username, password);
return AuthenticationAPIService.getInstance().register(registrationRequest)
.flatMap(new Func1<AuthenticationResponse, Observable<LoginRequest>>() {
@Override
public Observable<LoginRequest> call(AuthenticationResponse authenticationResponse) {
return Observable.just(getLoginRequest(username, password));
}
})
.flatMap(requestLogin).doOnNext(processAuthenticationResponse);
}
private Func1<LoginRequest, Observable<AuthenticationResponse>> requestLogin = new Func1<LoginRequest, Observable<AuthenticationResponse>>() {
@Override
public Observable<AuthenticationResponse> call(LoginRequest request) {
return AuthenticationAPIService.getInstance().login(request);
}
};
private Action1<AuthenticationResponse> processAuthenticationResponse = new Action1<AuthenticationResponse>() {
@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);
}
}

View File

@ -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<UsersResponse> searchForUsers(String searchString) {
return ContactsAPIService.getInstance().search(getSearchRequest(searchString));
}
private SearchRequest getSearchRequest(String searchString) {
SearchRequest searchRequest = new SearchRequest();
searchRequest.setSearch(searchString);
return searchRequest;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<User> data;
public List<User> getData() {
return data;
}
public void setData(List<User> data) {
this.data = data;
}
}

View File

@ -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);
}
}

View File

@ -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<AuthenticationResponse> login(@Body LoginRequest request);
@PUT(NetworkingConstants.API_REGISTRATION_ENDPOINT)
Observable<AuthenticationResponse> register(@Body RegistrationRequest request);
}

View File

@ -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<UsersResponse> searchUsers(@Body SearchRequest searchRequest);
}

View File

@ -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";
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}

View File

@ -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() {
}
}

View File

@ -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<AuthenticationResponse> login(LoginRequest request) {
return RestClient.getInstance().getAuthenticationAPI().login(request)
.doOnError(handleLoginErrors);
}
public Observable<AuthenticationResponse> register(RegistrationRequest request) {
return RestClient.getInstance().getAuthenticationAPI().register(request)
.doOnError(handleRegistrationErrors);
}
private Action1<Throwable> handleLoginErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleLoginErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}
}
};
private Action1<Throwable> handleRegistrationErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleRegistrationErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}
}
};
}

View File

@ -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<UsersResponse> search(SearchRequest request) {
return RestClient.getInstance().getContactsAPI().searchUsers(request)
.doOnError(handleSearchErrors);
}
private Action1<Throwable> handleSearchErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleSearchErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}
}
};
}

View File

@ -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<Pair<Class<? extends RTTActivity>, Bundle>> navigationSubject = PublishSubject.create();
private static PublishSubject<Pair<Class<? extends RTTFragment>, Bundle>> fragmentNavigationSubject = PublishSubject.create();
public static PublishSubject<Pair<Class<? extends RTTActivity>, Bundle>> getInstance() {
return navigationSubject;
}
public static PublishSubject<Pair<Class<? extends RTTFragment>, Bundle>> getFragmentNavigationInstance() {
return fragmentNavigationSubject;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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<AuthenticationResponse> loginSubject;
private ObservableField<String> username = new ObservableField<>();