From bd5b37aa5e5fa3b7a6c7e2e7eabd5ad4f12d7d1a Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 23 Apr 2018 20:39:37 +0200 Subject: [PATCH] Sending support and bunch of improvements Signed-off-by: Mario Danic --- .../java/com/nextcloud/talk/api/NcApi.java | 2 +- .../talk/controllers/ChatController.java | 108 ++++++++++++++---- .../talk/controllers/ContactsController.java | 45 +++++--- .../talk/models/json/chat/ChatMessage.java | 9 +- .../com/nextcloud/talk/utils/TimeUtils.java | 52 --------- app/src/main/res/layout/controller_chat.xml | 8 +- app/src/main/res/values/strings.xml | 7 +- 7 files changed, 127 insertions(+), 104 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index d03e6d1ef..56f16d316 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -277,7 +277,7 @@ public interface NcApi { */ @FormUrlEncoded - @PUT + @POST Observable sendChatMessage(@Header("Authorization") String authorization, @Url String url, @FieldMap Map fields); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java index 9dbbad960..28c54e5f3 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java @@ -24,6 +24,8 @@ package com.nextcloud.talk.controllers; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -41,6 +43,7 @@ import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.call.CallOverall; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.chat.ChatOverall; +import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; @@ -49,9 +52,11 @@ import com.stfalcon.chatkit.commons.ImageLoader; import com.stfalcon.chatkit.messages.MessageInput; import com.stfalcon.chatkit.messages.MessagesList; import com.stfalcon.chatkit.messages.MessagesListAdapter; +import com.stfalcon.chatkit.utils.DateFormatter; import org.parceler.Parcels; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,7 +72,8 @@ import io.reactivex.schedulers.Schedulers; import retrofit2.Response; @AutoInjector(NextcloudTalkApplication.class) -public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener { +public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener, + MessagesListAdapter.Formatter { @Inject NcApi ncApi; @Inject @@ -87,6 +93,7 @@ public class ChatController extends BaseController implements MessagesListAdapte private int globalLastKnownPastMessageId = -1; private MessagesListAdapter adapter; + private RecyclerView.LayoutManager layoutManager; public ChatController(Bundle args) { super(args); @@ -106,23 +113,37 @@ public class ChatController extends BaseController implements MessagesListAdapte super.onViewBound(view); NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); - adapter = new MessagesListAdapter<>(currentUser.getUserId(), new ImageLoader() { - @Override - public void loadImage(ImageView imageView, String url) { - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(url) - .centerInside() - .override(imageView.getMeasuredWidth(), imageView.getMeasuredHeight()) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(imageView); - } - }); + boolean adapterWasNull = false; + + if (adapter == null) { + adapterWasNull = true; + adapter = new MessagesListAdapter<>(currentUser.getUserId(), new ImageLoader() { + @Override + public void loadImage(ImageView imageView, String url) { + GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) + .asBitmap() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .load(url) + .centerInside() + .override(imageView.getMeasuredWidth(), imageView.getMeasuredHeight()) + .apply(RequestOptions.bitmapTransform(new CircleCrop())) + .into(imageView); + } + }); + } messagesList.setAdapter(adapter); adapter.setLoadMoreListener(this); - joinRoomWithPassword(null); + adapter.setDateHeadersFormatter(this::format); + + messageInput.setInputListener(input -> { + sendMessage(input.toString()); + return true; + }); + + if (adapterWasNull) { + joinRoomWithPassword(null); + } } @Override @@ -186,10 +207,43 @@ public class ChatController extends BaseController implements MessagesListAdapte }); } + private void sendMessage(String message) { + Map fieldMap = new HashMap<>(); + fieldMap.put("message", message); + fieldMap.put("actorDisplayName", currentUser.getDisplayName()); + + + ncApi.sendChatMessage(ApiUtils.getCredentials(currentUser.getUserId(), currentUser.getToken()), + ApiUtils.getUrlForChat(currentUser.getBaseUrl(), roomToken), fieldMap) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(3, observable -> inChat) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(GenericOverall genericOverall) { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } private void pullChatMessages(int lookIntoFuture) { Map fieldMap = new HashMap<>(); fieldMap.put("lookIntoFuture", lookIntoFuture); - fieldMap.put("limit", 2); + fieldMap.put("limit", 25); int lastKnown; if (lookIntoFuture == 1) { @@ -285,17 +339,14 @@ public class ChatController extends BaseController implements MessagesListAdapte } } - adapter.addToEnd(chatMessageList, false); } else { + LinearLayoutManager layoutManager = (LinearLayoutManager) messagesList.getLayoutManager(); for (int i = 0; i < chatMessageList.size(); i++) { chatMessageList.get(i).setBaseUrl(currentUser.getBaseUrl()); - if (i == chatMessageList.size() - 1) { - adapter.addToStart(chatMessageList.get(i), true); - } else { - adapter.addToStart(chatMessageList.get(i), false); - } + adapter.addToStart(chatMessageList.get(i), + layoutManager.findLastVisibleItemPosition() <= adapter.getItemCount() - 3); } globalLastKnownFutureMessageId = Integer.parseInt(response.headers().get("X-Chat-Last-Given")); @@ -315,4 +366,15 @@ public class ChatController extends BaseController implements MessagesListAdapte pullChatMessages(0); } } -} + + + @Override + public String format(Date date) { + if (DateFormatter.isToday(date)) { + return getResources().getString(R.string.nc_date_header_today); + } else if (DateFormatter.isYesterday(date)) { + return getResources().getString(R.string.nc_date_header_yesterday); + } else { + return DateFormatter.format(date, DateFormatter.Template.STRING_DAY_MONTH_YEAR); + } + }} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index 48be112b4..e47681649 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -137,7 +137,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ @BindView(R.id.clear_button) Button clearButton; - private UserEntity userEntity; + private UserEntity currentUser; private Disposable contactsQueryDisposable; private Disposable cacheQueryDisposable; private FlexibleAdapter adapter; @@ -196,9 +196,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ FlipView.resetLayoutAnimationDelay(true, 1000L); FlipView.stopLayoutAnimation(); - userEntity = userUtils.getCurrentUser(); + currentUser = userUtils.getCurrentUser(); - if (userEntity == null && + if (currentUser == null && getParentController() != null && getParentController().getRouter() != null) { getParentController().getRouter().setRoot((RouterTransaction.with(new ServerSelectionController()) .pushChangeHandler(new HorizontalChangeHandler()) @@ -210,7 +210,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ adapter.setNotifyChangeOfUnfilteredItems(true) .setMode(SelectableAdapter.Mode.MULTI); - if (userEntity != null) { + if (currentUser != null) { fetchData(); } } @@ -248,9 +248,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ public void onDoneButtonClick() { if (!isPublicCall && adapter.getSelectedPositions().size() == 1) { - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1", + RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), "1", ((UserItem) adapter.getItem(adapter.getSelectedPositions().get(0))).getModel().getUserId(), null); - ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), + ncApi.createRoom(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) @@ -266,7 +266,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ Intent callIntent = new Intent(getActivity(), CallActivity.class); Bundle bundle = new Bundle(); bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken()); - bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity)); + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(currentUser)); callIntent.putExtras(bundle); startActivity(callIntent); new Handler().postDelayed(() -> getRouter().popCurrentController(), 100); @@ -402,10 +402,10 @@ public class ContactsController extends BaseController implements SearchView.OnQ contactItems = new ArrayList<>(); userHeaderItems = new HashMap<>(); - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(userEntity.getBaseUrl(), + RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(), ""); contactsQueryDisposable = ncApi.getContactsWithSearchParam( - ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), + ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) @@ -424,7 +424,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ Participant participant; for (Sharee sharee : shareeHashSet) { - if (!sharee.getValue().getShareWith().equals(userEntity.getUsername())) { + if (!sharee.getValue().getShareWith().equals(currentUser.getUsername())) { participant = new Participant(); participant.setName(sharee.getLabel()); String headerTitle; @@ -438,7 +438,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ } participant.setUserId(sharee.getValue().getShareWith()); - contactItems.add(new UserItem(participant, userEntity, + contactItems.add(new UserItem(participant, currentUser, userHeaderItems.get(headerTitle))); } @@ -492,7 +492,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ if (getParentController() != null && getParentController().getRouter() != null) { getParentController().getRouter().pushController((RouterTransaction.with - (new WebViewLoginController(userEntity.getBaseUrl(), + (new WebViewLoginController(currentUser.getBaseUrl(), true)) .pushChangeHandler(new VerticalChangeHandler()) .popChangeHandler(new VerticalChangeHandler()))); @@ -700,9 +700,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ if (adapter.getItem(position) instanceof UserItem) { if (!isNewConversationView) { UserItem userItem = (UserItem) adapter.getItem(position); - RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1", + RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), "1", userItem.getModel().getUserId(), null); - ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), + ncApi.createRoom(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) @@ -715,12 +715,21 @@ public class ContactsController extends BaseController implements SearchView.OnQ @Override public void onNext(RoomOverall roomOverall) { if (getActivity() != null) { - Intent callIntent = new Intent(getActivity(), CallActivity.class); + Intent conversationIntent = new Intent(getActivity(), CallActivity.class); Bundle bundle = new Bundle(); bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken()); - bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity)); - callIntent.putExtras(bundle); - startActivity(callIntent); + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(currentUser)); + conversationIntent.putExtras(bundle); + + if (currentUser.hasSpreedCapabilityWithName("chat-v2")) { + bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, + roomOverall.getOcs().getData().getDisplayName()); + getParentController().getRouter().pushController((RouterTransaction.with(new ChatController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler()))); + } else { + startActivity(conversationIntent); + } } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java index 6b4f54c66..c9bc1f20f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java @@ -22,7 +22,6 @@ package com.nextcloud.talk.models.json.chat; import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.TimeUtils; import com.stfalcon.chatkit.commons.models.IMessage; import com.stfalcon.chatkit.commons.models.IUser; @@ -95,17 +94,13 @@ public class ChatMessage implements IMessage { @Override public String getAvatar() { - if ("guests".equals(actorType)) { - return null; - } else { - return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, false); - } + return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, false); } }; } @Override public Date getCreatedAt() { - return TimeUtils.getDateCurrentTimeZone(timestamp); + return new Date(timestamp * 1000L); } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java b/app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java deleted file mode 100644 index b7795bdb8..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017-2018 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.utils; - -import android.util.Log; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -public class TimeUtils { - private static final String TAG = "TimeUtils"; - - public static Date getDateCurrentTimeZone(long timestamp) { - try{ - Calendar calendar = Calendar.getInstance(); - TimeZone tz = Calendar.getInstance().getTimeZone(); - calendar.setTimeInMillis(timestamp * 1000); - calendar.add(Calendar.MILLISECOND, tz.getOffset(calendar.getTimeInMillis())); - Date currentTimeZone = calendar.getTime(); - return currentTimeZone; - } catch (Exception e) { - Log.d(TAG, "Failed to convert time to local timezone"); - } - return new Date(); - } - - public static String getDateStringCurrentTimeZone(long timestamp) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); - return simpleDateFormat.format(getDateCurrentTimeZone(timestamp)); - } -} diff --git a/app/src/main/res/layout/controller_chat.xml b/app/src/main/res/layout/controller_chat.xml index abe8cae6f..9b6dcdba9 100644 --- a/app/src/main/res/layout/controller_chat.xml +++ b/app/src/main/res/layout/controller_chat.xml @@ -29,6 +29,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/input" + app:outcomingDefaultBubbleColor="@color/colorPrimary" + app:outcomingDefaultBubbleSelectedColor="@color/colorPrimaryDark" app:textAutoLink="all"/> - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eed976687..32fd153d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -164,5 +164,10 @@ Millions of users use Nextcloud daily at businesses and homes around the world. Learn more on https://nextcloud.com/talk Find Nextcloud on https://nextcloud.com - Enter a messageā€¦ + + + Enter a messageā€¦ + Yesterday + Today +