Add caching, profile editing and call testing feature

master
esensar 2017-01-07 17:19:40 +01:00
parent a9d1051aa8
commit 997b43f2e2
60 changed files with 2160 additions and 115 deletions

6
.idea/vcs.xml 100644
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'realm-android'
android {
compileSdkVersion 24
@ -40,5 +41,6 @@ dependencies {
compile 'io.reactivex:rxjava:1.2.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.android.support:support-v4:24.2.1'
testCompile 'junit:junit:4.12'
}

View File

@ -2,7 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smarthomies.realtimetalk">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.RECORD_AUDIO"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".RTTApp"
@ -15,24 +22,39 @@
android:name=".views.activities.LoginActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"/>
<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:parentActivityName=".views.activities.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".views.activities.ProfileActivity"
android:label="@string/app_name"
android:parentActivityName=".views.activities.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".views.activities.RegistrationActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".views.activities.CallActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".views.activities.SplashScreenActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<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

@ -4,6 +4,8 @@ import android.app.Application;
import com.smarthomies.realtimetalk.utils.RTTAppHelper;
import io.realm.Realm;
/**
* Created by ensar on 31/10/16.
*/
@ -15,5 +17,7 @@ public class RTTApp extends Application {
super.onCreate();
RTTAppHelper.getInstance().initWithContext(getApplicationContext());
Realm.init(this);
}
}

View File

@ -0,0 +1,79 @@
package com.smarthomies.realtimetalk.database;
import java.util.List;
import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmResults;
/**
* Created by ensar on 12/12/16.
*/
public class RealmDAO<T extends RealmObject> {
public static final String TAG = RealmDAO.class.getSimpleName();
protected Class<T> entityClass;
public RealmDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
public List<T> load(Realm realm) {
RealmResults result = realm.where(entityClass).findAll();
return realm.copyFromRealm(result);
}
public void cleanSave(Realm realm, List objects) {
deleteAll(realm);
realm.beginTransaction();
for (Object realmObject : objects) {
realm.copyToRealm((RealmObject) realmObject);
}
realm.commitTransaction();
}
public void save(Realm realm, List objects) {
realm.beginTransaction();
for (Object realmObject : objects) {
realm.copyToRealm((RealmObject) realmObject);
}
realm.commitTransaction();
}
public void updateOrCreate(Realm realm, T object) {
realm.beginTransaction();
realm.copyToRealmOrUpdate(object);
realm.commitTransaction();
}
public void updateOrCreate(Realm realm, List<? extends RealmObject> objects, boolean deleteIfMissing) {
if(objects.isEmpty()) {
return;
}
Class clazz = objects.get(0).getClass();
realm.beginTransaction();
List liveObjects = realm.copyToRealmOrUpdate(objects);
// If true, all objects in DB which are not updated or added will be deleted
if(deleteIfMissing) {
RealmResults allDbObjects = realm.where(entityClass).findAll();
for(int i=0; i<allDbObjects.size(); i++) {
if(!liveObjects.contains(allDbObjects.get(i))) {
allDbObjects.deleteFromRealm(i);
}
}
}
realm.commitTransaction();
}
public void deleteAll(Realm realm) {
realm.beginTransaction();
realm.where(entityClass).findAll().deleteAllFromRealm();
realm.commitTransaction();
}
}

View File

@ -0,0 +1,26 @@
package com.smarthomies.realtimetalk.database;
import com.smarthomies.realtimetalk.models.db.User;
import io.realm.Realm;
/**
* Created by ensar on 12/12/16.
*/
public class UserDAO extends RealmDAO<User> {
public static final String TAG = UserDAO.class.getSimpleName();
public UserDAO() {
super(User.class);
}
public User findUserById(Realm realm, int id) {
return realm.where(entityClass).equalTo(User.PRIMARY_KEY, id).findFirst();
}
public void deleteById(Realm realm, int id) {
realm.beginTransaction();
realm.where(entityClass).equalTo(User.PRIMARY_KEY, id).findFirst().deleteFromRealm();
realm.commitTransaction();
}
}

View File

@ -0,0 +1,103 @@
package com.smarthomies.realtimetalk.managers;
import com.smarthomies.realtimetalk.database.UserDAO;
import com.smarthomies.realtimetalk.models.db.User;
import com.smarthomies.realtimetalk.models.network.PasswordChangeRequest;
import com.smarthomies.realtimetalk.models.network.RegistrationRequest;
import com.smarthomies.realtimetalk.models.network.UserResponse;
import com.smarthomies.realtimetalk.models.network.UsersResponse;
import com.smarthomies.realtimetalk.services.AccountAPIService;
import com.smarthomies.realtimetalk.utils.RTTAppHelper;
import io.realm.Realm;
import rx.Observable;
import rx.functions.Action1;
/**
* Created by ensar on 01/11/16.
*/
public class AccountManager {
public static final String TAG = AccountManager.class.getSimpleName();
public Observable<UserResponse> getProfile() {
return Observable.just(getUserFromDb()).concatWith(AccountAPIService.getInstance().getProfile()
.doOnNext(saveProfile));
}
public Observable<Object> updateProfile(User newProfile) {
return AccountAPIService.getInstance().updateProfile(getUpdateRequest(newProfile))
.doOnNext(new UpdateProfileDb(newProfile));
}
public Observable<Object> changePassword(String oldPassword, String newPassword) {
return AccountAPIService.getInstance().changePassword(getPasswordChangeRequest(oldPassword, newPassword));
}
private PasswordChangeRequest getPasswordChangeRequest(String oldPassword, String newPassword) {
PasswordChangeRequest passwordChangeRequest = new PasswordChangeRequest();
passwordChangeRequest.setCurrentPassword(oldPassword);
passwordChangeRequest.setNewPassword(newPassword);
return passwordChangeRequest;
}
private RegistrationRequest getUpdateRequest(User user) {
RegistrationRequest registrationRequest = new RegistrationRequest();
registrationRequest.setEmail(user.getEmail());
registrationRequest.setFirstName(user.getFirstName());
registrationRequest.setLastName(user.getLastName());
return registrationRequest;
}
private Action1<UserResponse> saveProfile = new Action1<UserResponse>() {
@Override
public void call(UserResponse userResponse) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
new UserDAO().updateOrCreate(realm, userResponse.getData());
} finally {
if(realm != null) {
realm.close();
}
}
}
};
private UserResponse getUserFromDb() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
UserResponse userResponse = new UserResponse();
userResponse.setData(new UserDAO().findUserById(realm, RTTAppHelper.getInstance().getUserId()));
return userResponse;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(realm != null) {
realm.close();
}
}
return null;
}
private class UpdateProfileDb implements Action1<Object> {
private User contact;
public UpdateProfileDb(User contact) {
this.contact = contact;
}
@Override
public void call(Object o) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
new UserDAO().updateOrCreate(realm, contact);
} finally {
if(realm != null) {
realm.close();
}
}
}
}
}

View File

@ -8,6 +8,7 @@ import com.smarthomies.realtimetalk.services.AuthenticationAPIService;
import com.smarthomies.realtimetalk.utils.RTTAppHelper;
import rx.Observable;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
@ -18,7 +19,8 @@ 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);
return AuthenticationAPIService.getInstance().login(getLoginRequest(username, password))
.doOnNext(processAuthenticationResponse);
}
public Observable<AuthenticationResponse> registerUser(User user, final String username, final String password) {
@ -30,7 +32,14 @@ public class AuthenticationManager {
return Observable.just(getLoginRequest(username, password));
}
})
.flatMap(requestLogin).doOnNext(processAuthenticationResponse);
.flatMap(requestLogin)
.doOnNext(processAuthenticationResponse);
}
public Observable<Object> logout() {
return AuthenticationAPIService.getInstance().logout()
.doOnNext(processLogoutResponse)
.doOnError(processLogoutFailure);
}
private Func1<LoginRequest, Observable<AuthenticationResponse>> requestLogin = new Func1<LoginRequest, Observable<AuthenticationResponse>>() {
@ -44,9 +53,29 @@ public class AuthenticationManager {
@Override
public void call(AuthenticationResponse authenticationResponse) {
RTTAppHelper.getInstance().saveToken(authenticationResponse.getToken());
RTTAppHelper.getInstance().saveUserId(authenticationResponse.getId());
}
};
private Action1<Object> processLogoutResponse = new Action1<Object>() {
@Override
public void call(Object object) {
clearToken();
}
};
private Action1<Throwable> processLogoutFailure = new Action1<Throwable>() {
@Override
public void call(Throwable object) {
clearToken();
}
};
private void clearToken() {
RTTAppHelper.getInstance().saveToken("");
RTTAppHelper.getInstance().saveUserId(-1);
}
private RegistrationRequest getRegistrationRequest(User user, String username, String password) {
RegistrationRequest registrationRequest = new RegistrationRequest();
registrationRequest.setUsername(username);

View File

@ -1,10 +1,19 @@
package com.smarthomies.realtimetalk.managers;
import android.util.Log;
import com.smarthomies.realtimetalk.database.UserDAO;
import com.smarthomies.realtimetalk.models.db.User;
import com.smarthomies.realtimetalk.models.network.ContactRequest;
import com.smarthomies.realtimetalk.models.network.SearchRequest;
import com.smarthomies.realtimetalk.models.network.UsersResponse;
import com.smarthomies.realtimetalk.services.ContactsAPIService;
import java.util.List;
import io.realm.Realm;
import rx.Observable;
import rx.functions.Action1;
/**
* Created by ensar on 01/11/16.
@ -16,9 +25,91 @@ public class ContactsManager {
return ContactsAPIService.getInstance().search(getSearchRequest(searchString));
}
public Observable<UsersResponse> getContacts() {
return Observable.just(getUsersFromDb()).concatWith(ContactsAPIService.getInstance().getContacts()
.doOnNext(saveContacts));
}
public Observable<Object> saveContact(User contact) {
return ContactsAPIService.getInstance().saveContact(getContactRequest(contact))
.doOnNext(new UpdateContactDb(contact, true));
}
public Observable<Object> deleteContact(User contact) {
return ContactsAPIService.getInstance().deleteContact(getContactRequest(contact))
.doOnNext(new UpdateContactDb(contact, false));
}
private SearchRequest getSearchRequest(String searchString) {
SearchRequest searchRequest = new SearchRequest();
searchRequest.setSearch(searchString);
return searchRequest;
}
private ContactRequest getContactRequest(User user) {
ContactRequest contactRequest = new ContactRequest();
contactRequest.setUsername(user.getUsername());
return contactRequest;
}
private UsersResponse getUsersFromDb() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
UsersResponse usersResponse = new UsersResponse();
usersResponse.setData(new UserDAO().load(realm));
return usersResponse;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(realm != null) {
realm.close();
}
}
return null;
}
private Action1<UsersResponse> saveContacts = new Action1<UsersResponse>() {
@Override
public void call(UsersResponse usersResponse) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
new UserDAO().updateOrCreate(realm, usersResponse.getData(), true);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(realm != null) {
realm.close();
}
}
}
};
private class UpdateContactDb implements Action1<Object> {
private User contact;
private boolean add;
public UpdateContactDb(User contact, boolean add) {
this.contact = contact;
this.add = add;
}
@Override
public void call(Object o) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
if(add) {
new UserDAO().updateOrCreate(realm, contact);
} else {
new UserDAO().deleteById(realm, contact.getId());
}
} finally {
if(realm != null) {
realm.close();
}
}
}
}
}

View File

@ -2,12 +2,19 @@ package com.smarthomies.realtimetalk.models.db;
import com.google.gson.annotations.SerializedName;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* Created by ensar on 01/11/16.
*/
public class User{
public class User extends RealmObject{
public static final String TAG = User.class.getSimpleName();
public static final String PRIMARY_KEY = "username";
@PrimaryKey
private int id;
@SerializedName("first_name")
private String firstName;
@SerializedName("last_name")
@ -16,6 +23,15 @@ public class User{
private String email;
@SerializedName("profile_picture")
private String imageUrl;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
@ -48,4 +64,12 @@ public class User{
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@ -7,6 +7,7 @@ public class AuthenticationResponse {
public static final String TAG = AuthenticationResponse.class.getSimpleName();
private String token;
private int id;
public String getToken() {
return token;
@ -15,4 +16,12 @@ public class AuthenticationResponse {
public void setToken(String token) {
this.token = token;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

View File

@ -0,0 +1,18 @@
package com.smarthomies.realtimetalk.models.network;
/**
* Created by ensar on 11/12/16.
*/
public class ContactRequest {
public static final String TAG = ContactRequest.class.getSimpleName();
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@ -0,0 +1,31 @@
package com.smarthomies.realtimetalk.models.network;
import com.google.gson.annotations.SerializedName;
/**
* Created by ensar on 31/10/16.
*/
public class PasswordChangeRequest {
public static final String TAG = PasswordChangeRequest.class.getSimpleName();
@SerializedName("current_password")
private String currentPassword;
@SerializedName("new_password")
private String newPassword;
public String getCurrentPassword() {
return currentPassword;
}
public void setCurrentPassword(String currentPassword) {
this.currentPassword = currentPassword;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
}

View File

@ -1,5 +1,7 @@
package com.smarthomies.realtimetalk.models.network;
import com.google.gson.annotations.SerializedName;
/**
* Created by ensar on 31/10/16.
*/
@ -9,7 +11,9 @@ public class RegistrationRequest {
private String username;
private String password;
private String email;
@SerializedName("first_name")
private String firstName;
@SerializedName("last_name")
private String lastName;
public String getUsername() {

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 UserResponse {
public static final String TAG = UserResponse.class.getSimpleName();
private User data;
public User getData() {
return data;
}
public void setData(User data) {
this.data = data;
}
}

View File

@ -1,10 +1,12 @@
package com.smarthomies.realtimetalk.network;
import com.smarthomies.realtimetalk.network.exceptions.APIConflictException;
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.ServerErrorException;
import com.smarthomies.realtimetalk.network.exceptions.UnauthorizedException;
import java.io.IOException;
@ -31,25 +33,16 @@ public class APIErrorHandler {
throw new ResourceForbiddenException(throwable);
case HttpURLConnection.HTTP_UNAUTHORIZED:
throw new UnauthorizedException(throwable);
case HttpURLConnection.HTTP_CONFLICT:
throw new APIConflictException(throwable);
case HttpURLConnection.HTTP_UNAVAILABLE:
case HttpURLConnection.HTTP_INTERNAL_ERROR:
throw new ServerErrorException(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

@ -1,20 +0,0 @@
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

@ -7,8 +7,14 @@ 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_LOGOUT_ENDPOINT = "odjava";
public static final String API_REGISTRATION_ENDPOINT = "dodaj";
public static final String API_CONTACTS_ENDPOINT = "contacts";
public static final String API_PROFILE_ENDPOINT = "profile";
public static final String API_PASSWORD_UPDATE_ENDPOINT = API_PROFILE_ENDPOINT + "/password";
public static final String API_SEARCH_ENDPOINT = "search";
public static final String AUTHORIZATION_HEADER = "Authorization";
}

View File

@ -0,0 +1,32 @@
package com.smarthomies.realtimetalk.network.apis;
import com.smarthomies.realtimetalk.models.network.ContactRequest;
import com.smarthomies.realtimetalk.models.network.PasswordChangeRequest;
import com.smarthomies.realtimetalk.models.network.RegistrationRequest;
import com.smarthomies.realtimetalk.models.network.SearchRequest;
import com.smarthomies.realtimetalk.models.network.UserResponse;
import com.smarthomies.realtimetalk.models.network.UsersResponse;
import com.smarthomies.realtimetalk.network.NetworkingConstants;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import rx.Observable;
/**
* Created by ensar on 15/11/16.
*/
public interface AccountAPI {
@GET(NetworkingConstants.API_PROFILE_ENDPOINT)
Observable<UserResponse> getProfile();
@PUT(NetworkingConstants.API_PROFILE_ENDPOINT)
Observable<Object> updateProfile(@Body RegistrationRequest request);
@PUT(NetworkingConstants.API_PASSWORD_UPDATE_ENDPOINT)
Observable<Object> changePassword(@Body PasswordChangeRequest request);
}

View File

@ -1,12 +1,13 @@
package com.smarthomies.realtimetalk.network;
package com.smarthomies.realtimetalk.network.apis;
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.NetworkingConstants;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import rx.Observable;
/**
@ -18,7 +19,10 @@ public interface AuthenticationAPI {
@POST(NetworkingConstants.API_LOGIN_ENDPOINT)
Observable<AuthenticationResponse> login(@Body LoginRequest request);
@PUT(NetworkingConstants.API_REGISTRATION_ENDPOINT)
@POST(NetworkingConstants.API_REGISTRATION_ENDPOINT)
Observable<AuthenticationResponse> register(@Body RegistrationRequest request);
@DELETE(NetworkingConstants.API_LOGOUT_ENDPOINT)
Observable<Object> logout();
}

View File

@ -0,0 +1,32 @@
package com.smarthomies.realtimetalk.network.apis;
import com.smarthomies.realtimetalk.models.network.ContactRequest;
import com.smarthomies.realtimetalk.models.network.SearchRequest;
import com.smarthomies.realtimetalk.models.network.UsersResponse;
import com.smarthomies.realtimetalk.network.NetworkingConstants;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
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);
@GET(NetworkingConstants.API_CONTACTS_ENDPOINT)
Observable<UsersResponse> getContacts();
@POST(NetworkingConstants.API_CONTACTS_ENDPOINT)
Observable<Object> saveContact(@Body ContactRequest contactRequest);
@DELETE(NetworkingConstants.API_CONTACTS_ENDPOINT)
Observable<Object> deleteContact(@Body ContactRequest contactRequest);
}

View File

@ -1,4 +1,10 @@
package com.smarthomies.realtimetalk.network;
package com.smarthomies.realtimetalk.network.clients;
import com.smarthomies.realtimetalk.network.NetworkingConstants;
import com.smarthomies.realtimetalk.network.apis.AccountAPI;
import com.smarthomies.realtimetalk.network.apis.AuthenticationAPI;
import com.smarthomies.realtimetalk.network.apis.ContactsAPI;
import com.smarthomies.realtimetalk.network.interceptors.AuthorizationInterceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
@ -17,6 +23,7 @@ public class RestClient {
private Retrofit retrofit;
private AuthenticationAPI authenticationAPI;
private ContactsAPI contactsAPI;
private AccountAPI accountAPI;
private RestClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
@ -25,6 +32,7 @@ public class RestClient {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// add your other interceptors …
httpClient.addInterceptor(new AuthorizationInterceptor());
// add logging as last interceptor
httpClient.addInterceptor(logging); // <-- this is the important line!
@ -57,4 +65,11 @@ public class RestClient {
}
return contactsAPI;
}
public AccountAPI getAccountAPI() {
if(accountAPI == null) {
accountAPI = retrofit.create(AccountAPI.class);
}
return accountAPI;
}
}

View File

@ -0,0 +1,15 @@
package com.smarthomies.realtimetalk.network.exceptions;
/**
* Created by ensar on 01/11/16.
*/
public class APIConflictException extends APIException {
public static final String TAG = APIConflictException.class.getSimpleName();
public APIConflictException() {
}
public APIConflictException(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 ServerErrorException extends APIException {
public static final String TAG = ServerErrorException.class.getSimpleName();
public ServerErrorException() {
}
public ServerErrorException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,26 @@
package com.smarthomies.realtimetalk.network.interceptors;
import com.smarthomies.realtimetalk.network.NetworkingConstants;
import com.smarthomies.realtimetalk.utils.RTTAppHelper;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* Created by ensar on 11/12/16.
*/
public class AuthorizationInterceptor implements Interceptor {
public static final String TAG = AuthorizationInterceptor.class.getSimpleName();
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain
.request()
.newBuilder()
.addHeader(NetworkingConstants.AUTHORIZATION_HEADER, RTTAppHelper.getInstance().getToken()).build();
return chain.proceed(request);
}
}

View File

@ -0,0 +1,56 @@
package com.smarthomies.realtimetalk.services;
import com.smarthomies.realtimetalk.models.network.PasswordChangeRequest;
import com.smarthomies.realtimetalk.models.network.RegistrationRequest;
import com.smarthomies.realtimetalk.models.network.UserResponse;
import com.smarthomies.realtimetalk.network.APIErrorHandler;
import com.smarthomies.realtimetalk.network.clients.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 AccountAPIService {
public static final String TAG = AccountAPIService.class.getSimpleName();
private static AccountAPIService instance;
private AccountAPIService() {}
public static AccountAPIService getInstance() {
if(instance == null) {
instance = new AccountAPIService();
}
return instance;
}
public Observable<UserResponse> getProfile() {
return RestClient.getInstance().getAccountAPI().getProfile()
.doOnError(handleGeneralErrors);
}
public Observable<Object> updateProfile(RegistrationRequest request) {
return RestClient.getInstance().getAccountAPI().updateProfile(request)
.doOnError(handleGeneralErrors);
}
public Observable<Object> changePassword(PasswordChangeRequest request) {
return RestClient.getInstance().getAccountAPI().changePassword(request)
.doOnError(handleGeneralErrors);
}
private Action1<Throwable> handleGeneralErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleGeneralAPIErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}
}
};
}

View File

@ -4,7 +4,7 @@ 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.clients.RestClient;
import com.smarthomies.realtimetalk.network.exceptions.APIException;
import rx.Observable;
@ -30,30 +30,24 @@ public class AuthenticationAPIService {
public Observable<AuthenticationResponse> login(LoginRequest request) {
return RestClient.getInstance().getAuthenticationAPI().login(request)
.doOnError(handleLoginErrors);
.doOnError(handleGeneralErrors);
}
public Observable<AuthenticationResponse> register(RegistrationRequest request) {
return RestClient.getInstance().getAuthenticationAPI().register(request)
.doOnError(handleRegistrationErrors);
.doOnError(handleGeneralErrors);
}
private Action1<Throwable> handleLoginErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleLoginErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}
}
};
public Observable<Object> logout() {
return RestClient.getInstance().getAuthenticationAPI().logout()
.doOnError(handleGeneralErrors);
}
private Action1<Throwable> handleRegistrationErrors = new Action1<Throwable>() {
private Action1<Throwable> handleGeneralErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleRegistrationErrors(throwable);
APIErrorHandler.handleGeneralAPIErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}

View File

@ -1,9 +1,10 @@
package com.smarthomies.realtimetalk.services;
import com.smarthomies.realtimetalk.models.network.ContactRequest;
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.clients.RestClient;
import com.smarthomies.realtimetalk.network.exceptions.APIException;
import rx.Observable;
@ -29,14 +30,29 @@ public class ContactsAPIService {
public Observable<UsersResponse> search(SearchRequest request) {
return RestClient.getInstance().getContactsAPI().searchUsers(request)
.doOnError(handleSearchErrors);
.doOnError(handleApiErrors);
}
private Action1<Throwable> handleSearchErrors = new Action1<Throwable>() {
public Observable<UsersResponse> getContacts() {
return RestClient.getInstance().getContactsAPI().getContacts()
.doOnError(handleApiErrors);
}
public Observable<Object> saveContact(ContactRequest request) {
return RestClient.getInstance().getContactsAPI().saveContact(request)
.doOnError(handleApiErrors);
}
public Observable<Object> deleteContact(ContactRequest request) {
return RestClient.getInstance().getContactsAPI().deleteContact(request)
.doOnError(handleApiErrors);
}
private Action1<Throwable> handleApiErrors = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
try {
APIErrorHandler.handleSearchErrors(throwable);
APIErrorHandler.handleGeneralAPIErrors(throwable);
} catch (APIException apiException) {
throw Exceptions.propagate(apiException);
}

View File

@ -0,0 +1,94 @@
package com.smarthomies.realtimetalk.utils;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.views.activities.CallActivity;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Properties;
public class MediaStreamClient {
boolean isRecording;
int recBufSize;
//ServerSocket sockfd;
Socket connfd;
AudioRecord audioRecord;
private static final String TAG = "MyActivity";
public MediaStreamClient(final Context ctx, final String ip) {
Properties prop = new Properties();
try {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
System.out.println("Can't finde config");
} catch (IOException e) {
System.out.println("Can't load config");
}
final int frequency = Integer.parseInt(prop.getProperty("frequency"));
final int channelConfiguration = Integer.parseInt(prop.getProperty("channal_server"));
final int audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
final int SERVERPORT = Integer.parseInt(prop.getProperty("serverport_server"));
recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
//Log.v(TAG,String.valueOf(AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO , AudioFormat.ENCODING_PCM_16BIT)));
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, recBufSize);
new Thread() {
byte[] buffer = new byte[recBufSize];
public void run() {
try { connfd = new Socket(ip, SERVERPORT); }
catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Can't connect!",ctx);
return;
}
audioRecord.startRecording();
isRecording = true;
while (isRecording) {
int readSize = audioRecord.read(buffer, 0, recBufSize);
try { connfd.getOutputStream().write(buffer, 0, readSize);
}
catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Closed stream by revicer!",ctx);
break;
}
}
audioRecord.stop();
//audioRecord.release();
try { connfd.close(); }
catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Can't close connection!",ctx);
}
}
}.start();
}
public void stop(Context ctx) {
isRecording = false;
/*try { connfd.close(); }
catch (Exception e) {
e.printStackTrace();
MainActivity.toast("Can't close connection!",ctx);
}*/
}
}

View File

@ -0,0 +1,108 @@
package com.smarthomies.realtimetalk.utils;
import android.content.Context;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.views.activities.CallActivity;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
public class MediaStreamServer implements Runnable {
boolean isPlaying;
int playBufSize;
Socket connfd;
ServerSocket sockfd;
AudioTrack audioTrack;
private static final String TAG = "MyActivity";
final int SERVERPORT;
Context ctx;
public MediaStreamServer(final Context ctx) {
Properties prop = new Properties();
try {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
System.out.println("Can't finde config");
} catch (IOException e) {
System.out.println("Can't load config");
}
final int frequency = Integer.parseInt(prop.getProperty("frequency"));
final int channelConfiguration = Integer.parseInt(prop.getProperty("channal_client"));
final int audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
SERVERPORT = Integer.parseInt(prop.getProperty("serverport_client"));
playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);
audioTrack.setStereoVolume(1f, 1f);
this.ctx=ctx;
}
public void stop() {
isPlaying = false;
}
public void setVolume(float lvol, float rvol) {
audioTrack.setStereoVolume(lvol, rvol);
}
@Override
public void run() {
byte[] buffer = new byte[playBufSize];
try { sockfd = new ServerSocket(SERVERPORT); }
catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Port unavailable",ctx);
return;
}
while(true) {
try {
connfd = sockfd.accept();
} catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Connection not accepted",ctx);
continue;
}
CallActivity.toast("Connected",ctx);
audioTrack.play();
isPlaying = true;
while (isPlaying) {
int readSize = 0;
try {
readSize = connfd.getInputStream().read(buffer);
} catch (Exception e) {
Log.v(TAG, "palo");
e.printStackTrace();
CallActivity.toast("Closed stream by sender",ctx);
break;
}
audioTrack.write(buffer, 0, readSize);
}
audioTrack.stop();
audioTrack.flush();
try {
connfd.close();
} catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Can't close connection!",ctx);
}
}
}
}

View File

@ -10,6 +10,7 @@ public class RTTAppHelper {
public static final String TAG = RTTAppHelper.class.getSimpleName();
public static final String SHARED_PREFERENCES_USER_TOKEN = "SHARED_PREFERENCES_USER_TOKEN";
public static final String SHARED_PREFERENCES_USER_ID = "SHARED_PREFERENCES_USER_ID";
private static RTTAppHelper instance;
private Context context;
@ -34,7 +35,21 @@ public class RTTAppHelper {
}
public String getToken() {
return readFromSharedPrefs(SHARED_PREFERENCES_USER_TOKEN);
return readFromSharedPrefs(SHARED_PREFERENCES_USER_TOKEN, "");
}
public void saveUserId(int userId) {
writeToSharedPrefs(SHARED_PREFERENCES_USER_ID, userId);
}
public int getUserId() {
return readFromSharedPrefs(SHARED_PREFERENCES_USER_ID, -1);
}
private void writeToSharedPrefs(String key, int value) {
SharedPreferences.Editor editor = context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit();
editor.putInt(key, value);
editor.apply();
}
private void writeToSharedPrefs(String key, String value) {
@ -52,4 +67,9 @@ public class RTTAppHelper {
return sharedPreferences.getString(key, defaultValue);
}
private int readFromSharedPrefs(String key, int defaultValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
return sharedPreferences.getInt(key, defaultValue);
}
}

View File

@ -4,10 +4,12 @@ import android.util.Log;
import android.widget.Toast;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.network.exceptions.APIConflictException;
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.ServerErrorException;
import com.smarthomies.realtimetalk.network.exceptions.UnauthorizedException;
/**
@ -25,8 +27,12 @@ public class RTTErrorUtil {
return R.string.error_unknown;
} else if (e instanceof UnauthorizedException) {
return R.string.error_user_bad_credentials;