Commit b8d7f7de authored by Nefari0uss's avatar Nefari0uss Committed by GitHub

Merge branch 'develop' into develop

parents 400fc005 95e001cc
...@@ -172,7 +172,10 @@ public class RocketChatCache { ...@@ -172,7 +172,10 @@ public class RocketChatCache {
} }
public Flowable<Optional<String>> getSelectedRoomIdPublisher() { public Flowable<Optional<String>> getSelectedRoomIdPublisher() {
return getValuePublisher(KEY_SELECTED_ROOM_ID); return getValuePublisher(KEY_SELECTED_ROOM_ID)
.filter(Optional::isPresent)
.map(Optional::get)
.map(roomValue -> Optional.ofNullable(new JSONObject(roomValue).optString(getSelectedServerHostname(), null)));
} }
private SharedPreferences getSharedPreferences() { private SharedPreferences getSharedPreferences() {
......
...@@ -6,9 +6,6 @@ import android.support.annotation.Nullable; ...@@ -6,9 +6,6 @@ import android.support.annotation.Nullable;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List; import java.util.List;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
...@@ -198,9 +195,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -198,9 +195,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
compositeDisposable.add( compositeDisposable.add(
rocketChatCache.getSelectedRoomIdPublisher() rocketChatCache.getSelectedRoomIdPublisher()
.filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.map(this::convertStringToJsonObject)
.map(jsonObject -> jsonObject.optString(rocketChatCache.getSelectedServerHostname(), null))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
...@@ -209,11 +205,4 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -209,11 +205,4 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
) )
); );
} }
private JSONObject convertStringToJsonObject(String json) throws JSONException {
if (json == null) {
return new JSONObject();
}
return new JSONObject(json);
}
} }
...@@ -48,7 +48,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -48,7 +48,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private SlidingPaneLayout pane; private SlidingPaneLayout pane;
private MainContract.Presenter presenter; private MainContract.Presenter presenter;
protected int getLayoutContainerForFragment() { @Override
public int getLayoutContainerForFragment() {
return R.id.activity_main_container; return R.id.activity_main_container;
} }
......
...@@ -9,7 +9,6 @@ import kotlinx.android.synthetic.main.activity_room.* ...@@ -9,7 +9,6 @@ import kotlinx.android.synthetic.main.activity_room.*
class RoomActivity : AppCompatActivity() { class RoomActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_room) setContentView(R.layout.activity_room)
...@@ -35,8 +34,7 @@ class RoomActivity : AppCompatActivity() { ...@@ -35,8 +34,7 @@ class RoomActivity : AppCompatActivity() {
} }
private fun addFragment(fragment: Fragment, tag: String) { private fun addFragment(fragment: Fragment, tag: String) {
supportFragmentManager supportFragmentManager.beginTransaction()
.beginTransaction()
.add(R.id.fragment_container, fragment, tag) .add(R.id.fragment_container, fragment, tag)
.commit() .commit()
} }
......
...@@ -30,7 +30,7 @@ public class DDPClientWrapper { ...@@ -30,7 +30,7 @@ public class DDPClientWrapper {
} }
/** /**
* create new API client instance. * build new API client instance.
*/ */
public static DDPClientWrapper create(String hostname) { public static DDPClientWrapper create(String hostname) {
return new DDPClientWrapper(hostname); return new DDPClientWrapper(hostname);
......
...@@ -96,6 +96,7 @@ object RestApiHelper { ...@@ -96,6 +96,7 @@ object RestApiHelper {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForFileList(roomType, hostname)) val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForFileList(roomType, hostname))
?.newBuilder() ?.newBuilder()
?.addQueryParameter("roomId", roomId) ?.addQueryParameter("roomId", roomId)
?.addQueryParameter("sort", "{\"uploadedAt\":-1}")
?.addQueryParameter("offset", offset) ?.addQueryParameter("offset", offset)
?.build() ?.build()
......
...@@ -29,4 +29,8 @@ public class RocketChatAbsoluteUrl implements AbsoluteUrl { ...@@ -29,4 +29,8 @@ public class RocketChatAbsoluteUrl implements AbsoluteUrl {
public String getToken() { public String getToken() {
return token; return token;
} }
public String getBaseUrl() {
return baseUrl;
}
} }
\ No newline at end of file
...@@ -2,11 +2,13 @@ package chat.rocket.android.fragment.chatroom; ...@@ -2,11 +2,13 @@ package chat.rocket.android.fragment.chatroom;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import chat.rocket.core.models.User;
import java.util.List; import java.util.List;
import chat.rocket.android.shared.BaseContract; import chat.rocket.android.shared.BaseContract;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.core.models.Message; import chat.rocket.core.models.Message;
import chat.rocket.core.models.Room; import chat.rocket.core.models.Room;
import chat.rocket.core.models.User;
public interface RoomContract { public interface RoomContract {
...@@ -35,6 +37,12 @@ public interface RoomContract { ...@@ -35,6 +37,12 @@ public interface RoomContract {
void autoloadImages(); void autoloadImages();
void manualLoadImages(); void manualLoadImages();
void onReply(AbsoluteUrl absoluteUrl, String markdown, Message message);
void onCopy(String message);
void showMessageActions(Message message);
} }
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
...@@ -58,5 +66,9 @@ public interface RoomContract { ...@@ -58,5 +66,9 @@ public interface RoomContract {
void onMarkAsRead(); void onMarkAsRead();
void refreshRoom(); void refreshRoom();
void replyMessage(Message message);
void copyMessage(Message message);
} }
} }
...@@ -2,6 +2,9 @@ package chat.rocket.android.fragment.chatroom; ...@@ -2,6 +2,9 @@ package chat.rocket.android.fragment.chatroom;
import android.Manifest; import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
...@@ -26,10 +29,11 @@ import java.util.List; ...@@ -26,10 +29,11 @@ import java.util.List;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.RocketChatApplication;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.activity.room.RoomActivity; import chat.rocket.android.activity.room.RoomActivity;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.fragment.chatroom.dialog.FileUploadProgressDialogFragment; import chat.rocket.android.fragment.chatroom.dialog.FileUploadProgressDialogFragment;
import chat.rocket.android.fragment.chatroom.dialog.MessageOptionsDialogFragment;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment; import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.AbsoluteUrlHelper; import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.FileUploadHelper; import chat.rocket.android.helper.FileUploadHelper;
...@@ -42,6 +46,7 @@ import chat.rocket.android.helper.TextUtils; ...@@ -42,6 +46,7 @@ import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.chatroom.AbstractNewMessageIndicatorManager; import chat.rocket.android.layouthelper.chatroom.AbstractNewMessageIndicatorManager;
import chat.rocket.android.layouthelper.chatroom.MessageFormManager; import chat.rocket.android.layouthelper.chatroom.MessageFormManager;
import chat.rocket.android.layouthelper.chatroom.MessageListAdapter; import chat.rocket.android.layouthelper.chatroom.MessageListAdapter;
import chat.rocket.android.layouthelper.chatroom.MessagePopup;
import chat.rocket.android.layouthelper.chatroom.ModelListAdapter; import chat.rocket.android.layouthelper.chatroom.ModelListAdapter;
import chat.rocket.android.layouthelper.chatroom.PairedMessage; import chat.rocket.android.layouthelper.chatroom.PairedMessage;
import chat.rocket.android.layouthelper.extra_action.AbstractExtraActionItem; import chat.rocket.android.layouthelper.extra_action.AbstractExtraActionItem;
...@@ -55,6 +60,7 @@ import chat.rocket.android.renderer.RocketChatUserStatusProvider; ...@@ -55,6 +60,7 @@ import chat.rocket.android.renderer.RocketChatUserStatusProvider;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.temp.DeafultTempSpotlightRoomCaller; import chat.rocket.android.service.temp.DeafultTempSpotlightRoomCaller;
import chat.rocket.android.service.temp.DefaultTempSpotlightUserCaller; import chat.rocket.android.service.temp.DefaultTempSpotlightUserCaller;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.android.widget.RoomToolbar; import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.android.widget.internal.ExtraActionPickerDialogFragment; import chat.rocket.android.widget.internal.ExtraActionPickerDialogFragment;
import chat.rocket.android.widget.message.MessageFormLayout; import chat.rocket.android.widget.message.MessageFormLayout;
...@@ -90,7 +96,6 @@ import permissions.dispatcher.RuntimePermissions; ...@@ -90,7 +96,6 @@ import permissions.dispatcher.RuntimePermissions;
public class RoomFragment extends AbstractChatRoomFragment implements public class RoomFragment extends AbstractChatRoomFragment implements
OnBackPressListener, OnBackPressListener,
ExtraActionPickerDialogFragment.Callback, ExtraActionPickerDialogFragment.Callback,
ModelListAdapter.OnItemClickListener<PairedMessage>,
ModelListAdapter.OnItemLongClickListener<PairedMessage>, ModelListAdapter.OnItemLongClickListener<PairedMessage>,
RoomContract.View { RoomContract.View {
...@@ -135,7 +140,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -135,7 +140,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
} }
/** /**
* create fragment with roomId. * build fragment with roomId.
*/ */
public static RoomFragment create(String hostname, String roomId) { public static RoomFragment create(String hostname, String roomId) {
Bundle args = new Bundle(); Bundle args = new Bundle();
...@@ -202,7 +207,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -202,7 +207,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageListAdapter = new MessageListAdapter(getContext(), hostname); messageListAdapter = new MessageListAdapter(getContext(), hostname);
messageRecyclerView.setAdapter(messageListAdapter); messageRecyclerView.setAdapter(messageListAdapter);
messageListAdapter.setOnItemClickListener(this);
messageListAdapter.setOnItemLongClickListener(this); messageListAdapter.setOnItemLongClickListener(this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, true); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, true);
...@@ -287,22 +291,9 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -287,22 +291,9 @@ public class RoomFragment extends AbstractChatRoomFragment implements
super.onDestroyView(); super.onDestroyView();
} }
@Override
public void onItemClick(PairedMessage pairedMessage) {
presenter.onMessageSelected(pairedMessage.target);
}
@Override @Override
public boolean onItemLongClick(PairedMessage pairedMessage) { public boolean onItemLongClick(PairedMessage pairedMessage) {
MessageOptionsDialogFragment messageOptionsDialogFragment = MessageOptionsDialogFragment presenter.onMessageSelected(pairedMessage.target);
.create(pairedMessage.target);
messageOptionsDialogFragment.setOnMessageOptionSelectedListener(message -> {
messageOptionsDialogFragment.dismiss();
onEditMessage(message);
});
messageOptionsDialogFragment.show(getChildFragmentManager(), "MessageOptionsDialogFragment");
return true; return true;
} }
...@@ -325,9 +316,9 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -325,9 +316,9 @@ public class RoomFragment extends AbstractChatRoomFragment implements
case R.id.action_favorite_messages: case R.id.action_favorite_messages:
showRoomListFragment(R.id.action_favorite_messages); showRoomListFragment(R.id.action_favorite_messages);
break; break;
// case R.id.action_file_list: case R.id.action_file_list:
// showRoomListFragment(R.id.action_file_list); showRoomListFragment(R.id.action_file_list);
// break; break;
case R.id.action_member_list: case R.id.action_member_list:
showRoomListFragment(R.id.action_member_list); showRoomListFragment(R.id.action_member_list);
break; break;
...@@ -659,6 +650,31 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -659,6 +650,31 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageListAdapter.setAutoloadImages(false); messageListAdapter.setAutoloadImages(false);
} }
@Override
public void onReply(AbsoluteUrl absoluteUrl, String markdown, Message message) {
messageFormManager.setReply(absoluteUrl, markdown, message);
}
@Override
public void onCopy(String message) {
RocketChatApplication context = RocketChatApplication.getInstance();
ClipboardManager clipboardManager =
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("message", message));
}
@Override
public void showMessageActions(Message message) {
Activity context = getActivity();
if (context != null && context instanceof MainActivity) {
MessagePopup.take(message)
.setReplyAction(presenter::replyMessage)
.setEditAction(this::onEditMessage)
.setCopyAction(msg -> onCopy(message.getMessage()))
.showWith(context);
}
}
private void onEditMessage(Message message) { private void onEditMessage(Message message) {
edittingMessage = message; edittingMessage = message;
messageFormManager.setEditMessage(message.getMessage()); messageFormManager.setEditMessage(message.getMessage());
......
...@@ -6,15 +6,12 @@ import android.support.v4.util.Pair; ...@@ -6,15 +6,12 @@ import android.support.v4.util.Pair;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.AbsoluteUrlHelper; import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.SyncState; import chat.rocket.core.SyncState;
import chat.rocket.core.interactors.MessageInteractor; import chat.rocket.core.interactors.MessageInteractor;
...@@ -24,7 +21,9 @@ import chat.rocket.core.models.Settings; ...@@ -24,7 +21,9 @@ import chat.rocket.core.models.Settings;
import chat.rocket.core.models.User; import chat.rocket.core.models.User;
import chat.rocket.core.repositories.RoomRepository; import chat.rocket.core.repositories.RoomRepository;
import chat.rocket.core.repositories.UserRepository; import chat.rocket.core.repositories.UserRepository;
import chat.rocket.android.service.ConnectivityManagerApi; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class RoomPresenter extends BasePresenter<RoomContract.View> public class RoomPresenter extends BasePresenter<RoomContract.View>
implements RoomContract.Presenter { implements RoomContract.Presenter {
...@@ -36,8 +35,9 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -36,8 +35,9 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
private final AbsoluteUrlHelper absoluteUrlHelper; private final AbsoluteUrlHelper absoluteUrlHelper;
private final MethodCallHelper methodCallHelper; private final MethodCallHelper methodCallHelper;
private final ConnectivityManagerApi connectivityManagerApi; private final ConnectivityManagerApi connectivityManagerApi;
private Room currentRoom;
public RoomPresenter(String roomId, public RoomPresenter(String roomId,
UserRepository userRepository, UserRepository userRepository,
MessageInteractor messageInteractor, MessageInteractor messageInteractor,
RoomRepository roomRepository, RoomRepository roomRepository,
...@@ -115,6 +115,50 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -115,6 +115,50 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
if (message.getSyncState() == SyncState.FAILED) { if (message.getSyncState() == SyncState.FAILED) {
view.showMessageSendFailure(message); view.showMessageSendFailure(message);
} }
if (message.getType() == null) {
// If message is not a system message show applicable actions.
view.showMessageActions(message);
}
}
@Override
public void replyMessage(Message message) {
this.absoluteUrlHelper.getRocketChatAbsoluteUrl()
.cache()
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
serverUrl -> {
if (serverUrl.isPresent()) {
String baseUrl = serverUrl.get().getBaseUrl();
view.onReply(serverUrl.get(), buildReplyMarkDown(baseUrl, message), message);
}
},
Logger::report
);
}
@Override
public void copyMessage(Message message) {
view.onCopy(message.getMessage());
}
private String buildReplyMarkDown(String baseUrl, Message message) {
if (currentRoom == null || message.getUser() == null) {
return "";
}
if (currentRoom.isDirectMessage()) {
return String.format("[ ](%s/direct/%s?msg=%s) ", baseUrl,
message.getUser().getUsername(),
message.getId());
} else {
return String.format("[ ](%s/channel/%s?msg=%s) @%s ", baseUrl,
currentRoom.getName(),
message.getId(),
message.getUser().getUsername());
}
} }
@Override @Override
...@@ -233,6 +277,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -233,6 +277,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
} }
private void processRoom(Room room) { private void processRoom(Room room) {
this.currentRoom = room;
view.render(room); view.render(room);
if (room.isDirectMessage()) { if (room.isDirectMessage()) {
......
package chat.rocket.android.fragment.chatroom.list package chat.rocket.android.fragment.chatroom.list
import chat.rocket.core.models.Attachment
import chat.rocket.core.models.Message import chat.rocket.core.models.Message
import chat.rocket.core.models.User import chat.rocket.core.models.User
...@@ -32,7 +33,7 @@ interface RoomListContract { ...@@ -32,7 +33,7 @@ interface RoomListContract {
* @param dataSet The file data set to show. * @param dataSet The file data set to show.
* @param total The total number of files. * @param total The total number of files.
*/ */
fun showFileList(dataSet: ArrayList<String>, total: String) fun showFileList(dataSet: ArrayList<Attachment>, total: String)
/** /**
* Shows a list of members of a room. * Shows a list of members of a room.
......
...@@ -9,8 +9,10 @@ import android.view.View ...@@ -9,8 +9,10 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.layouthelper.chatroom.list.RoomFileListAdapter
import chat.rocket.android.layouthelper.chatroom.list.RoomMemberListAdapter import chat.rocket.android.layouthelper.chatroom.list.RoomMemberListAdapter
import chat.rocket.android.layouthelper.chatroom.list.RoomMessagesAdapter import chat.rocket.android.layouthelper.chatroom.list.RoomMessagesAdapter
import chat.rocket.core.models.Attachment
import chat.rocket.core.models.Message import chat.rocket.core.models.Message
import chat.rocket.core.models.User import chat.rocket.core.models.User
import kotlinx.android.synthetic.main.fragment_room_list.* import kotlinx.android.synthetic.main.fragment_room_list.*
...@@ -84,6 +86,14 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -84,6 +86,14 @@ class RoomListFragment : Fragment(), RoomListContract.View {
userId, userId,
offset) offset)
} }
R.id.action_file_list -> {
presenter.requestFileList(roomId,
roomType,
hostname,
token,
userId,
offset)
}
R.id.action_favorite_messages -> { R.id.action_favorite_messages -> {
presenter.requestFavoriteMessages(roomId, presenter.requestFavoriteMessages(roomId,
roomType, roomType,
...@@ -139,8 +149,23 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -139,8 +149,23 @@ class RoomListFragment : Fragment(), RoomListContract.View {
} }
} }
// TODO (after REST api fixes) override fun showFileList(dataSet: ArrayList<Attachment>, total: String) {
override fun showFileList(dataSet: ArrayList<String>, total: String) {} activity.title = getString(R.string.fragment_room_list_file_list_title, total)
if (recyclerView.adapter == null) {
recyclerView.adapter = RoomFileListAdapter(dataSet)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
recyclerView.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
loadNextDataFromApi(page)
}
})
}
} else {
(recyclerView.adapter as RoomFileListAdapter).addDataSet(dataSet)
}
}
override fun showMemberList(dataSet: ArrayList<User>, total: String) { override fun showMemberList(dataSet: ArrayList<User>, total: String) {
activity.title = getString(R.string.fragment_room_list_member_list_title, total) activity.title = getString(R.string.fragment_room_list_member_list_title, total)
......
...@@ -2,11 +2,13 @@ package chat.rocket.android.fragment.chatroom.list ...@@ -2,11 +2,13 @@ package chat.rocket.android.fragment.chatroom.list
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.util.Log
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.api.rest.RestApiHelper import chat.rocket.android.api.rest.RestApiHelper
import chat.rocket.android.helper.OkHttpHelper import chat.rocket.android.helper.OkHttpHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.core.SyncState import chat.rocket.core.SyncState
import chat.rocket.core.models.Attachment
import chat.rocket.core.models.AttachmentTitle
import chat.rocket.core.models.Message import chat.rocket.core.models.Message
import chat.rocket.core.models.User import chat.rocket.core.models.User
import okhttp3.Call import okhttp3.Call
...@@ -98,13 +100,43 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -98,13 +100,43 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
}) })
} }
// TODO (after the REST api fixes)
override fun requestFileList(roomId: String, override fun requestFileList(roomId: String,
roomType: String, roomType: String,
hostname: String, hostname: String,
token: String, token: String,
userId: String, userId: String,
offset: Int) {} offset: Int) {
view.showWaitingView(true)
OkHttpHelper.getClient()
.newCall(RestApiHelper.getRequestForFileList(roomId,
roomType,
hostname,
token,
userId,
offset.toString()))
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!call.isCanceled) {
val message = e.message
if (message != null) {
showErrorMessage(message)
}
}
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val result = response.body()?.string()
if (result != null) {
handleFilesJson(result, hostname)
}
} else {
showErrorMessage(response.message())
}
}
})
}
override fun requestMemberList(roomId: String, override fun requestMemberList(roomId: String,
roomType: String, roomType: String,
...@@ -159,27 +191,13 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -159,27 +191,13 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
val messageJsonObject = messagesJSONArray.getJSONObject(it) val messageJsonObject = messagesJSONArray.getJSONObject(it)
val userJsonObject = messageJsonObject.getJSONObject("u") val userJsonObject = messageJsonObject.getJSONObject("u")
val timestampString = messageJsonObject.optString("ts")
val timestamp = if (timestampString.isBlank()) {
0
} else {
Timestamp.valueOf(timestampString.replace("T", " ").replace("Z", "")).time
}
val editedAtString = messageJsonObject.optString("_updatedAt")
val editedAt = if (editedAtString.isBlank()) {
0
} else {
Timestamp.valueOf(editedAtString.replace("T", " ").replace("Z", "")).time
}
Message.builder() Message.builder()
.setId(messageJsonObject.optString("_id")) .setId(messageJsonObject.optString("_id"))
.setRoomId(messageJsonObject.optString("rid")) .setRoomId(messageJsonObject.optString("rid"))
.setMessage(messageJsonObject.optString("msg")) .setMessage(messageJsonObject.optString("msg"))
.setUser(getUserFromJsonObject(userJsonObject)) .setUser(getUserFromJsonObject(userJsonObject))
.setTimestamp(timestamp) .setTimestamp(getLongTimestamp(messageJsonObject.optString("ts")))
.setEditedAt(editedAt) .setEditedAt(getLongTimestamp(messageJsonObject.optString("_updatedAt")))
.setGroupable(messageJsonObject.optBoolean("groupable")) .setGroupable(messageJsonObject.optBoolean("groupable"))
.setSyncState(SyncState.SYNCED) .setSyncState(SyncState.SYNCED)
.build() .build()
...@@ -202,6 +220,52 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -202,6 +220,52 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
} }
} }
private fun handleFilesJson(json: String, hostname: String) {
try {
val jsonObject = JSONObject(json)
val filesJsonArray = jsonObject.getJSONArray("files")
val total = filesJsonArray.length()
val dataSet = ArrayList<Attachment>(total)
(0 until total).mapTo(dataSet) {
val fileJsonObject = filesJsonArray.getJSONObject(it)
val fileLink = UrlHelper.getAttachmentLink(hostname, fileJsonObject.optString("_id"), fileJsonObject.optString("name"))
val attachmentTitle = AttachmentTitle.builder()
.setTitle(fileJsonObject.optString("name"))
.setLink(fileLink)
.setDownloadLink(fileLink)
.build()
val attachment = Attachment.builder()
val type = fileJsonObject.optString("type")
when {
type.startsWith("image") -> attachment.setImageUrl(fileLink)
type.startsWith("audio") -> attachment.setAudioUrl(fileLink)
type.startsWith("video") -> attachment.setVideoUrl(fileLink)
}
attachment.setCollapsed(false)
.setAttachmentTitle(attachmentTitle)
.setTimestamp(getSafeTimestamp(fileJsonObject.optString("uploadedAt")))
.build()
}
if (dataSet.isEmpty() && !hasItem) {
showEmptyViewMessage(context.getString(R.string.fragment_room_list_no_file_list_to_show))
} else {
if (dataSet.isNotEmpty()) {
hasItem = true
showFileList(dataSet, jsonObject.optString("total"))
}
}
} catch (exception: JSONException) {
showInvalidRequest()
}
}
private fun handleMembersJson(json: String) { private fun handleMembersJson(json: String) {
try { try {
val jsonObject = JSONObject(json) val jsonObject = JSONObject(json)
...@@ -221,7 +285,7 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -221,7 +285,7 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
showMemberList(dataSet, jsonObject.optString("total")) showMemberList(dataSet, jsonObject.optString("total"))
} }
} }
}catch (exception: JSONException) { } catch (exception: JSONException) {
showInvalidRequest() showInvalidRequest()
} }
} }
...@@ -236,6 +300,17 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -236,6 +300,17 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
.build() .build()
} }
private fun getLongTimestamp(timestamp: String): Long {
return if (timestamp.isNotBlank()) {
Timestamp.valueOf(getSafeTimestamp(timestamp)).time
} else {
0
}
}
private fun getSafeTimestamp(timestamp: String): String =
timestamp.replace("T", " ").replace("Z", "")
private fun showPinnedMessageList(dataSet: ArrayList<Message>, total: String) { private fun showPinnedMessageList(dataSet: ArrayList<Message>, total: String) {
mainHandler.post { mainHandler.post {
view.showWaitingView(false) view.showWaitingView(false)
...@@ -250,6 +325,13 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) : ...@@ -250,6 +325,13 @@ class RoomListPresenter(val context: Context, val view: RoomListContract.View) :
} }
} }
private fun showFileList(dataSet: ArrayList<Attachment>, total: String) {
mainHandler.post {
view.showWaitingView(false)
view.showFileList(dataSet, total)
}
}
private fun showMemberList(dataSet: ArrayList<User>, total: String) { private fun showMemberList(dataSet: ArrayList<User>, total: String) {
mainHandler.post { mainHandler.post {
view.showWaitingView(false) view.showWaitingView(false)
......
...@@ -100,7 +100,7 @@ public class LoginFragment extends AbstractServerConfigFragment implements Login ...@@ -100,7 +100,7 @@ public class LoginFragment extends AbstractServerConfigFragment implements Login
try { try {
fragment = info.fragmentClass.newInstance(); fragment = info.fragmentClass.newInstance();
} catch (Exception exception) { } catch (Exception exception) {
RCLog.w(exception, "failed to create new Fragment"); RCLog.w(exception, "failed to build new Fragment");
} }
if (fragment != null) { if (fragment != null) {
Bundle args = new Bundle(); Bundle args = new Bundle();
......
...@@ -30,7 +30,7 @@ public class UserRegistrationDialogFragment extends DialogFragment { ...@@ -30,7 +30,7 @@ public class UserRegistrationDialogFragment extends DialogFragment {
} }
/** /**
* create UserRegistrationDialogFragment with auto-detect email/username. * build UserRegistrationDialogFragment with auto-detect email/username.
*/ */
public static UserRegistrationDialogFragment create(String hostname, public static UserRegistrationDialogFragment create(String hostname,
String usernameOrEmail, String password) { String usernameOrEmail, String password) {
...@@ -42,7 +42,7 @@ public class UserRegistrationDialogFragment extends DialogFragment { ...@@ -42,7 +42,7 @@ public class UserRegistrationDialogFragment extends DialogFragment {
} }
/** /**
* create UserRegistrationDialogFragment. * build UserRegistrationDialogFragment.
*/ */
public static UserRegistrationDialogFragment create(String hostname, public static UserRegistrationDialogFragment create(String hostname,
String username, String email, String username, String email,
......
...@@ -67,7 +67,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -67,7 +67,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
public SidebarMainFragment() {} public SidebarMainFragment() {}
/** /**
* create SidebarMainFragment with hostname. * build SidebarMainFragment with hostname.
*/ */
public static SidebarMainFragment create(String hostname) { public static SidebarMainFragment create(String hostname) {
Bundle args = new Bundle(); Bundle args = new Bundle();
......
...@@ -8,7 +8,8 @@ object UrlHelper { ...@@ -8,7 +8,8 @@ object UrlHelper {
* @param uri The URI. * @param uri The URI.
* @return The URI whit no scheme (HTTP or HTTPS) * @return The URI whit no scheme (HTTP or HTTPS)
*/ */
fun removeUriScheme(uri: String) = uri.replace("http://", "").replace("https://", "") fun removeUriScheme(uri: String) =
uri.replace("http://", "").replace("https://", "")
/** /**
* Returns the hostname with the security protocol (scheme) HTTPS. * Returns the hostname with the security protocol (scheme) HTTPS.
...@@ -17,7 +18,7 @@ object UrlHelper { ...@@ -17,7 +18,7 @@ object UrlHelper {
* @return The hostname with the security protocol (scheme) HTTPS. * @return The hostname with the security protocol (scheme) HTTPS.
*/ */
fun getSafeHostname(hostname: String): String = fun getSafeHostname(hostname: String): String =
"https://" + hostname.replace("http://", "").replace("https://", "") "https://" + removeUriScheme(hostname)
/** /**
* Returns an URL with no spaces and inverted slashes. * Returns an URL with no spaces and inverted slashes.
...@@ -25,17 +26,17 @@ object UrlHelper { ...@@ -25,17 +26,17 @@ object UrlHelper {
* @param url The URL. * @param url The URL.
* @return The URL with no spaces and inverted slashes. * @return The URL with no spaces and inverted slashes.
*/ */
fun getUrl(url: String) = fun getSafeUrl(url: String) =
url.replace(" ", "%20").replace("\\", "") url.replace(" ", "%20").replace("\\", "")
/** /**
* Returns an URL for a file. * Returns an attachment link.
* *
* @param path The path to the file. * @param hostname The hostname.
* @param userId The user ID. * @param fileId The file ID.
* @param token The token. * @param fileName The file name.
* @return The URL for a file * @return The attachment link.
*/ */
fun getUrlForFile(path: String, userId: String, token: String): String = fun getAttachmentLink(hostname: String, fileId: String, fileName: String): String =
"https://" + removeUriScheme(getUrl(path)) + "?rc_uid=$userId" + "&rc_token=$token" getSafeUrl(getSafeHostname(hostname) + "/file-upload/" + fileId + "/" + fileName)
} }
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom package chat.rocket.android.layouthelper.chatroom
import chat.rocket.android.widget.AbsoluteUrl
import chat.rocket.android.widget.message.MessageFormLayout import chat.rocket.android.widget.message.MessageFormLayout
import chat.rocket.core.models.Message
class MessageFormManager(private val messageFormLayout: MessageFormLayout, val callback: MessageFormLayout.ExtraActionSelectionClickListener) { class MessageFormManager(private val messageFormLayout: MessageFormLayout, val callback: MessageFormLayout.ExtraActionSelectionClickListener) {
private var sendMessageCallback: SendMessageCallback? = null private var sendMessageCallback: SendMessageCallback? = null
private var replyMarkDown: String = ""
init { init {
messageFormLayout.setExtraActionSelectionClickListener(callback) messageFormLayout.setExtraActionSelectionClickListener(callback)
...@@ -31,8 +34,20 @@ class MessageFormManager(private val messageFormLayout: MessageFormLayout, val c ...@@ -31,8 +34,20 @@ class MessageFormManager(private val messageFormLayout: MessageFormLayout, val c
messageFormLayout.isEnabled = enable messageFormLayout.isEnabled = enable
} }
fun setReply(absoluteUrl: AbsoluteUrl, replyMarkDown: String, message: Message) {
this.replyMarkDown = replyMarkDown
messageFormLayout.setReplyContent(absoluteUrl, message)
messageFormLayout.setReplyCancelListener({
this.replyMarkDown = ""
messageFormLayout.clearReplyContent()
messageFormLayout.hideKeyboard()
})
}
private fun sendMessage(message: String) { private fun sendMessage(message: String) {
sendMessageCallback?.onSubmitText(message) val finalMessage = if (replyMarkDown.isNotEmpty()) "$replyMarkDown $message" else message
replyMarkDown = ""
sendMessageCallback?.onSubmitText(finalMessage)
} }
interface SendMessageCallback { interface SendMessageCallback {
......
package chat.rocket.android.layouthelper.chatroom;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.util.Pair;
import android.support.v7.app.AlertDialog;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.RocketChatApplication;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.Logger;
import chat.rocket.core.interactors.EditMessageInteractor;
import chat.rocket.core.interactors.PermissionInteractor;
import chat.rocket.core.models.Message;
import chat.rocket.core.repositories.MessageRepository;
import chat.rocket.core.repositories.PermissionRepository;
import chat.rocket.core.repositories.PublicSettingRepository;
import chat.rocket.core.repositories.RoomRepository;
import chat.rocket.core.repositories.RoomRoleRepository;
import chat.rocket.core.repositories.UserRepository;
import chat.rocket.persistence.realm.repositories.RealmMessageRepository;
import chat.rocket.persistence.realm.repositories.RealmPermissionRepository;
import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRoleRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
public class MessagePopup {
private static volatile MessagePopup singleton = null;
private static final Action REPLY_ACTION_INFO = new Action("Reply", null, true);
private static final Action EDIT_ACTION_INFO = new Action("Edit", null, true);
private static final Action COPY_ACTION_INFO = new Action("Copy", null, true);
private final List<Action> defaultActions = new ArrayList<>(3);
private final List<Action> otherActions = new ArrayList<>();
private Message message;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private MessagePopup(Message message) {
this.message = message;
}
private void showAvailableActionsOnly(Context context) {
RocketChatCache cache = new RocketChatCache(RocketChatApplication.getInstance());
String hostname = cache.getSelectedServerHostname();
EditMessageInteractor editMessageInteractor = getEditMessageInteractor(hostname);
MessageRepository messageRepository = new RealmMessageRepository(hostname);
Disposable disposable = messageRepository.getById(singleton.message.getId())
.flatMap(it -> {
if (!it.isPresent()) {
return Single.just(Pair.<Message, Boolean>create(null, false));
}
Message message = it.get();
return Single.zip(
Single.just(message),
editMessageInteractor.isAllowed(message),
Pair::create
);
})
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
pair -> {
EDIT_ACTION_INFO.allowed = pair.second;
List<Action> allActions = singleton.defaultActions;
List<Action> allowedActions = new ArrayList<>(3);
for (int i = 0; i < allActions.size(); i++) {
Action action = allActions.get(i);
if (action.allowed) {
allowedActions.add(action);
}
}
allowedActions.addAll(singleton.otherActions);
CharSequence[] items = new CharSequence[allowedActions.size()];
for (int j = 0; j < items.length; j++) {
items[j] = allowedActions.get(j).actionName;
}
new AlertDialog.Builder(context)
.setItems(items, (dialog, index) -> {
Action action = allowedActions.get(index);
ActionListener actionListener = action.actionListener;
if (actionListener != null) {
actionListener.execute(singleton.message);
}
})
.setOnCancelListener(dialog -> compositeDisposable.clear())
.setOnDismissListener(dialog1 -> compositeDisposable.clear())
.setTitle("Message")
.create()
.show();
},
Logger::report
);
compositeDisposable.add(disposable);
}
private void addDefaultActions() {
singleton.defaultActions.add(REPLY_ACTION_INFO);
singleton.defaultActions.add(EDIT_ACTION_INFO);
singleton.defaultActions.add(COPY_ACTION_INFO);
}
public static MessagePopup take(Message message) {
if (singleton == null) {
synchronized (MessagePopup.class) {
if (singleton == null) {
singleton = new Builder(message).build();
singleton.addDefaultActions();
}
}
}
singleton.message = message;
singleton.otherActions.clear();
return singleton;
}
private Action getActionIfExists(Action action) {
if (singleton.otherActions.contains(action)) {
return singleton.otherActions.get(singleton.otherActions.indexOf(action));
}
if (singleton.defaultActions.contains(action)) {
return singleton.defaultActions.get(singleton.defaultActions.indexOf(action));
}
return null;
}
public MessagePopup addAction(@NonNull CharSequence actionName, ActionListener actionListener) {
List<Action> actions = singleton.otherActions;
Action newAction = new Action(actionName, actionListener, true);
Action existingAction = getActionIfExists(newAction);
if (existingAction != null) {
existingAction.actionListener = actionListener;
} else {
actions.add(newAction);
}
return singleton;
}
public MessagePopup setReplyAction(ActionListener actionListener) {
REPLY_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setEditAction(ActionListener actionListener) {
EDIT_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setCopyAction(ActionListener actionListener) {
COPY_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public void showWith(Context context) {
showAvailableActionsOnly(context);
}
private EditMessageInteractor getEditMessageInteractor(String hostname) {
UserRepository userRepository = new RealmUserRepository(hostname);
RoomRoleRepository roomRoleRepository = new RealmRoomRoleRepository(hostname);
PermissionRepository permissionRepository = new RealmPermissionRepository(hostname);
PermissionInteractor permissionInteractor = new PermissionInteractor(
userRepository,
roomRoleRepository,
permissionRepository
);
MessageRepository messageRepository = new RealmMessageRepository(hostname);
RoomRepository roomRepository = new RealmRoomRepository(hostname);
PublicSettingRepository publicSettingRepository = new RealmPublicSettingRepository(hostname);
return new EditMessageInteractor(
permissionInteractor,
userRepository,
messageRepository,
roomRepository,
publicSettingRepository
);
}
private static class Builder {
private final Message message;
Builder(Message message) {
if (message == null) {
throw new IllegalArgumentException("Message must not be null");
}
this.message = message;
}
public MessagePopup build() {
Message message = this.message;
return new MessagePopup(message);
}
}
public static class Action {
private CharSequence actionName;
private ActionListener actionListener;
private boolean allowed;
public Action(CharSequence actionName, ActionListener actionListener, boolean allowed) {
this.actionName = actionName;
this.actionListener = actionListener;
this.allowed = allowed;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Action that = (Action) o;
return actionName.equals(that.actionName);
}
@Override
public int hashCode() {
return actionName.hashCode();
}
}
public interface ActionListener {
void execute(Message message);
}
}
...@@ -4,14 +4,19 @@ import android.support.v7.widget.RecyclerView ...@@ -4,14 +4,19 @@ import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.widget.message.RocketChatMessageLayout import chat.rocket.android.helper.DateTime
import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
import chat.rocket.core.models.Attachment
import kotlinx.android.synthetic.main.day.view.*
import kotlinx.android.synthetic.main.item_room_file.view.* import kotlinx.android.synthetic.main.item_room_file.view.*
import java.sql.Timestamp
/** /**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17. * Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/ */
class RoomFileListAdapter(private var dataSet: List<String>) : RecyclerView.Adapter<RoomFileListAdapter.ViewHolder>() { class RoomFileListAdapter(private var dataSet: List<Attachment>) : RecyclerView.Adapter<RoomFileListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_room_file, parent, false) val view = LayoutInflater.from(parent.context).inflate(R.layout.item_room_file, parent, false)
...@@ -19,17 +24,22 @@ class RoomFileListAdapter(private var dataSet: List<String>) : RecyclerView.Adap ...@@ -19,17 +24,22 @@ class RoomFileListAdapter(private var dataSet: List<String>) : RecyclerView.Adap
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.fileNameLink.setText(dataSet[position]) val attachment = dataSet[position]
holder.newDay.text = DateTime.fromEpocMs(Timestamp.valueOf(attachment.timestamp).time, DateTime.Format.DATE)
holder.attachment.appendAttachmentView(attachment, true, false)
} }
override fun getItemCount(): Int = dataSet.size override fun getItemCount(): Int = dataSet.size
fun setDataSet(dataSet: List<String>) { fun addDataSet(dataSet: List<Attachment>) {
this.dataSet = dataSet val previousDataSetSize = this.dataSet.size
notifyDataSetChanged() this.dataSet += dataSet
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
} }
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val fileNameLink : RocketChatMessageLayout = itemView.fileLink val newDay: TextView = itemView.day
val attachment: RocketChatMessageAttachmentsLayout = itemView.attachment
} }
} }
\ No newline at end of file
...@@ -88,7 +88,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -88,7 +88,7 @@ public class PushNotificationHandler implements PushConstants {
if ((message != null && message.length() != 0) || if ((message != null && message.length() != 0) ||
(title != null && title.length() != 0)) { (title != null && title.length() != 0)) {
Log.d(LOG_TAG, "create notification"); Log.d(LOG_TAG, "build notification");
if (title == null || title.isEmpty()) { if (title == null || title.isEmpty()) {
extras.putString(TITLE, getAppName(context)); extras.putString(TITLE, getAppName(context));
...@@ -191,7 +191,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -191,7 +191,7 @@ public class PushNotificationHandler implements PushConstants {
private void createActions(Context context, Bundle extras, NotificationCompat.Builder builder, private void createActions(Context context, Bundle extras, NotificationCompat.Builder builder,
Resources resources, String packageName, int notId) { Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "create actions: with in-line"); Log.d(LOG_TAG, "build actions: with in-line");
String actions = extras.getString(ACTIONS); String actions = extras.getString(ACTIONS);
if (actions == null) { if (actions == null) {
return; return;
...@@ -256,7 +256,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -256,7 +256,7 @@ public class PushNotificationHandler implements PushConstants {
RemoteInput remoteInput; RemoteInput remoteInput;
if (inline) { if (inline) {
Log.d(LOG_TAG, "create remote input"); Log.d(LOG_TAG, "build remote input");
String replyLabel = "Enter your reply here"; String replyLabel = "Enter your reply here";
remoteInput = new RemoteInput.Builder(INLINE_REPLY) remoteInput = new RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel) .setLabel(replyLabel)
...@@ -287,7 +287,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -287,7 +287,7 @@ public class PushNotificationHandler implements PushConstants {
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH) @RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
private void createActions(Context context, Bundle extras, Notification.Builder builder, private void createActions(Context context, Bundle extras, Notification.Builder builder,
Resources resources, String packageName, int notId) { Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "create actions: with in-line"); Log.d(LOG_TAG, "build actions: with in-line");
String actions = extras.getString(ACTIONS); String actions = extras.getString(ACTIONS);
if (actions == null) { if (actions == null) {
return; return;
...@@ -352,7 +352,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -352,7 +352,7 @@ public class PushNotificationHandler implements PushConstants {
android.app.RemoteInput remoteInput; android.app.RemoteInput remoteInput;
if (inline) { if (inline) {
Log.d(LOG_TAG, "create remote input"); Log.d(LOG_TAG, "build remote input");
String replyLabel = "Enter your reply here"; String replyLabel = "Enter your reply here";
remoteInput = new android.app.RemoteInput.Builder(INLINE_REPLY) remoteInput = new android.app.RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel) .setLabel(replyLabel)
......
...@@ -109,7 +109,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -109,7 +109,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
/** /**
* create new Thread. * build new Thread.
*/ */
@DebugLog @DebugLog
public static Single<RocketChatWebSocketThread> getStarted(Context appContext, String hostname) { public static Single<RocketChatWebSocketThread> getStarted(Context appContext, String hostname) {
...@@ -241,11 +241,11 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -241,11 +241,11 @@ public class RocketChatWebSocketThread extends HandlerThread {
private Single<Boolean> prepareDDPClient() { private Single<Boolean> prepareDDPClient() {
// TODO: temporarily replaced checkIfConnectionAlive() call for this single checking if ddpClient is // TODO: temporarily replaced checkIfConnectionAlive() call for this single checking if ddpClient is
// null or not. In case it is, create a new client, otherwise just keep connecting with existing one. // null or not. In case it is, build a new client, otherwise just keep connecting with existing one.
return Single.just(ddpClient != null) return Single.just(ddpClient != null)
.doOnSuccess(alive -> { .doOnSuccess(alive -> {
if (!alive) { if (!alive) {
RCLog.d("DDPClient#create"); RCLog.d("DDPClient#build");
ddpClient = DDPClientWrapper.create(hostname); ddpClient = DDPClientWrapper.create(hostname);
} }
}); });
...@@ -392,7 +392,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -392,7 +392,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
) )
); );
} else { } else {
// if we don't have any session then just create the observers and register normally // if we don't have any session then just build the observers and register normally
createObserversAndRegister(); createObserversAndRegister();
} }
} }
......
...@@ -4,14 +4,13 @@ import android.content.Context; ...@@ -4,14 +4,13 @@ import android.content.Context;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import chat.rocket.android.log.RCLog;
import io.reactivex.disposables.CompositeDisposable;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.persistence.realm.models.ddp.RealmRoom; import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.Registrable; import chat.rocket.android.service.Registrable;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import io.reactivex.disposables.CompositeDisposable;
public abstract class AbstractRocketChatCacheObserver implements Registrable { public abstract class AbstractRocketChatCacheObserver implements Registrable {
private final Context context; private final Context context;
...@@ -50,6 +49,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable { ...@@ -50,6 +49,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
compositeDisposable.add( compositeDisposable.add(
new RocketChatCache(context) new RocketChatCache(context)
.getSelectedRoomIdPublisher() .getSelectedRoomIdPublisher()
.filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.subscribe(this::updateRoomIdWith, RCLog::e) .subscribe(this::updateRoomIdWith, RCLog::e)
); );
......
...@@ -3,20 +3,20 @@ ...@@ -3,20 +3,20 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="@dimen/margin_16"> android:paddingRight="@dimen/margin_16"
android:paddingStart="@dimen/margin_16"
android:paddingLeft="@dimen/margin_16"
android:paddingEnd="@dimen/margin_16"
android:paddingBottom="@dimen/margin_16">
<chat.rocket.android.widget.message.RocketChatMessageLayout <include
android:id="@+id/fileLink" android:id="@+id/dayLayout"
android:layout_width="wrap_content" layout="@layout/day" />
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16" />
<View <chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
android:layout_width="0dp" android:id="@+id/attachment"
android:layout_height="1dp" android:layout_width="match_parent"
android:layout_weight="1" android:layout_height="wrap_content"
android:background="@color/colorDivider" android:layout_marginTop="@dimen/margin_16"
android:layout_marginTop="@dimen/margin_8" app:layout_constraintTop_toBottomOf="@+id/dayLayout" />
app:layout_constraintTop_toBottomOf="@+id/fileLink" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
\ No newline at end of file
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
android:title="@string/menu_favorite_messages" android:title="@string/menu_favorite_messages"
app:showAsAction="never" /> app:showAsAction="never" />
<!--<item android:id="@+id/action_file_list"--> <item android:id="@+id/action_file_list"
<!--android:title="@string/menu_file_list"--> android:title="@string/menu_file_list"
<!--app:showAsAction="never" />--> app:showAsAction="never" />
<item android:id="@+id/action_member_list" <item android:id="@+id/action_member_list"
android:title="@string/menu_member_list" android:title="@string/menu_member_list"
......
...@@ -21,20 +21,17 @@ class UrlHelperTest { ...@@ -21,20 +21,17 @@ class UrlHelperTest {
@Test @Test
fun getUrlTest() { fun getUrlTest() {
assertEquals("https://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("https://demo.rocket.chat/GENERAL/file.txt")) assertEquals("https://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getSafeUrl("https://demo.rocket.chat/GENERAL/file.txt"))
assertEquals("http://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("http://demo.rocket.chat/GENERAL/file.txt")) assertEquals("http://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getSafeUrl("http://demo.rocket.chat/GENERAL/file.txt"))
assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("demo.rocket.chat/GENERAL/file.txt")) assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getSafeUrl("demo.rocket.chat/GENERAL/file.txt"))
assertEquals("demo.rocket.chat/GENERAL/a%20sample%20file.txt", UrlHelper.getUrl("demo.rocket.chat/GENERAL/a sample file.txt")) assertEquals("demo.rocket.chat/GENERAL/a%20sample%20file.txt", UrlHelper.getSafeUrl("demo.rocket.chat/GENERAL/a sample file.txt"))
assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("demo.rocket.chat\\/GENERAL\\/file.txt")) assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getSafeUrl("demo.rocket.chat\\/GENERAL\\/file.txt"))
} }
@Test @Test
fun getUrlForFileTest() { fun getAttachmentLinkTest() {
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("https://demo.rocket.chat/GENERAL/file.txt","userId", "token")) assertEquals("https://demo.rocket.chat/file-upload/aFileId/aFileName.txt", UrlHelper.getAttachmentLink("https://demo.rocket.chat", "aFileId", "aFileName.txt"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("http://demo.rocket.chat/GENERAL/file.txt","userId", "token")) assertEquals("https://demo.rocket.chat/file-upload/aFileId/aFileName.txt", UrlHelper.getAttachmentLink("http://demo.rocket.chat", "aFileId", "aFileName.txt"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat/GENERAL/file.txt","userId", "token")) assertEquals("https://demo.rocket.chat/file-upload/aFileId/aFileName.txt", UrlHelper.getAttachmentLink("demo.rocket.chat", "aFileId", "aFileName.txt"))
assertEquals("https://demo.rocket.chat/GENERAL/a%20sample%20file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat/GENERAL/a sample file.txt","userId", "token"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat\\/GENERAL\\/file.txt","userId", "token"))
} }
} }
\ No newline at end of file
...@@ -6,7 +6,9 @@ import android.support.graphics.drawable.VectorDrawableCompat ...@@ -6,7 +6,9 @@ import android.support.graphics.drawable.VectorDrawableCompat
import chat.rocket.android.widget.R import chat.rocket.android.widget.R
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.drawable.ProgressBarDrawable import com.facebook.drawee.drawable.ProgressBarDrawable
import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.drawee.generic.GenericDraweeHierarchy import com.facebook.drawee.generic.GenericDraweeHierarchy
import com.facebook.drawee.generic.RoundingParams
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
object FrescoHelper { object FrescoHelper {
...@@ -31,6 +33,8 @@ object FrescoHelper { ...@@ -31,6 +33,8 @@ object FrescoHelper {
val hierarchy: GenericDraweeHierarchy = draweeView.hierarchy val hierarchy: GenericDraweeHierarchy = draweeView.hierarchy
hierarchy.setPlaceholderImage(VectorDrawableCompat.create(draweeView.resources, R.drawable.image_dummy, null)) hierarchy.setPlaceholderImage(VectorDrawableCompat.create(draweeView.resources, R.drawable.image_dummy, null))
hierarchy.setFailureImage(VectorDrawableCompat.create(draweeView.resources, R.drawable.image_error, null)) hierarchy.setFailureImage(VectorDrawableCompat.create(draweeView.resources, R.drawable.image_error, null))
hierarchy.roundingParams = RoundingParams().setCornersRadii(5F, 5F, 5F, 5F)
hierarchy.actualImageScaleType = ScalingUtils.ScaleType.FIT_CENTER
hierarchy.setProgressBarImage(ProgressBarDrawable()) hierarchy.setProgressBarImage(ProgressBarDrawable())
val controller = Fresco.newDraweeControllerBuilder() val controller = Fresco.newDraweeControllerBuilder()
......
...@@ -51,7 +51,7 @@ public class MarkDown { ...@@ -51,7 +51,7 @@ public class MarkDown {
private static final Pattern LINK_PATTERN = Pattern.compile( private static final Pattern LINK_PATTERN = Pattern.compile(
"\\[([^\\]]+)\\]\\(((?:http|https):\\/\\/[^\\)]+)\\)", Pattern.MULTILINE); "\\[(.*?)\\]\\(((https?):\\/\\/[-a-zA-Z0-9+&@#\\/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#\\/%=~_|]?)\\)", Pattern.MULTILINE);
private static void highlightLink1(SpannableString inputText) { private static void highlightLink1(SpannableString inputText) {
final Matcher matcher = LINK_PATTERN.matcher(inputText); final Matcher matcher = LINK_PATTERN.matcher(inputText);
while (matcher.find()) { while (matcher.find()) {
......
...@@ -4,6 +4,7 @@ import android.annotation.TargetApi; ...@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v13.view.inputmethod.InputContentInfoCompat; import android.support.v13.view.inputmethod.InputContentInfoCompat;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
...@@ -15,10 +16,20 @@ import android.view.ViewGroup; ...@@ -15,10 +16,20 @@ import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.android.widget.R; import chat.rocket.android.widget.R;
import chat.rocket.android.widget.helper.DebouncingOnClickListener; import chat.rocket.android.widget.helper.DebouncingOnClickListener;
import chat.rocket.android.widget.helper.FrescoHelper;
import chat.rocket.core.models.Attachment;
import chat.rocket.core.models.AttachmentTitle;
import chat.rocket.core.models.Message;
public class MessageFormLayout extends LinearLayout { public class MessageFormLayout extends LinearLayout {
...@@ -27,6 +38,12 @@ public class MessageFormLayout extends LinearLayout { ...@@ -27,6 +38,12 @@ public class MessageFormLayout extends LinearLayout {
private ImageButton attachButton; private ImageButton attachButton;
private ImageButton sendButton; private ImageButton sendButton;
private RelativeLayout replyBar;
private ImageView replyCancelButton;
private SimpleDraweeView replyThumb;
private TextView replyMessageText;
private TextView replyUsernameText;
private ExtraActionSelectionClickListener extraActionSelectionClickListener; private ExtraActionSelectionClickListener extraActionSelectionClickListener;
private SubmitTextListener submitTextListener; private SubmitTextListener submitTextListener;
private ImageKeyboardEditText.OnCommitContentListener listener; private ImageKeyboardEditText.OnCommitContentListener listener;
...@@ -65,6 +82,12 @@ public class MessageFormLayout extends LinearLayout { ...@@ -65,6 +82,12 @@ public class MessageFormLayout extends LinearLayout {
} }
}); });
replyCancelButton = composer.findViewById(R.id.reply_cancel);
replyMessageText = composer.findViewById(R.id.reply_message);
replyUsernameText = composer.findViewById(R.id.reply_username);
replyThumb = composer.findViewById(R.id.reply_thumb);
replyBar = composer.findViewById(R.id.reply_bar);
sendButton = composer.findViewById(R.id.button_send); sendButton = composer.findViewById(R.id.button_send);
sendButton.setOnClickListener(new DebouncingOnClickListener() { sendButton.setOnClickListener(new DebouncingOnClickListener() {
...@@ -73,6 +96,7 @@ public class MessageFormLayout extends LinearLayout { ...@@ -73,6 +96,7 @@ public class MessageFormLayout extends LinearLayout {
String messageText = getText(); String messageText = getText();
if (messageText.length() > 0 && submitTextListener != null) { if (messageText.length() > 0 && submitTextListener != null) {
submitTextListener.onSubmitText(messageText); submitTextListener.onSubmitText(messageText);
clearReplyContent();
} }
} }
}); });
...@@ -118,6 +142,20 @@ public class MessageFormLayout extends LinearLayout { ...@@ -118,6 +142,20 @@ public class MessageFormLayout extends LinearLayout {
addView(composer); addView(composer);
} }
public void clearReplyContent() {
replyBar.setVisibility(View.GONE);
replyThumb.setVisibility(View.GONE);
replyMessageText.setText("");
replyUsernameText.setText("");
}
public void showReplyThumb() {
replyThumb.setVisibility(View.VISIBLE);
}
public void setReplyCancelListener(OnClickListener onClickListener) {
replyCancelButton.setOnClickListener(onClickListener);
}
public EditText getEditText() { public EditText getEditText() {
return (EditText) composer.findViewById(R.id.editor); return (EditText) composer.findViewById(R.id.editor);
} }
...@@ -154,10 +192,7 @@ public class MessageFormLayout extends LinearLayout { ...@@ -154,10 +192,7 @@ public class MessageFormLayout extends LinearLayout {
if (text.length() > 0) { if (text.length() > 0) {
editor.setSelection(text.length()); editor.setSelection(text.length());
InputMethodManager inputMethodManager = (InputMethodManager) editor.getContext() requestFocusAndShowKeyboard();
.getSystemService(Context.INPUT_METHOD_SERVICE);
editor.requestFocus();
inputMethodManager.showSoftInput(editor, 0);
} }
} }
}); });
...@@ -173,6 +208,61 @@ public class MessageFormLayout extends LinearLayout { ...@@ -173,6 +208,61 @@ public class MessageFormLayout extends LinearLayout {
this.listener = listener; this.listener = listener;
} }
public void setReplyContent(@NonNull AbsoluteUrl absoluteUrl, @NonNull Message message) {
String text = message.getMessage();
replyUsernameText.setText(message.getUser().getUsername());
if (!TextUtils.isEmpty(text)) {
replyMessageText.setText(text);
} else {
if (message.getAttachments() != null && message.getAttachments().size() > 0) {
Attachment attachment = message.getAttachments().get(0);
AttachmentTitle attachmentTitle = attachment.getAttachmentTitle();
String imageUrl = null;
if (attachment.getImageUrl() != null) {
imageUrl = absoluteUrl.from(attachment.getImageUrl());
}
if (attachmentTitle != null) {
text = attachmentTitle.getTitle();
}
if (TextUtils.isEmpty(text)) {
text = "Unknown";
}
if (imageUrl != null) {
FrescoHelper.INSTANCE.loadImageWithCustomization(replyThumb, imageUrl);
showReplyThumb();
}
replyMessageText.setText(text);
}
}
replyBar.setVisibility(View.VISIBLE);
requestFocusAndShowKeyboard();
}
public void hideKeyboard() {
final EditText editor = getEditor();
editor.post(new Runnable() {
@Override
public void run() {
InputMethodManager inputMethodManager = (InputMethodManager) editor.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(editor.getWindowToken(), 0);
}
});
}
private void requestFocusAndShowKeyboard() {
final EditText editor = getEditor();
editor.post(new Runnable() {
@Override
public void run() {
InputMethodManager inputMethodManager = (InputMethodManager) editor.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
editor.requestFocus();
inputMethodManager.showSoftInput(editor, 0);
}
});
}
private void animateHide(final View view) { private void animateHide(final View view) {
view.animate().scaleX(0).scaleY(0).setDuration(150).withEndAction(new Runnable() { view.animate().scaleX(0).scaleY(0).setDuration(150).withEndAction(new Runnable() {
@Override @Override
......
...@@ -71,21 +71,21 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout { ...@@ -71,21 +71,21 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
return; return;
} }
this.attachments = attachments; this.attachments = attachments;
removeAllViews();
for (int i = 0, size = attachments.size(); i < size; i++) { for (int i = 0, size = attachments.size(); i < size; i++) {
appendAttachmentView(attachments.get(i), autoloadImages); appendAttachmentView(attachments.get(i), autoloadImages, true);
} }
} }
private void appendAttachmentView(Attachment attachment, boolean autoloadImages) { public void appendAttachmentView(Attachment attachment, boolean autoloadImages, boolean showAttachmentStrip) {
if (attachment == null) { if (attachment == null) {
return; return;
} }
removeAllViews();
View attachmentView = inflater.inflate(R.layout.message_inline_attachment, this, false); View attachmentView = inflater.inflate(R.layout.message_inline_attachment, this, false);
colorizeAttachmentBar(attachment, attachmentView); colorizeAttachmentBar(attachment, attachmentView, showAttachmentStrip);
showAuthorAttachment(attachment, attachmentView); showAuthorAttachment(attachment, attachmentView);
showTitleAttachment(attachment, attachmentView); showTitleAttachment(attachment, attachmentView);
showReferenceAttachment(attachment, attachmentView); showReferenceAttachment(attachment, attachmentView);
...@@ -97,19 +97,23 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout { ...@@ -97,19 +97,23 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
addView(attachmentView); addView(attachmentView);
} }
private void colorizeAttachmentBar(Attachment attachment, View attachmentView) { private void colorizeAttachmentBar(Attachment attachment, View attachmentView, boolean showAttachmentStrip) {
final View attachmentStrip = attachmentView.findViewById(R.id.attachment_strip); final View attachmentStrip = attachmentView.findViewById(R.id.attachment_strip);
final String colorString = attachment.getColor(); if (showAttachmentStrip) {
if (TextUtils.isEmpty(colorString)) { final String colorString = attachment.getColor();
attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line); if (TextUtils.isEmpty(colorString)) {
return; attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line);
} return;
}
try { try {
attachmentStrip.setBackgroundColor(Color.parseColor(colorString)); attachmentStrip.setBackgroundColor(Color.parseColor(colorString));
} catch (Exception e) { } catch (Exception e) {
attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line); attachmentStrip.setBackgroundResource(R.color.inline_attachment_quote_line);
}
} else {
attachmentStrip.setVisibility(GONE);
} }
} }
...@@ -204,8 +208,7 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout { ...@@ -204,8 +208,7 @@ public class RocketChatMessageAttachmentsLayout extends LinearLayout {
} }
} }
private void showImageAttachment(Attachment attachment, View attachmentView, private void showImageAttachment(Attachment attachment, View attachmentView, boolean autoloadImages) {
boolean autoloadImages) {
final View imageContainer = attachmentView.findViewById(R.id.image_container); final View imageContainer = attachmentView.findViewById(R.id.image_container);
if (attachment.getImageUrl() == null) { if (attachment.getImageUrl() == null) {
imageContainer.setVisibility(GONE); imageContainer.setVisibility(GONE);
......
<!-- drawable/close.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</vector>
\ No newline at end of file
<!-- drawable/reply.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M10,9V5L3,12L10,19V14.9C15,14.9 18.5,16.5 21,20C20,15 17,10 10,9Z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke <stroke
android:color="@color/inline_attachment_box_outline" android:width="1dp"
android:width="1dp"/> android:color="@color/inline_attachment_box_outline" />
<solid android:color="@color/inline_attachment_box_background"/>
<corners android:radius="2dp"/> <solid
android:color="@color/inline_attachment_box_background" />
<corners
android:radius="2dp" />
</shape> </shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:minHeight="48dp" android:layout_height="wrap_content"
android:background="@drawable/top_shadow" android:background="@drawable/top_shadow"
tools:context="chat.rocket.android.widget.message.MessageFormLayout"> android:minHeight="48dp"
tools:context="chat.rocket.android.widget.message.MessageFormLayout">
<chat.rocket.android.widget.message.ImageKeyboardEditText
android:id="@+id/editor" <RelativeLayout
android:layout_width="0dp" android:id="@+id/reply_bar"
android:layout_height="wrap_content" android:layout_width="0dp"
android:layout_marginLeft="16dp" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginBottom="8dp"
android:inputType="textCapSentences|textMultiLine" android:layout_marginEnd="8dp"
android:hint="@string/message_composer_message_hint" android:layout_marginLeft="8dp"
android:textSize="14sp" android:layout_marginRight="8dp"
android:minLines="1" android:layout_marginStart="8dp"
android:maxLines="4" android:layout_marginTop="8dp"
android:background="@null" android:visibility="gone"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/keyboard_container"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/container" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="8dp"
<FrameLayout tools:layout_editor_absoluteY="8dp"
android:id="@+id/container" tools:visibility="visible">
android:layout_width="wrap_content"
android:layout_height="wrap_content" <android.support.v7.widget.AppCompatImageView
android:layout_marginLeft="16dp" android:id="@+id/reply_icon"
android:layout_marginStart="16dp" android:layout_width="24dp"
android:layout_marginRight="16dp" android:layout_height="24dp"
android:layout_marginEnd="16dp" android:layout_marginRight="8dp"
app:layout_constraintTop_toTopOf="@+id/editor" android:layout_marginEnd="8dp"
app:layout_constraintRight_toRightOf="parent" android:layout_centerVertical="true"
app:layout_constraintLeft_toRightOf="@+id/editor" android:layout_alignParentStart="true"
app:layout_constraintBottom_toBottomOf="@+id/editor"> android:layout_alignParentLeft="true"
android:adjustViewBounds="true"
<ImageButton app:srcCompat="@drawable/ic_reply"
android:id="@+id/button_attach" app:tint="@color/color_accent" />
android:layout_width="wrap_content"
android:layout_height="wrap_content" <android.support.v7.widget.AppCompatImageView
android:tint="@color/color_icon_composer" android:id="@+id/reply_cancel"
app:srcCompat="@drawable/ic_attach_file_black_24dp" android:layout_width="24dp"
android:background="?attr/selectableItemBackgroundBorderless"/> android:layout_height="24dp"
android:layout_marginLeft="8dp"
<ImageButton android:layout_marginStart="8dp"
android:id="@+id/button_send" android:layout_centerVertical="true"
android:layout_width="wrap_content" android:layout_alignParentEnd="true"
android:layout_height="wrap_content" android:layout_alignParentRight="true"
android:tint="@color/color_accent" android:adjustViewBounds="true"
app:srcCompat="@drawable/ic_send_black_24dp" app:srcCompat="@drawable/ic_close"
android:background="?attr/selectableItemBackgroundBorderless"/> app:tint="@color/color_icon_composer" />
</FrameLayout>
<TextView
android:id="@+id/reply_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/reply_username"
android:layout_toLeftOf="@+id/reply_cancel"
android:layout_toStartOf="@id/reply_cancel"
android:layout_toRightOf="@+id/reply_thumb"
android:layout_toEndOf="@+id/reply_thumb"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/color_accent"
android:textStyle="bold"
tools:text="jane.doe" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/reply_thumb"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:layout_toRightOf="@+id/reply_icon"
android:layout_toEndOf="@+id/reply_icon"
android:layout_alignBottom="@+id/reply_message"
android:layout_alignTop="@+id/reply_username"
android:layout_centerVertical="true"
android:visibility="gone"
fresco:actualImageScaleType="fitCenter" />
<TextView
android:id="@+id/reply_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/reply_username"
android:layout_toLeftOf="@+id/reply_cancel"
android:layout_toStartOf="@id/reply_cancel"
android:layout_toRightOf="@+id/reply_thumb"
android:layout_toEndOf="@+id/reply_thumb"
android:ellipsize="end"
android:maxLines="1"
tools:text="Message" />
</RelativeLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/keyboard_container"
android:layout_width="0dp"
android:layout_height="48dp"
android:background="@drawable/top_shadow"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reply_bar">
<chat.rocket.android.widget.message.ImageKeyboardEditText
android:id="@+id/editor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:background="@null"
android:hint="@string/message_composer_message_hint"
android:inputType="textCapSentences|textMultiLine"
android:maxLines="4"
android:minLines="1"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/container"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/editor"
app:layout_constraintLeft_toRightOf="@+id/editor"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/editor">
<ImageButton
android:id="@+id/button_attach"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="@color/color_icon_composer"
app:srcCompat="@drawable/ic_attach_file_black_24dp" />
<ImageButton
android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="@color/color_accent"
app:srcCompat="@drawable/ic_send_black_24dp" />
</FrameLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:fresco="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="4dp" android:paddingBottom="4dp"
android:paddingBottom="4dp"> android:paddingTop="4dp">
<View <View
android:id="@+id/attachment_strip" android:id="@+id/attachment_strip"
android:layout_width="3dp" android:layout_width="3dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginRight="5dp" android:layout_marginEnd="5dp"
android:background="@color/inline_attachment_quote_line" /> android:layout_marginRight="5dp"
android:background="@color/inline_attachment_quote_line" />
<LinearLayout <LinearLayout
android:id="@+id/attachment_content" android:id="@+id/attachment_content"
android:layout_width="0px" android:layout_width="0px"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/author_box" android:id="@+id/author_box"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:layout_marginBottom="8dp"
android:orientation="horizontal" android:gravity="center_vertical"
android:layout_marginBottom="8dp"> android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/author_icon" android:id="@+id/author_icon"
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
tools:src="@drawable/circle_black" fresco:actualImageScaleType="fitCenter"
fresco:actualImageScaleType="fitCenter" /> tools:src="@drawable/circle_black" />
<android.support.v4.widget.Space <android.support.v4.widget.Space
android:layout_width="8dp" android:layout_width="8dp"
android:layout_height="8dp" /> android:layout_height="8dp" />
<TextView <TextView
android:id="@+id/author_name" android:id="@+id/author_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title.Link" android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title.Link"
tools:text="Bradley Hilton" /> tools:text="Bradley Hilton" />
<android.support.v4.widget.Space <android.support.v4.widget.Space
android:layout_width="8dp" android:layout_width="8dp"
android:layout_height="8dp" /> android:layout_height="8dp" />
<TextView <TextView
android:id="@+id/timestamp" android:id="@+id/timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="14:53" /> tools:text="14:53" />
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title" android:background="?attr/selectableItemBackground"
android:background="?attr/selectableItemBackground" android:textAppearance="@style/TextAppearance.RocketChat.MessageAttachment.Title"
tools:text="Attachment Example" /> tools:text="Attachment Example" />
<LinearLayout <LinearLayout
android:id="@+id/ref_box" android:id="@+id/ref_box"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:layout_marginBottom="8dp"
android:orientation="horizontal" android:gravity="center_vertical"
android:layout_marginBottom="8dp"> android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/thumb" android:id="@+id/thumb"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
tools:src="@drawable/circle_black" fresco:actualImageScaleType="fitCenter"
fresco:actualImageScaleType="fitCenter" /> tools:src="@drawable/circle_black" />
<android.support.v4.widget.Space <android.support.v4.widget.Space
android:layout_width="8dp" android:layout_width="8dp"
android:layout_height="8dp" /> android:layout_height="8dp" />
<TextView <TextView
android:id="@+id/text" android:id="@+id/text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="Bradley Hilton" /> tools:text="Bradley Hilton" />
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout
android:id="@+id/image_container" android:id="@+id/image_container"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:padding="5dp"
android:background="@drawable/inline_attachment_background">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="match_parent" android:layout_width="200dp"
android:layout_height="150dp" android:layout_height="200dp"/>
android:layout_marginBottom="8dp"
fresco:actualImageScaleType="fitStart" />
<TextView <TextView
android:id="@+id/image_load" android:id="@+id/image_load"
android:layout_width="match_parent" android:layout_width="200dp"
android:layout_height="150dp" android:layout_height="200dp"
android:gravity="center_horizontal|bottom" android:gravity="center_horizontal|bottom"
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:text="@string/click_to_load" /> android:text="@string/click_to_load" />
</FrameLayout> </FrameLayout>
<!-- audio --> <!-- audio -->
<!-- video --> <!-- video -->
</LinearLayout> </LinearLayout>
</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