Commit 80872e33 authored by Yusuke Iwaki's avatar Yusuke Iwaki

implement send-message.

parent 8c7742ff
......@@ -6,6 +6,7 @@ import bolts.Continuation;
import bolts.Task;
import chat.rocket.android.helper.CheckSum;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.model.SyncState;
import chat.rocket.android.model.ddp.Message;
import chat.rocket.android.model.ddp.RoomSubscription;
import chat.rocket.android.model.internal.MethodCall;
......@@ -255,7 +256,10 @@ public class MethodCallHelper {
return realmHelper.executeTransaction(realm -> {
if (timestamp == 0) {
realm.where(Message.class).equalTo("rid", roomId).findAll().deleteAllFromRealm();
realm.where(Message.class)
.equalTo("rid", roomId)
.equalTo("syncstate", SyncState.SYNCED)
.findAll().deleteAllFromRealm();
}
if (messages.length() > 0) {
realm.createOrUpdateAllFromJson(Message.class, messages);
......@@ -278,4 +282,27 @@ public class MethodCallHelper {
return call("getUsersOfRoom", TIMEOUT_MS, () -> new JSONArray().put(roomId).put(showAll))
.onSuccessTask(CONVERT_TO_JSON_OBJECT);
}
/**
* send message.
*/
public Task<JSONObject> sendMessage(String messageId, String roomId, String msg) {
try {
return sendMessage(new JSONObject()
.put("_id", messageId)
.put("rid", roomId)
.put("msg", msg));
} catch (JSONException exception) {
return Task.forError(exception);
}
}
/**
* Send message object.
*/
public Task<JSONObject> sendMessage(final JSONObject messageJson) {
return call("sendMessage", TIMEOUT_MS, () -> new JSONArray().put(messageJson))
.onSuccessTask(CONVERT_TO_JSON_OBJECT)
.onSuccessTask(task -> Task.forResult(Message.customizeJson(task.getResult())));
}
}
......@@ -2,6 +2,7 @@ package chat.rocket.android.fragment.chatroom;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SlidingPaneLayout;
......@@ -13,6 +14,7 @@ import chat.rocket.android.fragment.chatroom.dialog.UsersOfRoomDialogFragment;
import chat.rocket.android.helper.LoadMoreScrollListener;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.helper.OnBackPressListener;
import chat.rocket.android.layouthelper.chatroom.MessageComposerManager;
import chat.rocket.android.layouthelper.chatroom.MessageListAdapter;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.SyncState;
......@@ -23,9 +25,11 @@ import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmObjectObserver;
import chat.rocket.android.realm_helper.RealmStore;
import chat.rocket.android.service.RocketChatService;
import chat.rocket.android.widget.message.MessageComposer;
import com.jakewharton.rxbinding.support.v4.widget.RxDrawerLayout;
import io.realm.Sort;
import java.lang.reflect.Field;
import java.util.UUID;
import org.json.JSONObject;
import timber.log.Timber;
......@@ -41,6 +45,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements OnBackPres
private String hostname;
private LoadMoreScrollListener scrollListener;
private RealmObjectObserver<LoadMessageProcedure> procedureObserver;
private MessageComposerManager messageComposerManager;
/**
* create fragment with roomId.
......@@ -107,6 +112,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements OnBackPres
listView.addOnScrollListener(scrollListener);
setupSideMenu();
setupMessageComposer();
}
private void setupSideMenu() {
......@@ -143,7 +149,27 @@ public class RoomFragment extends AbstractChatRoomFragment implements OnBackPres
return false;
}
private void setupMessageComposer() {
final FloatingActionButton fabCompose =
(FloatingActionButton) rootView.findViewById(R.id.fab_compose);
final MessageComposer messageComposer =
(MessageComposer) rootView.findViewById(R.id.message_composer);
messageComposerManager = new MessageComposerManager(fabCompose, messageComposer);
messageComposerManager.setCallback(messageText ->
realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(Message.class, new JSONObject()
.put("_id", UUID.randomUUID().toString())
.put("syncstate", SyncState.NOT_SYNCED)
.put("ts", System.currentTimeMillis())
.put("rid", roomId)
.put("msg", messageText))));
}
private void onRenderRoom(RoomSubscription roomSubscription) {
if (roomSubscription == null) {
return;
}
String type = roomSubscription.getT();
if (RoomSubscription.TYPE_CHANNEL.equals(type)) {
activityToolbar.setNavigationIcon(R.drawable.ic_hashtag_white_24dp);
......@@ -225,6 +251,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements OnBackPres
}
@Override public boolean onBackPressed() {
return closeSideMenuIfNeeded();
return closeSideMenuIfNeeded() || messageComposerManager.hideMessageComposerIfNeeded();
}
}
package chat.rocket.android.layouthelper.chatroom;
import android.support.design.widget.FloatingActionButton;
import bolts.Task;
import chat.rocket.android.widget.message.MessageComposer;
/**
* handling visibility of FAB-compose and MessageComposer.
*/
public class MessageComposerManager {
public interface Callback {
Task<Void> onSubmit(String messageText);
}
private final FloatingActionButton fabCompose;
private final MessageComposer messageComposer;
private Callback callback;
public MessageComposerManager(FloatingActionButton fabCompose, MessageComposer messageComposer) {
this.fabCompose = fabCompose;
this.messageComposer = messageComposer;
init();
}
private void init() {
fabCompose.setOnClickListener(view -> {
setMessageComposerVisibility(true);
});
messageComposer.setOnActionListener(new MessageComposer.ActionListener() {
@Override public void onSubmit(String message) {
if (callback != null) {
messageComposer.setEnabled(false);
callback.onSubmit(message).onSuccess(task -> {
clearComposingText();
return null;
}).continueWith(task -> {
messageComposer.setEnabled(true);
return null;
});
}
}
@Override public void onCancel() {
setMessageComposerVisibility(false);
}
});
setMessageComposerVisibility(false);
}
public void setCallback(Callback callback) {
this.callback = callback;
}
public void clearComposingText() {
messageComposer.setText("");
}
private void setMessageComposerVisibility(boolean show) {
if (show) {
fabCompose.hide();
messageComposer.show(null);
} else {
messageComposer.hide(fabCompose::show);
}
}
public boolean hideMessageComposerIfNeeded() {
if (messageComposer.isShown()) {
setMessageComposerVisibility(false);
return true;
}
return false;
}
}
......@@ -18,6 +18,7 @@ import chat.rocket.android.service.ddp.LoginServiceConfigurationSubscriber;
import chat.rocket.android.service.observer.GetUsersOfRoomsProcedureObserver;
import chat.rocket.android.service.observer.LoadMessageProcedureObserver;
import chat.rocket.android.service.observer.MethodCallObserver;
import chat.rocket.android.service.observer.NewMessageObserver;
import chat.rocket.android.service.observer.SessionObserver;
import chat.rocket.android.service.observer.TokenLoginObserver;
import chat.rocket.android_ddp.DDPClientCallback;
......@@ -39,7 +40,8 @@ public class RocketChatWebSocketThread extends HandlerThread {
MethodCallObserver.class,
SessionObserver.class,
LoadMessageProcedureObserver.class,
GetUsersOfRoomsProcedureObserver.class
GetUsersOfRoomsProcedureObserver.class,
NewMessageObserver.class
};
private final Context appContext;
private final String serverConfigId;
......
package chat.rocket.android.service.observer;
import android.content.Context;
import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.SyncState;
import chat.rocket.android.model.ddp.Message;
import chat.rocket.android.realm_helper.RealmHelper;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List;
import org.json.JSONObject;
import timber.log.Timber;
/**
* Observe messages for sending.
*/
public class NewMessageObserver extends AbstractModelObserver<Message> {
private final MethodCallHelper methodCall;
public NewMessageObserver(Context context, RealmHelper realmHelper,
DDPClientWraper ddpClient) {
super(context, realmHelper, ddpClient);
methodCall = new MethodCallHelper(realmHelper, ddpClient);
realmHelper.executeTransaction(realm -> {
// resume pending operations.
RealmResults<Message> pendingMethodCalls = realm.where(Message.class)
.equalTo("syncstate", SyncState.SYNCING)
.findAll();
for (Message message : pendingMethodCalls) {
message.setSyncstate(SyncState.NOT_SYNCED);
}
return null;
}).continueWith(new LogcatIfError());
}
@Override public RealmResults<Message> queryItems(Realm realm) {
return realm.where(Message.class)
.equalTo("syncstate", SyncState.NOT_SYNCED)
.isNotNull("rid")
.findAll();
}
@Override public void onUpdateResults(List<Message> results) {
if (results.isEmpty()) {
return;
}
Message message = results.get(0);
final String messageId = message.get_id();
final String roomId = message.getRid();
final String msg = message.getMsg();
realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(Message.class, new JSONObject()
.put("_id", messageId)
.put("syncstate", SyncState.SYNCING)
)
).onSuccessTask(task ->
methodCall.sendMessage(messageId, roomId, msg).onSuccessTask(_task -> {
JSONObject messageJson = _task.getResult();
messageJson.put("syncstate", SyncState.SYNCED);
return realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(Message.class, messageJson));
})
).continueWith(task -> {
if (task.isFaulted()) {
Timber.w(task.getError());
realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(Message.class, new JSONObject()
.put("_id", messageId)
.put("syncstate", SyncState.FAILED)));
}
return null;
});
}
}
......@@ -11,6 +11,14 @@
android:layout_height="match_parent"
/>
<chat.rocket.android.widget.message.MessageComposer
android:id="@+id/message_composer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.AppCompat.Light"
android:layout_gravity="bottom"
android:background="@android:color/white"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_compose"
android:layout_width="wrap_content"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="instabug_api_key">e111b2f9ac16d4eed4a3568cf6270835</string>
<string name="instabug_api_key">ac4314823dbb87263c76b22db0135727</string>
</resources>
\ No newline at end of file
......@@ -18,6 +18,8 @@ android {
targetSdkVersion rootProject.ext.compileSdkVersion
versionCode 1
versionName "1"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
......
package chat.rocket.android.widget.message;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import chat.rocket.android.widget.R;
public class MessageComposer extends LinearLayout {
protected ActionListener actionListener;
protected ViewGroup composer;
public MessageComposer(Context context) {
super(context);
init();
}
public MessageComposer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MessageComposer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MessageComposer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public final void setOnActionListener(@Nullable ActionListener listener) {
actionListener = listener;
}
private void init() {
composer = (ViewGroup) LayoutInflater.from(getContext())
.inflate(R.layout.message_composer, this, false);
composer.findViewById(R.id.btn_submit).setOnClickListener(new OnClickListener() {
@Override public void onClick(View view) {
String messageText = getText();
if (messageText.length() > 0) {
if (actionListener != null) {
actionListener.onSubmit(messageText);
}
}
}
});
addView(composer);
}
private TextView getEditor() {
return (TextView) composer.findViewById(R.id.editor);
}
public final String getText() {
return getEditor().getText().toString().trim();
}
public final void setText(CharSequence text) {
getEditor().setText(text);
}
public void setEnabled(boolean enabled) {
getEditor().setEnabled(enabled);
composer.findViewById(R.id.btn_submit).setEnabled(enabled);
}
protected final void focusToEditor() {
final TextView editor = getEditor();
editor.requestFocus();
InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editor, InputMethodManager.SHOW_IMPLICIT);
}
protected final void unFocusEditor() {
InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
public void show(@Nullable Runnable callback) {
focusToEditor();
setVisibility(View.VISIBLE);
if (callback != null) {
callback.run();
}
}
public void hide(@Nullable Runnable callback) {
unFocusEditor();
setVisibility(View.GONE);
if (callback != null) {
callback.run();
}
}
public boolean isShown() {
return getVisibility() == View.VISIBLE;
}
public interface ActionListener {
void onSubmit(String message);
void onCancel();
}
}
<vector android:alpha="0.87" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<chat.rocket.android.widget.DividerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<EditText
android:id="@+id/editor"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="3"
/>
<ImageButton
android:id="@+id/btn_submit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
app:srcCompat="@drawable/ic_send_black_24dp"
/>
</LinearLayout>
</LinearLayout>
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