Sending support and bunch of improvements

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2018-04-23 20:39:37 +02:00
parent 09c4b83f82
commit bd5b37aa5e
7 changed files with 127 additions and 104 deletions

View file

@ -277,7 +277,7 @@ public interface NcApi {
*/ */
@FormUrlEncoded @FormUrlEncoded
@PUT @POST
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url, Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url,
@FieldMap Map<String, String> fields); @FieldMap Map<String, String> fields);

View file

@ -24,6 +24,8 @@ package com.nextcloud.talk.controllers;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; 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.call.CallOverall;
import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.chat.ChatMessage;
import com.nextcloud.talk.models.json.chat.ChatOverall; 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.ApiUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils; 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.MessageInput;
import com.stfalcon.chatkit.messages.MessagesList; import com.stfalcon.chatkit.messages.MessagesList;
import com.stfalcon.chatkit.messages.MessagesListAdapter; import com.stfalcon.chatkit.messages.MessagesListAdapter;
import com.stfalcon.chatkit.utils.DateFormatter;
import org.parceler.Parcels; import org.parceler.Parcels;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -67,7 +72,8 @@ import io.reactivex.schedulers.Schedulers;
import retrofit2.Response; import retrofit2.Response;
@AutoInjector(NextcloudTalkApplication.class) @AutoInjector(NextcloudTalkApplication.class)
public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener { public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener,
MessagesListAdapter.Formatter<Date> {
@Inject @Inject
NcApi ncApi; NcApi ncApi;
@Inject @Inject
@ -87,6 +93,7 @@ public class ChatController extends BaseController implements MessagesListAdapte
private int globalLastKnownPastMessageId = -1; private int globalLastKnownPastMessageId = -1;
private MessagesListAdapter<ChatMessage> adapter; private MessagesListAdapter<ChatMessage> adapter;
private RecyclerView.LayoutManager layoutManager;
public ChatController(Bundle args) { public ChatController(Bundle args) {
super(args); super(args);
@ -106,23 +113,37 @@ public class ChatController extends BaseController implements MessagesListAdapte
super.onViewBound(view); super.onViewBound(view);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
adapter = new MessagesListAdapter<>(currentUser.getUserId(), new ImageLoader() { boolean adapterWasNull = false;
@Override
public void loadImage(ImageView imageView, String url) { if (adapter == null) {
GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) adapterWasNull = true;
.asBitmap() adapter = new MessagesListAdapter<>(currentUser.getUserId(), new ImageLoader() {
.diskCacheStrategy(DiskCacheStrategy.NONE) @Override
.load(url) public void loadImage(ImageView imageView, String url) {
.centerInside() GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext())
.override(imageView.getMeasuredWidth(), imageView.getMeasuredHeight()) .asBitmap()
.apply(RequestOptions.bitmapTransform(new CircleCrop())) .diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView); .load(url)
} .centerInside()
}); .override(imageView.getMeasuredWidth(), imageView.getMeasuredHeight())
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(imageView);
}
});
}
messagesList.setAdapter(adapter); messagesList.setAdapter(adapter);
adapter.setLoadMoreListener(this); adapter.setLoadMoreListener(this);
joinRoomWithPassword(null); adapter.setDateHeadersFormatter(this::format);
messageInput.setInputListener(input -> {
sendMessage(input.toString());
return true;
});
if (adapterWasNull) {
joinRoomWithPassword(null);
}
} }
@Override @Override
@ -186,10 +207,43 @@ public class ChatController extends BaseController implements MessagesListAdapte
}); });
} }
private void sendMessage(String message) {
Map<String, String> 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<GenericOverall>() {
@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) { private void pullChatMessages(int lookIntoFuture) {
Map<String, Integer> fieldMap = new HashMap<>(); Map<String, Integer> fieldMap = new HashMap<>();
fieldMap.put("lookIntoFuture", lookIntoFuture); fieldMap.put("lookIntoFuture", lookIntoFuture);
fieldMap.put("limit", 2); fieldMap.put("limit", 25);
int lastKnown; int lastKnown;
if (lookIntoFuture == 1) { if (lookIntoFuture == 1) {
@ -285,17 +339,14 @@ public class ChatController extends BaseController implements MessagesListAdapte
} }
} }
adapter.addToEnd(chatMessageList, false); adapter.addToEnd(chatMessageList, false);
} else { } else {
LinearLayoutManager layoutManager = (LinearLayoutManager) messagesList.getLayoutManager();
for (int i = 0; i < chatMessageList.size(); i++) { for (int i = 0; i < chatMessageList.size(); i++) {
chatMessageList.get(i).setBaseUrl(currentUser.getBaseUrl()); chatMessageList.get(i).setBaseUrl(currentUser.getBaseUrl());
if (i == chatMessageList.size() - 1) { adapter.addToStart(chatMessageList.get(i),
adapter.addToStart(chatMessageList.get(i), true); layoutManager.findLastVisibleItemPosition() <= adapter.getItemCount() - 3);
} else {
adapter.addToStart(chatMessageList.get(i), false);
}
} }
globalLastKnownFutureMessageId = Integer.parseInt(response.headers().get("X-Chat-Last-Given")); globalLastKnownFutureMessageId = Integer.parseInt(response.headers().get("X-Chat-Last-Given"));
@ -315,4 +366,15 @@ public class ChatController extends BaseController implements MessagesListAdapte
pullChatMessages(0); 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);
}
}}

View file

@ -137,7 +137,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
@BindView(R.id.clear_button) @BindView(R.id.clear_button)
Button clearButton; Button clearButton;
private UserEntity userEntity; private UserEntity currentUser;
private Disposable contactsQueryDisposable; private Disposable contactsQueryDisposable;
private Disposable cacheQueryDisposable; private Disposable cacheQueryDisposable;
private FlexibleAdapter adapter; private FlexibleAdapter adapter;
@ -196,9 +196,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
FlipView.resetLayoutAnimationDelay(true, 1000L); FlipView.resetLayoutAnimationDelay(true, 1000L);
FlipView.stopLayoutAnimation(); FlipView.stopLayoutAnimation();
userEntity = userUtils.getCurrentUser(); currentUser = userUtils.getCurrentUser();
if (userEntity == null && if (currentUser == null &&
getParentController() != null && getParentController().getRouter() != null) { getParentController() != null && getParentController().getRouter() != null) {
getParentController().getRouter().setRoot((RouterTransaction.with(new ServerSelectionController()) getParentController().getRouter().setRoot((RouterTransaction.with(new ServerSelectionController())
.pushChangeHandler(new HorizontalChangeHandler()) .pushChangeHandler(new HorizontalChangeHandler())
@ -210,7 +210,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
adapter.setNotifyChangeOfUnfilteredItems(true) adapter.setNotifyChangeOfUnfilteredItems(true)
.setMode(SelectableAdapter.Mode.MULTI); .setMode(SelectableAdapter.Mode.MULTI);
if (userEntity != null) { if (currentUser != null) {
fetchData(); fetchData();
} }
} }
@ -248,9 +248,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
public void onDoneButtonClick() { public void onDoneButtonClick() {
if (!isPublicCall && adapter.getSelectedPositions().size() == 1) { 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); ((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()) retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -266,7 +266,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
Intent callIntent = new Intent(getActivity(), CallActivity.class); Intent callIntent = new Intent(getActivity(), CallActivity.class);
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken()); 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); callIntent.putExtras(bundle);
startActivity(callIntent); startActivity(callIntent);
new Handler().postDelayed(() -> getRouter().popCurrentController(), 100); new Handler().postDelayed(() -> getRouter().popCurrentController(), 100);
@ -402,10 +402,10 @@ public class ContactsController extends BaseController implements SearchView.OnQ
contactItems = new ArrayList<>(); contactItems = new ArrayList<>();
userHeaderItems = new HashMap<>(); userHeaderItems = new HashMap<>();
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(userEntity.getBaseUrl(), RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForContactsSearch(currentUser.getBaseUrl(),
""); "");
contactsQueryDisposable = ncApi.getContactsWithSearchParam( contactsQueryDisposable = ncApi.getContactsWithSearchParam(
ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -424,7 +424,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
Participant participant; Participant participant;
for (Sharee sharee : shareeHashSet) { for (Sharee sharee : shareeHashSet) {
if (!sharee.getValue().getShareWith().equals(userEntity.getUsername())) { if (!sharee.getValue().getShareWith().equals(currentUser.getUsername())) {
participant = new Participant(); participant = new Participant();
participant.setName(sharee.getLabel()); participant.setName(sharee.getLabel());
String headerTitle; String headerTitle;
@ -438,7 +438,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
} }
participant.setUserId(sharee.getValue().getShareWith()); participant.setUserId(sharee.getValue().getShareWith());
contactItems.add(new UserItem(participant, userEntity, contactItems.add(new UserItem(participant, currentUser,
userHeaderItems.get(headerTitle))); userHeaderItems.get(headerTitle)));
} }
@ -492,7 +492,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (getParentController() != null && if (getParentController() != null &&
getParentController().getRouter() != null) { getParentController().getRouter() != null) {
getParentController().getRouter().pushController((RouterTransaction.with getParentController().getRouter().pushController((RouterTransaction.with
(new WebViewLoginController(userEntity.getBaseUrl(), (new WebViewLoginController(currentUser.getBaseUrl(),
true)) true))
.pushChangeHandler(new VerticalChangeHandler()) .pushChangeHandler(new VerticalChangeHandler())
.popChangeHandler(new VerticalChangeHandler()))); .popChangeHandler(new VerticalChangeHandler())));
@ -700,9 +700,9 @@ public class ContactsController extends BaseController implements SearchView.OnQ
if (adapter.getItem(position) instanceof UserItem) { if (adapter.getItem(position) instanceof UserItem) {
if (!isNewConversationView) { if (!isNewConversationView) {
UserItem userItem = (UserItem) adapter.getItem(position); UserItem userItem = (UserItem) adapter.getItem(position);
RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(userEntity.getBaseUrl(), "1", RetrofitBucket retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), "1",
userItem.getModel().getUserId(), null); userItem.getModel().getUserId(), null);
ncApi.createRoom(ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken()), ncApi.createRoom(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) retrofitBucket.getUrl(), retrofitBucket.getQueryMap())
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -715,12 +715,21 @@ public class ContactsController extends BaseController implements SearchView.OnQ
@Override @Override
public void onNext(RoomOverall roomOverall) { public void onNext(RoomOverall roomOverall) {
if (getActivity() != null) { if (getActivity() != null) {
Intent callIntent = new Intent(getActivity(), CallActivity.class); Intent conversationIntent = new Intent(getActivity(), CallActivity.class);
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken()); 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); conversationIntent.putExtras(bundle);
startActivity(callIntent);
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);
}
} }
} }

View file

@ -22,7 +22,6 @@ package com.nextcloud.talk.models.json.chat;
import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.nextcloud.talk.utils.ApiUtils; 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.IMessage;
import com.stfalcon.chatkit.commons.models.IUser; import com.stfalcon.chatkit.commons.models.IUser;
@ -95,17 +94,13 @@ public class ChatMessage implements IMessage {
@Override @Override
public String getAvatar() { public String getAvatar() {
if ("guests".equals(actorType)) { return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, false);
return null;
} else {
return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, false);
}
} }
}; };
} }
@Override @Override
public Date getCreatedAt() { public Date getCreatedAt() {
return TimeUtils.getDateCurrentTimeZone(timestamp); return new Date(timestamp * 1000L);
} }
} }

View file

@ -1,52 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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));
}
}

View file

@ -29,6 +29,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@+id/input" android:layout_above="@+id/input"
app:outcomingDefaultBubbleColor="@color/colorPrimary"
app:outcomingDefaultBubbleSelectedColor="@color/colorPrimaryDark"
app:textAutoLink="all"/> app:textAutoLink="all"/>
<View <View
@ -45,10 +47,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:maxLength="1000" android:maxLength="1000"
app:inputButtonDefaultBgColor="@color/colorPrimary"
app:inputButtonDefaultBgPressedColor="@color/colorPrimaryDark"
app:inputButtonHeight="30dp" app:inputButtonHeight="30dp"
app:inputButtonMargin="16dp" app:inputButtonMargin="16dp"
app:inputButtonWidth="30dp" app:inputButtonWidth="30dp"
app:inputHint="@string/hint_enter_a_message" app:inputHint="@string/nc_hint_enter_a_message"
app:inputTextColor="@color/black" app:inputTextColor="@color/black"
app:inputTextSize="18sp"/> app:inputTextSize="18sp"/>

View file

@ -164,5 +164,10 @@ Millions of users use Nextcloud daily at businesses and homes around the world.
Learn more on https://nextcloud.com/talk Learn more on https://nextcloud.com/talk
Find Nextcloud on https://nextcloud.com</string> Find Nextcloud on https://nextcloud.com</string>
<string name="hint_enter_a_message">Enter a message…</string>
<!-- Chat -->
<string name="nc_hint_enter_a_message">Enter a message…</string>
<string name="nc_date_header_yesterday">Yesterday</string>
<string name="nc_date_header_today">Today</string>
</resources> </resources>