diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c8749e3..ea122c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 522444a..14b6eae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,14 @@ - + + + + + + android:windowSoftInputMode="adjustPan"/> + + + + + + + - - - \ No newline at end of file diff --git a/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java b/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java index 5cebd71..f17270c 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/RTTApp.java @@ -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); } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/database/RealmDAO.java b/app/src/main/java/com/smarthomies/realtimetalk/database/RealmDAO.java new file mode 100644 index 0000000..c575e0a --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/database/RealmDAO.java @@ -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 { + public static final String TAG = RealmDAO.class.getSimpleName(); + + protected Class entityClass; + + public RealmDAO(Class entityClass) { + this.entityClass = entityClass; + } + + public List 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 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 { + 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(); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/managers/AccountManager.java b/app/src/main/java/com/smarthomies/realtimetalk/managers/AccountManager.java new file mode 100644 index 0000000..26cf834 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/managers/AccountManager.java @@ -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 getProfile() { + return Observable.just(getUserFromDb()).concatWith(AccountAPIService.getInstance().getProfile() + .doOnNext(saveProfile)); + } + + public Observable updateProfile(User newProfile) { + return AccountAPIService.getInstance().updateProfile(getUpdateRequest(newProfile)) + .doOnNext(new UpdateProfileDb(newProfile)); + } + + public Observable 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 saveProfile = new Action1() { + @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 { + 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(); + } + } + } + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java b/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java index cd3fc8c..6815540 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/managers/AuthenticationManager.java @@ -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 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 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 logout() { + return AuthenticationAPIService.getInstance().logout() + .doOnNext(processLogoutResponse) + .doOnError(processLogoutFailure); } private Func1> requestLogin = new Func1>() { @@ -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 processLogoutResponse = new Action1() { + @Override + public void call(Object object) { + clearToken(); + } + }; + + private Action1 processLogoutFailure = new Action1() { + @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); diff --git a/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java b/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java index 8dad977..a8ee0d1 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/managers/ContactsManager.java @@ -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 getContacts() { + return Observable.just(getUsersFromDb()).concatWith(ContactsAPIService.getInstance().getContacts() + .doOnNext(saveContacts)); + } + + public Observable saveContact(User contact) { + return ContactsAPIService.getInstance().saveContact(getContactRequest(contact)) + .doOnNext(new UpdateContactDb(contact, true)); + } + + public Observable 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 saveContacts = new Action1() { + @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 { + 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(); + } + } + } + } } 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 index 247db3f..772e001 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/db/User.java @@ -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; + } } 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 index 8c0f307..5bfb1c8 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/AuthenticationResponse.java @@ -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; + } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/ContactRequest.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/ContactRequest.java new file mode 100644 index 0000000..6264b4b --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/ContactRequest.java @@ -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; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/PasswordChangeRequest.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/PasswordChangeRequest.java new file mode 100644 index 0000000..11f01be --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/PasswordChangeRequest.java @@ -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; + } +} 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 index b19e0b8..e4510b4 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/RegistrationRequest.java @@ -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() { diff --git a/app/src/main/java/com/smarthomies/realtimetalk/models/network/UserResponse.java b/app/src/main/java/com/smarthomies/realtimetalk/models/network/UserResponse.java new file mode 100644 index 0000000..8c05c44 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/models/network/UserResponse.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 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; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java b/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java index cd4b4a1..c1e0ff6 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/APIErrorHandler.java @@ -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); - } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java deleted file mode 100644 index 8442cd3..0000000 --- a/app/src/main/java/com/smarthomies/realtimetalk/network/ContactsAPI.java +++ /dev/null @@ -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 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 index b391909..fbb64e6 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/NetworkingConstants.java @@ -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"; } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/apis/AccountAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/AccountAPI.java new file mode 100644 index 0000000..ea18c97 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/AccountAPI.java @@ -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 getProfile(); + + @PUT(NetworkingConstants.API_PROFILE_ENDPOINT) + Observable updateProfile(@Body RegistrationRequest request); + + @PUT(NetworkingConstants.API_PASSWORD_UPDATE_ENDPOINT) + Observable changePassword(@Body PasswordChangeRequest request); +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/AuthenticationAPI.java similarity index 66% rename from app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java rename to app/src/main/java/com/smarthomies/realtimetalk/network/apis/AuthenticationAPI.java index 92fed48..18657f3 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/network/AuthenticationAPI.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/AuthenticationAPI.java @@ -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 login(@Body LoginRequest request); - @PUT(NetworkingConstants.API_REGISTRATION_ENDPOINT) + @POST(NetworkingConstants.API_REGISTRATION_ENDPOINT) Observable register(@Body RegistrationRequest request); + @DELETE(NetworkingConstants.API_LOGOUT_ENDPOINT) + Observable logout(); + } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/apis/ContactsAPI.java b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/ContactsAPI.java new file mode 100644 index 0000000..f4a13b7 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/apis/ContactsAPI.java @@ -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 searchUsers(@Body SearchRequest searchRequest); + + @GET(NetworkingConstants.API_CONTACTS_ENDPOINT) + Observable getContacts(); + + @POST(NetworkingConstants.API_CONTACTS_ENDPOINT) + Observable saveContact(@Body ContactRequest contactRequest); + + @DELETE(NetworkingConstants.API_CONTACTS_ENDPOINT) + Observable deleteContact(@Body ContactRequest contactRequest); + +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java b/app/src/main/java/com/smarthomies/realtimetalk/network/clients/RestClient.java similarity index 73% rename from app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java rename to app/src/main/java/com/smarthomies/realtimetalk/network/clients/RestClient.java index e4f12e2..dd30d65 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/network/RestClient.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/clients/RestClient.java @@ -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; + } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIConflictException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIConflictException.java new file mode 100644 index 0000000..51d8594 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/APIConflictException.java @@ -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); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ServerErrorException.java b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ServerErrorException.java new file mode 100644 index 0000000..74bc290 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/exceptions/ServerErrorException.java @@ -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); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/network/interceptors/AuthorizationInterceptor.java b/app/src/main/java/com/smarthomies/realtimetalk/network/interceptors/AuthorizationInterceptor.java new file mode 100644 index 0000000..ed486a3 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/network/interceptors/AuthorizationInterceptor.java @@ -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); + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/services/AccountAPIService.java b/app/src/main/java/com/smarthomies/realtimetalk/services/AccountAPIService.java new file mode 100644 index 0000000..17e59cc --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/services/AccountAPIService.java @@ -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 getProfile() { + return RestClient.getInstance().getAccountAPI().getProfile() + .doOnError(handleGeneralErrors); + } + + public Observable updateProfile(RegistrationRequest request) { + return RestClient.getInstance().getAccountAPI().updateProfile(request) + .doOnError(handleGeneralErrors); + } + + public Observable changePassword(PasswordChangeRequest request) { + return RestClient.getInstance().getAccountAPI().changePassword(request) + .doOnError(handleGeneralErrors); + } + + private Action1 handleGeneralErrors = new Action1() { + @Override + public void call(Throwable throwable) { + try { + APIErrorHandler.handleGeneralAPIErrors(throwable); + } catch (APIException apiException) { + throw Exceptions.propagate(apiException); + } + } + }; +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java b/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java index 49949c2..7b76ec9 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/services/AuthenticationAPIService.java @@ -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 login(LoginRequest request) { return RestClient.getInstance().getAuthenticationAPI().login(request) - .doOnError(handleLoginErrors); + .doOnError(handleGeneralErrors); } public Observable register(RegistrationRequest request) { return RestClient.getInstance().getAuthenticationAPI().register(request) - .doOnError(handleRegistrationErrors); + .doOnError(handleGeneralErrors); } - private Action1 handleLoginErrors = new Action1() { - @Override - public void call(Throwable throwable) { - try { - APIErrorHandler.handleLoginErrors(throwable); - } catch (APIException apiException) { - throw Exceptions.propagate(apiException); - } - } - }; + public Observable logout() { + return RestClient.getInstance().getAuthenticationAPI().logout() + .doOnError(handleGeneralErrors); + } - private Action1 handleRegistrationErrors = new Action1() { + private Action1 handleGeneralErrors = new Action1() { @Override public void call(Throwable throwable) { try { - APIErrorHandler.handleRegistrationErrors(throwable); + APIErrorHandler.handleGeneralAPIErrors(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 index 45c2ef9..94e2b77 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/services/ContactsAPIService.java @@ -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 search(SearchRequest request) { return RestClient.getInstance().getContactsAPI().searchUsers(request) - .doOnError(handleSearchErrors); + .doOnError(handleApiErrors); } - private Action1 handleSearchErrors = new Action1() { + public Observable getContacts() { + return RestClient.getInstance().getContactsAPI().getContacts() + .doOnError(handleApiErrors); + } + + public Observable saveContact(ContactRequest request) { + return RestClient.getInstance().getContactsAPI().saveContact(request) + .doOnError(handleApiErrors); + } + + public Observable deleteContact(ContactRequest request) { + return RestClient.getInstance().getContactsAPI().deleteContact(request) + .doOnError(handleApiErrors); + } + + private Action1 handleApiErrors = new Action1() { @Override public void call(Throwable throwable) { try { - APIErrorHandler.handleSearchErrors(throwable); + APIErrorHandler.handleGeneralAPIErrors(throwable); } catch (APIException apiException) { throw Exceptions.propagate(apiException); } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamClient.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamClient.java new file mode 100644 index 0000000..276c723 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamClient.java @@ -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); + }*/ + } +} \ No newline at end of file diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamServer.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamServer.java new file mode 100644 index 0000000..7a439fe --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/MediaStreamServer.java @@ -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); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java index 0b7c161..5def4aa 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTAppHelper.java @@ -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); + } + } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java index ff43f8e..bdf9952 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/utils/RTTErrorUtil.java @@ -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; + } else if (e instanceof APIConflictException) { + return R.string.error_user_already_exists; } else if (e instanceof NetworkException) { return R.string.error_no_internet; + } else if (e instanceof ServerErrorException) { + return R.string.error_server; } return 0; } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java index 0a7974b..eab27eb 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/LoginViewModel.java @@ -71,10 +71,9 @@ public class LoginViewModel extends BaseObservable { return new View.OnClickListener() { @Override public void onClick(View v) { -// if(validateFields()) { -// loginUser(); -// } - onLoginDone(); + if(validateFields()) { + loginUser(); + } } }; } @@ -90,6 +89,7 @@ public class LoginViewModel extends BaseObservable { public void onLoginDone() { NavigationSubject.getInstance().onNext(new Pair, Bundle>(MainActivity.class, null)); + NavigationSubject.getInstance().onNext(null); } public void onRequestCompleted() { diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java index a947573..4e4efed 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/MainViewModel.java @@ -2,35 +2,124 @@ package com.smarthomies.realtimetalk.viewmodels; import android.databinding.BaseObservable; import android.databinding.Bindable; +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.AccountManager; +import com.smarthomies.realtimetalk.managers.AuthenticationManager; +import com.smarthomies.realtimetalk.managers.ContactsManager; import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.models.network.UserResponse; +import com.smarthomies.realtimetalk.models.network.UsersResponse; import com.smarthomies.realtimetalk.utils.NavigationSubject; +import com.smarthomies.realtimetalk.views.activities.LoginActivity; import com.smarthomies.realtimetalk.views.activities.MainActivity; +import com.smarthomies.realtimetalk.views.activities.ProfileActivity; import com.smarthomies.realtimetalk.views.activities.SearchActivity; +import java.util.List; + +import rx.Observable; +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; +import rx.subjects.AsyncSubject; +import rx.subscriptions.CompositeSubscription; + /** * Created by ensar on 15/11/16. */ public class MainViewModel extends BaseObservable { public static final String TAG = MainViewModel.class.getSimpleName(); - UserViewModel userViewModel; + private AsyncSubject logoutSubject; - public void setUser(User user) { - userViewModel = new UserViewModel(user); + private CompositeSubscription subscription; + + private ObservableField> contacts = new ObservableField<>(); + + private ObservableField userViewModel = new ObservableField<>(); + + public MainViewModel() { + logoutSubject = AsyncSubject.create(); + subscription = new CompositeSubscription(); + userViewModel.set(new UserViewModel()); + } + + public void loadContacts() { + subscription.add(new ContactsManager().getContacts() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onNext(UsersResponse usersResponse) { + contacts.set(usersResponse.getData()); + } + })); + } + + public void loadProfile() { + subscription.add(new AccountManager().getProfile() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onNext(UserResponse userResponse) { + userViewModel.get().setModel(userResponse.getData()); + userViewModel.notifyChange(); + } + })); + } + + public AsyncSubject createLogoutSubject() { + logoutSubject = AsyncSubject.create(); + return logoutSubject; + } + + public AsyncSubject getLogoutSubject() { + return logoutSubject; + } + + private void logoutUser() { + new AuthenticationManager().logout().subscribeOn(Schedulers.io()).subscribe(logoutSubject); + } + + public ObservableField> getContacts() { + return contacts; + } + + public void setContacts(ObservableField> contacts) { + this.contacts = contacts; } @Bindable public UserViewModel getUserViewModel() { - return userViewModel; - } - - public void setUserViewModel(UserViewModel userViewModel) { - this.userViewModel = userViewModel; + return userViewModel.get(); } public View.OnClickListener onSearchClick() { @@ -41,4 +130,22 @@ public class MainViewModel extends BaseObservable { } }; } + + public void onLogoutClick() { + logoutUser(); + } + + public void onProfileClick() { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(ProfileActivity.class, null)); + } + + public void onLogoutDone() { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(LoginActivity.class, null)); + } + + public void clear() { + if(subscription != null && !subscription.isUnsubscribed()) { + subscription.unsubscribe(); + } + } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/ProfileViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/ProfileViewModel.java new file mode 100644 index 0000000..aa7d198 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/ProfileViewModel.java @@ -0,0 +1,282 @@ +package com.smarthomies.realtimetalk.viewmodels; + +import android.databinding.BaseObservable; +import android.databinding.ObservableBoolean; +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.AccountManager; +import com.smarthomies.realtimetalk.managers.AuthenticationManager; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.models.network.UserResponse; +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.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; +import rx.subjects.AsyncSubject; +import rx.subscriptions.CompositeSubscription; + +/** + * Created by ensar on 01/11/16. + */ +public class ProfileViewModel extends BaseObservable { + public static final String TAG = ProfileViewModel.class.getSimpleName(); + + private ObservableField firstName = new ObservableField<>(); + private ObservableField lastName = new ObservableField<>(); + private ObservableField firstNameError = new ObservableField<>(); + private ObservableField lastNameError = new ObservableField<>(); + private ObservableField oldPassword = new ObservableField<>(); + private ObservableField password = new ObservableField<>(); + private ObservableField passwordConfirmation = new ObservableField<>(); + private ObservableField oldPasswordError = new ObservableField<>(); + private ObservableField passwordError = new ObservableField<>(); + private ObservableField passwordConfirmationError = new ObservableField<>(); + private ObservableField email = new ObservableField<>(); + private ObservableField emailError = new ObservableField<>(); + private ObservableBoolean passwordsVisibility = new ObservableBoolean(); + + private CompositeSubscription subscription; + + public ProfileViewModel() { + subscription = new CompositeSubscription(); + } + + private boolean validateNames() { + clearNamesErrors(); + + firstNameError.set(RTTUtil.getRequiredFieldError(firstName.get())); + lastNameError.set(RTTUtil.getRequiredFieldError(lastName.get())); + emailError.set(RTTUtil.getRequiredFieldError(email.get())); + + return firstNameError.get() == 0 && lastNameError.get() == 0 && emailError.get() == 0; + } + + private boolean validatePassword() { + clearPasswordErrors(); + + oldPasswordError.set(RTTUtil.getPasswordError(oldPassword.get())); + passwordError.set(RTTUtil.getPasswordError(password.get())); + passwordConfirmationError.set(RTTUtil.getPasswordConfirmationError(password.get(), passwordConfirmation.get())); + + return passwordError.get() == 0 && passwordConfirmationError.get() == 0 && oldPasswordError.get() == 0; + } + + private void clearNamesErrors() { + firstNameError.set(0); + lastNameError.set(0); + emailError.set(0); + } + + private void clearPasswordErrors() { + oldPasswordError.set(0); + passwordError.set(0); + passwordConfirmationError.set(0); + } + + 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 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 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 ObservableBoolean getPasswordsVisibility() { + return passwordsVisibility; + } + + public void setPasswordsVisibility(ObservableBoolean passwordsVisibility) { + this.passwordsVisibility = passwordsVisibility; + } + + public ObservableField getOldPassword() { + return oldPassword; + } + + public void setOldPassword(ObservableField oldPassword) { + this.oldPassword = oldPassword; + } + + public ObservableField getOldPasswordError() { + return oldPasswordError; + } + + public void setOldPasswordError(ObservableField oldPasswordError) { + this.oldPasswordError = oldPasswordError; + } + + public ObservableField getEmail() { + return email; + } + + public void setEmail(ObservableField email) { + this.email = email; + } + + public ObservableField getEmailError() { + return emailError; + } + + public void setEmailError(ObservableField emailError) { + this.emailError = emailError; + } + + public void onSaveClicked() { + saveProfile(); + + } + + public void loadProfile() { + subscription.add(new AccountManager().getProfile() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onNext(UserResponse userResponse) { + User user = userResponse.getData(); + if(user != null) { + firstName.set(user.getFirstName()); + lastName.set(user.getLastName()); + email.set(user.getEmail()); + clearPasswordErrors(); + clearNamesErrors(); + } + } + })); + } + + public void saveProfile() { + if (validateNames()) { + if(passwordsVisibility.get() && validatePassword()) { + subscription.add(new AccountManager().changePassword(oldPassword.get(), password.get()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onNext(Object o) { + + } + })); + } + User user = new User(); + user.setFirstName(firstName.get()); + user.setLastName(lastName.get()); + user.setEmail(email.get()); + subscription.add(new AccountManager().updateProfile(user) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onNext(Object o) { + if(!passwordsVisibility.get() || validatePassword()) { + NavigationSubject.getInstance().onNext(null); + } + } + })); + } + } + + public void clear() { + if(subscription != null && !subscription.isUnsubscribed()) { + subscription.unsubscribe(); + } + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java index e0a622a..85561cd 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/SearchViewModel.java @@ -49,7 +49,7 @@ public class SearchViewModel extends BaseObservable { }); } }); - rxSearch = rxSearch.debounce(1000, TimeUnit.MILLISECONDS); + rxSearch = rxSearch.debounce(500, TimeUnit.MILLISECONDS); subscription = rxSearch.flatMap(new Func1>() { @Override public rx.Observable call(String s) { diff --git a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java index 8d5e275..1c4882d 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/viewmodels/UserViewModel.java @@ -1,17 +1,28 @@ package com.smarthomies.realtimetalk.viewmodels; +import android.content.Intent; import android.databinding.BaseObservable; import android.databinding.Bindable; import android.databinding.BindingAdapter; import android.databinding.ObservableBoolean; +import android.support.design.widget.Snackbar; import android.util.Log; import android.view.View; import android.widget.ImageView; import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.managers.ContactsManager; import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.views.activities.CallActivity; import com.squareup.picasso.Picasso; +import java.util.concurrent.TimeUnit; + +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + /** * Created by ensar on 15/11/16. */ @@ -21,10 +32,22 @@ public class UserViewModel extends BaseObservable { private User model; private ObservableBoolean state = new ObservableBoolean(); + public UserViewModel() { + this.model = new User(); + } + public UserViewModel(User model) { this.model = model; } + public void setModel(User model) { + this.model = model; + if (model == null) { + this.model = new User(); + } + notifyChange(); + } + @Bindable public String getName() { return model.getFirstName() + " " + model.getLastName(); @@ -48,8 +71,48 @@ public class UserViewModel extends BaseObservable { return new View.OnClickListener() { @Override public void onClick(View v) { + final Subscription subscription = new ContactsManager() + .saveContact(model) + .delaySubscription(3000, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + } + + @Override + public void onError(Throwable e) { + state.set(!state.get()); + } + + @Override + public void onNext(Object o) { + + } + }); Log.d(TAG, "onClick: "); state.set(!state.get()); + Snackbar.make(v, model.getFirstName() + " " + model.getLastName() + " added to conctacts.", Snackbar.LENGTH_LONG) + .setDuration(3000) + .setAction("Undo", new View.OnClickListener() { + @Override + public void onClick(View v) { + if(subscription != null && !subscription.isUnsubscribed()) { + subscription.unsubscribe(); + state.set(!state.get()); + } + } + }).show(); + } + }; + } + + public View.OnClickListener call() { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + v.getContext().startActivity(new Intent(v.getContext(), CallActivity.class)); } }; } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/CallActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/CallActivity.java new file mode 100644 index 0000000..821d3b6 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/CallActivity.java @@ -0,0 +1,78 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.content.Context; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.utils.MediaStreamClient; +import com.smarthomies.realtimetalk.utils.MediaStreamServer; + +public class CallActivity extends AppCompatActivity { + + private TextView serverStatus; + private EditText serverIp; + private Button nazovi; + private MediaStreamClient mss; + private MediaStreamServer msc; + // DESIGNATE A PORT + boolean isRecording; + private static Handler handler = new Handler(); + Thread t=null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_call); + + nazovi = (Button) findViewById(R.id.nazovi); + serverIp = (EditText) findViewById(R.id.ipAdress); + serverStatus=(TextView) findViewById(R.id.labela); + nazovi.setOnTouchListener(nazoviL); + isRecording=false; + msc=new MediaStreamServer(CallActivity.this); + t=new Thread(msc); + t.start(); + } + + + private View.OnTouchListener nazoviL=new View.OnTouchListener() { + + public boolean onTouch(View v, MotionEvent event) { + // TODO Auto-generated method stub + String ip=serverIp.getText().toString(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + msc.stop(); + mss=new MediaStreamClient(CallActivity.this,ip); + break; + case MotionEvent.ACTION_UP: + mss.stop(CallActivity.this); + break; + } + return false; + } + }; + + public static void toast(final String msg, final Context ctx){ + handler.post(new Runnable() { + @Override + public void run() { + Context context = ctx; + CharSequence text =msg; + int duration = Toast.LENGTH_SHORT; + + Toast toast = Toast.makeText(context, text, duration); + toast.show(); + } + }); + } + +} \ No newline at end of file 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 index 81c84e4..bdd7d05 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/MainActivity.java @@ -2,13 +2,11 @@ package com.smarthomies.realtimetalk.views.activities; import android.content.Intent; import android.databinding.DataBindingUtil; +import android.databinding.Observable; +import android.databinding.ObservableField; 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; @@ -16,25 +14,35 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; +import android.widget.Toast; 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.models.network.AuthenticationResponse; +import com.smarthomies.realtimetalk.utils.RTTErrorUtil; 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; +import java.util.List; + +import rx.Subscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.exceptions.CompositeException; +import rx.schedulers.Schedulers; public class MainActivity extends RTTActivity implements NavigationView.OnNavigationItemSelectedListener { private MainViewModelHolder viewModelHolder; + private Subscription logoutSubscription; + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -54,24 +62,25 @@ public class MainActivity extends RTTActivity 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); + viewModelHolder.getMainViewModel().loadContacts(); + viewModelHolder.getMainViewModel().loadProfile(); + + final 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); + viewModelHolder.getMainViewModel().getContacts().addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(Observable observable, int i) { + List users = ((ObservableField>)observable).get(); + UsersAdapter usersAdapter = new UsersAdapter(UsersAdapter.ViewType.LIST); + usersAdapter.setUsers(users); + recyclerView.setAdapter(usersAdapter); + } + }); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -87,6 +96,59 @@ public class MainActivity extends RTTActivity binding.navView.setNavigationItemSelectedListener(this); } + @Override + protected void subscribeToSubjects() { + super.subscribeToSubjects(); + logoutSubscription = getViewModelHolder().getMainViewModel().getLogoutSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new LogoutSubscriber()); + } + + @Override + protected void unsubscribeFromSubjects() { + super.unsubscribeFromSubjects(); + if(logoutSubscription != null && !logoutSubscription.isUnsubscribed()) { + logoutSubscription.unsubscribe(); + } + } + + private void reconnectToLogoutSubject() { + logoutSubscription = getViewModelHolder().getMainViewModel().createLogoutSubject().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(new LogoutSubscriber()); + } + + @Override + protected void onResume() { + super.onResume(); + + } + + private class LogoutSubscriber extends Subscriber { + @Override + public void onCompleted() { + reconnectToLogoutSubject(); + getViewModelHolder().getMainViewModel().onLogoutDone(); + } + + @Override + public void onError(Throwable e) { + reconnectToLogoutSubject(); + getViewModelHolder().getMainViewModel().onLogoutDone(); + + if (e instanceof CompositeException) { + for (Throwable ex : ((CompositeException) e).getExceptions()) { + if(ex instanceof RuntimeException) { + handleException(ex.getCause()); + } + } + } else { + handleException(e); + } + } + + @Override + public void onNext(Object object) { + getViewModelHolder().getMainViewModel().onLogoutDone(); + } + } + @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -127,8 +189,11 @@ public class MainActivity extends RTTActivity if (id == R.id.nav_logout) { // Handle the camera action - startActivity(new Intent(this, LoginActivity.class)); - finish(); + getViewModelHolder().getMainViewModel().onLogoutClick(); + } + + if (id == R.id.nav_profile) { + getViewModelHolder().getMainViewModel().onProfileClick(); } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -140,4 +205,8 @@ public class MainActivity extends RTTActivity return viewModelHolder; } + 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/ProfileActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/ProfileActivity.java new file mode 100644 index 0000000..9f617fd --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/ProfileActivity.java @@ -0,0 +1,97 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.app.SearchManager; +import android.databinding.DataBindingUtil; +import android.databinding.Observable; +import android.databinding.ObservableField; +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +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 com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.databinding.ActivityProfileBinding; +import com.smarthomies.realtimetalk.databinding.ActivitySearchBinding; +import com.smarthomies.realtimetalk.models.db.User; +import com.smarthomies.realtimetalk.viewmodels.SearchViewModel; +import com.smarthomies.realtimetalk.views.activities.bindingutils.OnErrorChangedCallback; +import com.smarthomies.realtimetalk.views.adapters.UsersAdapter; +import com.smarthomies.realtimetalk.views.fragments.ProfileViewModelHolder; +import com.smarthomies.realtimetalk.views.fragments.SearchViewModelHolder; + +import java.util.List; + +public class ProfileActivity extends RTTActivity { + + private ProfileViewModelHolder viewModelHolder; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Add retained to fragment manager + if(getSupportFragmentManager().findFragmentByTag(ProfileViewModelHolder.class.getName()) == null) { + viewModelHolder = new ProfileViewModelHolder(); + + getSupportFragmentManager() + .beginTransaction() + .add(viewModelHolder, SearchViewModelHolder.class.getName()) + .commit(); + } + else { + viewModelHolder = (ProfileViewModelHolder)getSupportFragmentManager().findFragmentByTag(ProfileViewModelHolder.class.getName()); + } + + ActivityProfileBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_profile); + binding.setViewModel(viewModelHolder.getProfileViewModel()); + + + getViewModelHolder().getProfileViewModel().getFirstNameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilFirstName)); + getViewModelHolder().getProfileViewModel().getLastNameError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilLastName)); + getViewModelHolder().getProfileViewModel().getEmailError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilEmail)); + + getViewModelHolder().getProfileViewModel().getOldPasswordError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilCurrentPassword)); + getViewModelHolder().getProfileViewModel().getPasswordError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilPassword)); + getViewModelHolder().getProfileViewModel().getPasswordConfirmationError().addOnPropertyChangedCallback(new OnErrorChangedCallback(binding.tilConfirmPassword)); + + viewModelHolder.getProfileViewModel().loadProfile(); + + 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.profile, 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_save) { + getViewModelHolder().getProfileViewModel().onSaveClicked(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + public ProfileViewModelHolder getViewModelHolder() { + return viewModelHolder; + } +} diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SplashScreenActivity.java b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SplashScreenActivity.java new file mode 100644 index 0000000..e92d1b1 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/activities/SplashScreenActivity.java @@ -0,0 +1,38 @@ +package com.smarthomies.realtimetalk.views.activities; + +import android.annotation.SuppressLint; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Pair; +import android.view.MotionEvent; +import android.view.View; + +import com.smarthomies.realtimetalk.R; +import com.smarthomies.realtimetalk.RTTActivity; +import com.smarthomies.realtimetalk.utils.NavigationSubject; +import com.smarthomies.realtimetalk.utils.RTTAppHelper; + +public class SplashScreenActivity extends RTTActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_splash_screen); + + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if(TextUtils.isEmpty(RTTAppHelper.getInstance().getToken())) { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(LoginActivity.class, null)); + } else { + NavigationSubject.getInstance().onNext(new Pair, Bundle>(MainActivity.class, null)); + } + NavigationSubject.getInstance().onNext(null); + } + }, 3000); + } +} 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 index 5cc5f32..aeb93d5 100644 --- 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 @@ -1,9 +1,15 @@ package com.smarthomies.realtimetalk.views.activities.bindingutils; +import android.app.Activity; import android.databinding.BindingAdapter; import android.support.design.widget.NavigationView; +import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; import android.widget.ImageView; +import android.widget.TextView; import com.smarthomies.realtimetalk.R; import com.smarthomies.realtimetalk.viewmodels.UserViewModel; @@ -27,8 +33,27 @@ public class BindingAdapters { NavHeaderMainBinding navHeaderMainBinding = NavHeaderMainBinding.inflate(LayoutInflater.from(view.getContext())); navHeaderMainBinding.setViewModel(userViewModel); navHeaderMainBinding.executePendingBindings(); + for(int i = 0; i < view.getHeaderCount(); i++) { + view.removeHeaderView(view.getHeaderView(i)); + } view.addHeaderView(navHeaderMainBinding.getRoot()); } + @BindingAdapter("android:imeActionId") + public static void setActionId(EditText view, int id) { + if (view.getContext() instanceof Activity) { + final View actionView = ((Activity) view.getContext()).findViewById(id); + view.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if(actionId == EditorInfo.IME_ACTION_DONE) { + actionView.performClick(); + } + return false; + } + }); + } + } + } 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 index a6d2f80..fcc2e3c 100644 --- a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/MainViewModelHolder.java @@ -17,4 +17,10 @@ public class MainViewModelHolder extends ViewModelHolder { public void setMainViewModel(MainViewModel mainViewModel) { this.mainViewModel = mainViewModel; } + + @Override + public void onDestroy() { + super.onDestroy(); + mainViewModel.clear(); + } } diff --git a/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ProfileViewModelHolder.java b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ProfileViewModelHolder.java new file mode 100644 index 0000000..de3e365 --- /dev/null +++ b/app/src/main/java/com/smarthomies/realtimetalk/views/fragments/ProfileViewModelHolder.java @@ -0,0 +1,26 @@ +package com.smarthomies.realtimetalk.views.fragments; + +import com.smarthomies.realtimetalk.viewmodels.ProfileViewModel; + +/** + * Created by ensar on 15/11/16. + */ +public class ProfileViewModelHolder extends ViewModelHolder { + public static final String TAG = ProfileViewModelHolder.class.getSimpleName(); + + private ProfileViewModel profileViewModel = new ProfileViewModel(); + + public ProfileViewModel getProfileViewModel() { + return profileViewModel; + } + + public void setProfileViewModel(ProfileViewModel profileViewModel) { + this.profileViewModel = profileViewModel; + } + + @Override + public void onDestroy() { + super.onDestroy(); + profileViewModel.clear(); + } +} diff --git a/app/src/main/res/drawable/clicked_rounde_shape.xml b/app/src/main/res/drawable/clicked_rounde_shape.xml new file mode 100644 index 0000000..fe621b7 --- /dev/null +++ b/app/src/main/res/drawable/clicked_rounde_shape.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/mjenjanje.xml b/app/src/main/res/drawable/mjenjanje.xml new file mode 100644 index 0000000..3bef1cd --- /dev/null +++ b/app/src/main/res/drawable/mjenjanje.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/round_shape.xml b/app/src/main/res/drawable/round_shape.xml new file mode 100644 index 0000000..df6ef07 --- /dev/null +++ b/app/src/main/res/drawable/round_shape.xml @@ -0,0 +1,9 @@ + + + + + + + + \ 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 index 458b4b0..dcc8392 100644 --- a/app/src/main/res/drawable/side_nav_bar.xml +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -2,8 +2,8 @@ android:shape="rectangle"> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_call.xml b/app/src/main/res/layout/activity_call.xml new file mode 100644 index 0000000..172d6bc --- /dev/null +++ b/app/src/main/res/layout/activity_call.xml @@ -0,0 +1,50 @@ + + + +