Unverified Commit 2a281fa3 authored by Leonardo Aramaki's avatar Leonardo Aramaki Committed by GitHub

Merge branch 'develop' into patch-4

parents 124340a7 fcc9a2a5
......@@ -25,6 +25,9 @@ object RocketChatCache {
private val KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST"
private val KEY_OPENED_ROOMS = "KEY_OPENED_ROOMS"
private val KEY_SESSION_TOKEN = "KEY_SESSION_TOKEN"
private val KEY_USER_ID = "KEY_USER_ID"
private val KEY_USER_NAME = "KEY_USER_NAME"
private val KEY_USER_USERNAME = "KEY_USER_USERNAME"
private lateinit var sharedPreferences: SharedPreferences
......@@ -404,4 +407,20 @@ object RocketChatCache {
sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
}, BackpressureStrategy.LATEST)
}
fun setUserId(userId: String) = setString(KEY_USER_ID, userId)
fun getUserId(): String? = getString(KEY_USER_ID, null)
fun setUserName(name: String?) {
name?.let {
setString(KEY_USER_NAME, name)
}
}
fun getUserName(): String? = getString(KEY_USER_NAME, null)
fun setUserUsername(username: String) = setString(KEY_USER_USERNAME, username)
fun getUserUsername(): String? = getString(KEY_USER_USERNAME, null)
}
\ No newline at end of file
package chat.rocket.android.api.rest
import chat.rocket.android.R
import chat.rocket.android.RocketChatApplication
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.push.gcm.GcmPushHelper
import chat.rocket.core.models.Room
import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID
import okhttp3.HttpUrl
import okhttp3.MediaType
import okhttp3.Request
import okhttp3.RequestBody
import org.json.JSONObject
import java.io.IOException
/**
* Helper class for dealing with Rest API calls.
......@@ -139,6 +148,30 @@ object RestApiHelper {
.build()
}
fun getRequestForPushTokenRegistration(hostname: String,
token: String,
userId: String): Request {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForPushToken(hostname))
?.newBuilder()
?.build()
val json = JSONObject()
.put("type", "gcm")
.put("appName", "main")
.put("value", GcmPushHelper.getGcmToken())
val requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),
json.toString())
return Request.Builder()
.url(parsedHttpUrl)
.post(requestBody)
.addHeader("X-Auth-Token", token)
.addHeader("X-User-Id", userId)
.addHeader("Content-Type", "application/json")
.build()
}
/**
* Returns a Rest API endpoint URL for favorite or pinned messages accordingly with the room type and the server hostname.
*
......@@ -169,6 +202,9 @@ object RestApiHelper {
fun getEndpointUrlForMemberList(roomType: String, hostname: String): String =
UrlHelper.getSafeHostname(hostname) + getRestApiUrlForMemberList(roomType)
fun getEndpointUrlForPushToken(hostname: String): String =
UrlHelper.getSafeHostname(hostname) + getRestApiUrlForPushToken()
/**
* Returns the correspondent Rest API URL accordingly with the room type to get its favorite or pinned messages.
*
......@@ -216,4 +252,21 @@ object RestApiHelper {
}
return restApiUrl
}
/**
* Returns the correspondent Rest API URL for registration/deletion of Gcm Registration token.
*/
fun getRestApiUrlForPushToken(): String {
return "/api/v1/push.token"
}
@Throws(IOException::class)
private fun getGcmToken(senderId: String): String {
return InstanceID.getInstance(RocketChatApplication.getInstance())
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null)
}
private fun getSenderId(): String {
return RocketChatApplication.getInstance().getString(R.string.gcm_sender_id)
}
}
\ No newline at end of file
......@@ -4,14 +4,17 @@ import android.app.Dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.jakewharton.rxbinding2.widget.RxTextView;
import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.TextUtils;
......@@ -20,122 +23,174 @@ import chat.rocket.android.helper.TextUtils;
* Dialog for user registration.
*/
public class UserRegistrationDialogFragment extends DialogFragment {
private String hostname;
private String username;
private String email;
private String password;
public UserRegistrationDialogFragment() {
super();
}
/**
* build UserRegistrationDialogFragment with auto-detect email/username.
*/
public static UserRegistrationDialogFragment create(String hostname,
String usernameOrEmail, String password) {
if (Patterns.EMAIL_ADDRESS.matcher(usernameOrEmail).matches()) {
return create(hostname, null, usernameOrEmail, password);
} else {
return create(hostname, usernameOrEmail, null, password);
private String hostname;
private String username;
private String email;
private String password;
private TextInputEditText txtUsername;
private TextInputEditText txtEmail;
private TextInputEditText txtPasswd;
private TextInputLayout textInputUsername;
private TextInputLayout textInputEmail;
private TextInputLayout textInputPassword;
private View waitingView;
public UserRegistrationDialogFragment() {
super();
}
}
/**
* build UserRegistrationDialogFragment.
*/
public static UserRegistrationDialogFragment create(String hostname,
String username, String email,
String password) {
Bundle args = new Bundle();
args.putString("hostname", hostname);
if (!TextUtils.isEmpty(username)) {
args.putString("username", username);
/**
* build UserRegistrationDialogFragment with auto-detect email/username.
*/
public static UserRegistrationDialogFragment create(String hostname,
String usernameOrEmail, String password) {
if (Patterns.EMAIL_ADDRESS.matcher(usernameOrEmail).matches()) {
return create(hostname, null, usernameOrEmail, password);
} else {
return create(hostname, usernameOrEmail, null, password);
}
}
if (!TextUtils.isEmpty(email)) {
args.putString("email", email);
/**
* build UserRegistrationDialogFragment.
*/
public static UserRegistrationDialogFragment create(String hostname,
String username, String email,
String password) {
Bundle args = new Bundle();
args.putString("hostname", hostname);
if (!TextUtils.isEmpty(username)) {
args.putString("username", username);
}
if (!TextUtils.isEmpty(email)) {
args.putString("email", email);
}
if (!TextUtils.isEmpty(password)) {
args.putString("password", password);
}
UserRegistrationDialogFragment dialog = new UserRegistrationDialogFragment();
dialog.setArguments(args);
return dialog;
}
if (!TextUtils.isEmpty(password)) {
args.putString("password", password);
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
hostname = args.getString("hostname");
username = args.getString("username");
email = args.getString("email");
password = args.getString("password");
}
}
UserRegistrationDialogFragment dialog = new UserRegistrationDialogFragment();
dialog.setArguments(args);
return dialog;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
hostname = args.getString("hostname");
username = args.getString("username");
email = args.getString("email");
password = args.getString("password");
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getContext(), R.style.AppTheme_Dialog)
.setView(createDialogView())
.create();
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getContext(), R.style.AppTheme_Dialog)
.setView(createDialogView())
.create();
}
private View createDialogView() {
View dialog = LayoutInflater.from(getContext())
.inflate(R.layout.dialog_user_registration, null, false);
final TextView txtUsername = (TextView) dialog.findViewById(R.id.editor_username);
final TextView txtEmail = (TextView) dialog.findViewById(R.id.editor_email);
final TextView txtPasswd = (TextView) dialog.findViewById(R.id.editor_passwd);
if (!TextUtils.isEmpty(username)) {
txtUsername.setText(username);
private View createDialogView() {
View dialog = LayoutInflater.from(getContext())
.inflate(R.layout.dialog_user_registration, null, false);
initViews(dialog);
setUpRxBinders();
if (!TextUtils.isEmpty(username)) {
txtUsername.setText(username);
}
if (!TextUtils.isEmpty(email)) {
txtEmail.setText(email);
}
if (!TextUtils.isEmpty(password)) {
txtPasswd.setText(password);
}
waitingView.setVisibility(View.GONE);
dialog.findViewById(R.id.btn_register_user).setOnClickListener(registerButton -> {
if (checkIfEditTextsEmpty())
return;
registerButton.setEnabled(false);
registerButton.setAlpha(0.5f);
waitingView.setVisibility(View.VISIBLE);
username = txtUsername.getText().toString();
email = txtEmail.getText().toString();
password = txtPasswd.getText().toString();
MethodCallHelper methodCallHelper = new MethodCallHelper(getContext(), hostname);
methodCallHelper.registerUser(username, email, password, password)
.onSuccessTask(task -> methodCallHelper.loginWithEmail(email, password))
.onSuccessTask(task -> methodCallHelper.setUsername(username)) //TODO: should prompt!
.onSuccessTask(task -> methodCallHelper.joinDefaultChannels())
.onSuccessTask(task -> {
dismiss();
return task;
})
.continueWith(task -> {
if (task.isFaulted()) {
Exception exception = task.getError();
showError(exception.getMessage());
registerButton.setEnabled(true);
waitingView.setVisibility(View.GONE);
}
return null;
});
});
return dialog;
}
if (!TextUtils.isEmpty(email)) {
txtEmail.setText(email);
private void initViews(View dialog) {
txtUsername = dialog.findViewById(R.id.editor_username);
txtEmail = dialog.findViewById(R.id.editor_email);
txtPasswd = dialog.findViewById(R.id.editor_passwd);
textInputEmail = dialog.findViewById(R.id.text_input_email);
textInputUsername = dialog.findViewById(R.id.text_input_username);
textInputPassword = dialog.findViewById(R.id.text_input_passwd);
waitingView = dialog.findViewById(R.id.waiting);
}
if (!TextUtils.isEmpty(password)) {
txtPasswd.setText(password);
private boolean checkIfEditTextsEmpty() {
boolean check = false;
if (TextUtils.isEmpty(txtEmail.getText().toString())) {
textInputEmail.setError("Enter an email address");
textInputEmail.setErrorEnabled(true);
check = true;
}
if (TextUtils.isEmpty(txtUsername.getText().toString())) {
textInputUsername.setError("Enter a username");
textInputUsername.setErrorEnabled(true);
check = true;
}
if (TextUtils.isEmpty(txtPasswd.getText().toString())) {
textInputPassword.setError("Enter a password");
textInputPassword.setErrorEnabled(true);
check = true;
}
return check;
}
final View waitingView = dialog.findViewById(R.id.waiting);
waitingView.setVisibility(View.GONE);
dialog.findViewById(R.id.btn_register_user).setOnClickListener(view -> {
view.setEnabled(false);
waitingView.setVisibility(View.VISIBLE);
username = txtUsername.getText().toString();
email = txtEmail.getText().toString();
password = txtPasswd.getText().toString();
MethodCallHelper methodCallHelper = new MethodCallHelper(getContext(), hostname);
methodCallHelper.registerUser(username, email, password, password)
.onSuccessTask(task -> methodCallHelper.loginWithEmail(email, password))
.onSuccessTask(task -> methodCallHelper.setUsername(username)) //TODO: should prompt!
.onSuccessTask(task -> methodCallHelper.joinDefaultChannels())
.onSuccessTask(task -> {
dismiss();
return task;
})
.continueWith(task -> {
if (task.isFaulted()) {
Exception exception = task.getError();
showError(exception.getMessage());
view.setEnabled(true);
waitingView.setVisibility(View.GONE);
}
return null;
});
});
return dialog;
}
private void showError(String errMessage) {
Toast.makeText(getContext(), errMessage, Toast.LENGTH_SHORT).show();
}
private void setUpRxBinders() {
RxTextView.textChanges(txtUsername).subscribe(text -> {
if (!TextUtils.isEmpty(text) && textInputUsername.isErrorEnabled())
textInputUsername.setErrorEnabled(false);
});
RxTextView.textChanges(txtEmail).subscribe(text -> {
if (!TextUtils.isEmpty(text) && textInputEmail.isErrorEnabled())
textInputEmail.setErrorEnabled(false);
});
RxTextView.textChanges(txtPasswd).subscribe(text -> {
if (!TextUtils.isEmpty(text) && textInputPassword.isErrorEnabled())
textInputPassword.setErrorEnabled(false);
});
}
private void showError(String errMessage) {
Toast.makeText(getContext(), errMessage, Toast.LENGTH_SHORT).show();
}
}
package chat.rocket.android.push.gcm
import bolts.Task
import chat.rocket.android.R
import chat.rocket.android.RocketChatApplication
import chat.rocket.android.RocketChatCache
import chat.rocket.android.api.RaixPushHelper
import chat.rocket.persistence.realm.RealmHelper
import chat.rocket.persistence.realm.models.ddp.RealmUser
import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID
import java.io.IOException
object GcmPushHelper {
fun getGcmToken(): String? = getGcmToken(getSenderId())
@Throws(IOException::class)
private fun registerGcmTokenForServer(realmHelper: RealmHelper): Task<Void> {
val gcmToken = getGcmToken(getSenderId())
val currentUser = realmHelper.executeTransactionForRead({ realm -> RealmUser.queryCurrentUser(realm).findFirst() })
val userId = if (currentUser != null) currentUser.getId() else null
val pushId = RocketChatCache.getOrCreatePushId()
return RaixPushHelper(realmHelper)
.pushUpdate(pushId!!, gcmToken, userId)
}
@Throws(IOException::class)
private fun getGcmToken(senderId: String): String {
return InstanceID.getInstance(RocketChatApplication.getInstance())
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null)
}
private fun getSenderId(): String {
return RocketChatApplication.getInstance().getString(R.string.gcm_sender_id)
}
}
\ No newline at end of file
......@@ -70,10 +70,10 @@ public class RocketChatWebSocketThread extends HandlerThread {
private final String hostname;
private final RealmHelper realmHelper;
private final ConnectivityManagerInternal connectivityManager;
private final ArrayList<Registrable> listeners = new ArrayList<>();
private volatile ArrayList<Registrable> listeners = new ArrayList<>();
private final CompositeDisposable heartbeatDisposable = new CompositeDisposable();
private final CompositeDisposable reconnectDisposable = new CompositeDisposable();
private boolean listenersRegistered;
private volatile boolean listenersRegistered;
private RocketChatWebSocketThread(Context appContext, String hostname) {
super("RC_thread_" + hostname);
......
......@@ -5,6 +5,7 @@ import android.content.Context;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.service.Registrable;
......@@ -56,6 +57,10 @@ public class CurrentUserObserver extends AbstractModelObserver<RealmUser> {
}
listeners = new ArrayList<>();
RocketChatCache.INSTANCE.setUserId(user.getId());
RocketChatCache.INSTANCE.setUserUsername(user.getUsername());
RocketChatCache.INSTANCE.setUserName(user.getName());
final String userId = user.getId();
// get and observe Room subscriptions.
......
......@@ -13,6 +13,7 @@ import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.RaixPushHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.push.gcm.GcmPushHelper;
import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
......@@ -24,63 +25,72 @@ import io.realm.RealmResults;
* call raix:push-update if needed.
*/
public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRegistration> {
public GcmPushRegistrationObserver(Context context, String hostname,
RealmHelper realmHelper) {
super(context, hostname, realmHelper);
}
@Override
public RealmResults<GcmPushRegistration> queryItems(Realm realm) {
return GcmPushRegistration.queryDefault(realm)
.equalTo(GcmPushRegistration.SYNC_STATE, SyncState.NOT_SYNCED)
.equalTo(GcmPushRegistration.GCM_PUSH_ENABLED, true)
.findAll();
}
public GcmPushRegistrationObserver(Context context, String hostname,
RealmHelper realmHelper) {
super(context, hostname, realmHelper);
}
@Override
public void onUpdateResults(List<GcmPushRegistration> results) {
if (results.isEmpty()) {
return;
@Override
public RealmResults<GcmPushRegistration> queryItems(Realm realm) {
return GcmPushRegistration.queryDefault(realm)
.equalTo(GcmPushRegistration.SYNC_STATE, SyncState.NOT_SYNCED)
.equalTo(GcmPushRegistration.GCM_PUSH_ENABLED, true)
.findAll();
}
realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.SYNCING);
return null;
}).onSuccessTask(_task -> registerGcmTokenForServer()
).onSuccessTask(_task ->
realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.SYNCED);
return null;
})
).continueWith(task -> {
if (task.isFaulted()) {
realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.FAILED);
return null;
}).continueWith(new LogIfError());
}
return null;
});
}
@Override
public void onUpdateResults(List<GcmPushRegistration> results) {
String gcmToken = GcmPushHelper.INSTANCE.getGcmToken();
if (gcmToken != null && !gcmToken.isEmpty()) {
// We already have gcm token, so try to register it.
try {
tryToRegisterToken();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Task<Void> registerGcmTokenForServer() throws IOException {
final String gcmToken = getGcmToken(getSenderId());
final RealmUser currentUser = realmHelper.executeTransactionForRead(realm ->
RealmUser.queryCurrentUser(realm).findFirst());
final String userId = currentUser != null ? currentUser.getId() : null;
final String pushId = RocketChatCache.INSTANCE.getOrCreatePushId();
private Task<Void> tryToRegisterToken() {
return realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.SYNCING);
return null;
}).onSuccessTask(_task -> registerGcmTokenForServer()
).onSuccessTask(_task ->
realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.SYNCED);
return null;
})
).continueWith(task -> {
if (task.isFaulted()) {
realmHelper.executeTransaction(realm -> {
GcmPushRegistration.queryDefault(realm).findFirst().setSyncState(SyncState.FAILED);
return null;
}).continueWith(new LogIfError());
}
return null;
});
}
return new RaixPushHelper(realmHelper)
.pushUpdate(pushId, gcmToken, userId);
}
private Task<Void> registerGcmTokenForServer() throws IOException {
final String gcmToken = getGcmToken(getSenderId());
final RealmUser currentUser = realmHelper.executeTransactionForRead(realm ->
RealmUser.queryCurrentUser(realm).findFirst());
final String userId = currentUser != null ? currentUser.getId() : null;
final String pushId = RocketChatCache.INSTANCE.getOrCreatePushId();
private String getGcmToken(String senderId) throws IOException {
return InstanceID.getInstance(context)
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
}
return new RaixPushHelper(realmHelper)
.pushUpdate(pushId, gcmToken, userId);
}
private String getSenderId() {
return context.getString(R.string.gcm_sender_id);
}
private String getGcmToken(String senderId) throws IOException {
return InstanceID.getInstance(context)
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
}
private String getSenderId() {
return context.getString(R.string.gcm_sender_id);
}
}
......@@ -173,6 +173,8 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
public void onClick(View view) {
new CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(getContext(), R.color.colorPrimary))
.setStartAnimations(getContext(), R.anim.slide_in_right, R.anim.slide_out_left)
.setExitAnimations(getContext(), R.anim.slide_in_left, R.anim.slide_out_right)
.build()
.launchUrl(getContext(), Uri.parse(link));
}
......
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%p"
android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="-100%p"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%p"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment