mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Initial chat work
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
51698470f8
commit
09c4b83f82
@ -150,6 +150,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.github.wooplr:Spotlight:1.2.3'
|
implementation 'com.github.wooplr:Spotlight:1.2.3'
|
||||||
|
|
||||||
|
implementation 'com.github.stfalcon:chatkit:0.2.2'
|
||||||
|
|
||||||
implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
|
implementation 'com.github.Kennyc1012:BottomSheet:2.4.0'
|
||||||
implementation 'eu.davidea:flipview:1.1.3'
|
implementation 'eu.davidea:flipview:1.1.3'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
@ -24,6 +24,7 @@ import android.support.annotation.Nullable;
|
|||||||
|
|
||||||
import com.nextcloud.talk.models.json.call.CallOverall;
|
import com.nextcloud.talk.models.json.call.CallOverall;
|
||||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
||||||
|
import com.nextcloud.talk.models.json.chat.ChatOverall;
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||||
import com.nextcloud.talk.models.json.generic.Status;
|
import com.nextcloud.talk.models.json.generic.Status;
|
||||||
import com.nextcloud.talk.models.json.participants.AddParticipantOverall;
|
import com.nextcloud.talk.models.json.participants.AddParticipantOverall;
|
||||||
@ -39,6 +40,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
import retrofit2.Response;
|
||||||
import retrofit2.http.DELETE;
|
import retrofit2.http.DELETE;
|
||||||
import retrofit2.http.Field;
|
import retrofit2.http.Field;
|
||||||
import retrofit2.http.FieldMap;
|
import retrofit2.http.FieldMap;
|
||||||
@ -256,4 +258,28 @@ public interface NcApi {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
Observable<CapabilitiesOverall> getCapabilities(@Header("Authorization") String authorization, @Url String url);
|
Observable<CapabilitiesOverall> getCapabilities(@Header("Authorization") String authorization, @Url String url);
|
||||||
|
|
||||||
|
/*
|
||||||
|
QueryMap items are as follows:
|
||||||
|
- "lookIntoFuture": int (0 or 1),
|
||||||
|
- "limit" : int, range 100-200,
|
||||||
|
- "timeout": used with look into future, 30 default, 60 at most
|
||||||
|
- "lastKnownMessageId", int, use one from X-Chat-Last-Given
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
Observable<Response<ChatOverall>> pullChatMessages(@Header("Authorization") String authorization, @Url String url,
|
||||||
|
@QueryMap Map<String, Integer> fields);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fieldmap items are as follows:
|
||||||
|
- "message": ,
|
||||||
|
- "actorDisplayName"
|
||||||
|
*/
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT
|
||||||
|
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url,
|
||||||
|
@FieldMap Map<String, String> fields);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
@BindView(R.id.fast_scroller)
|
@BindView(R.id.fast_scroller)
|
||||||
FastScroller fastScroller;
|
FastScroller fastScroller;
|
||||||
|
|
||||||
private UserEntity userEntity;
|
private UserEntity currentUser;
|
||||||
private Disposable roomsQueryDisposable;
|
private Disposable roomsQueryDisposable;
|
||||||
private FlexibleAdapter<CallItem> adapter;
|
private FlexibleAdapter<CallItem> adapter;
|
||||||
private List<CallItem> callItems = new ArrayList<>();
|
private List<CallItem> callItems = new ArrayList<>();
|
||||||
@ -144,9 +144,9 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
getActionBar().show();
|
getActionBar().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
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())
|
||||||
@ -155,7 +155,7 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
|
|
||||||
if (adapter == null) {
|
if (adapter == null) {
|
||||||
adapter = new FlexibleAdapter<>(callItems, getActivity(), false);
|
adapter = new FlexibleAdapter<>(callItems, getActivity(), false);
|
||||||
if (userEntity != null) {
|
if (currentUser != null) {
|
||||||
fetchData(false);
|
fetchData(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,7 +172,7 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
getActionBar().setDisplayHomeAsUpEnabled(false);
|
getActionBar().setDisplayHomeAsUpEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
userEntity = userUtils.getCurrentUser();
|
currentUser = userUtils.getCurrentUser();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,15 +268,15 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
|
|
||||||
callItems = new ArrayList<>();
|
callItems = new ArrayList<>();
|
||||||
|
|
||||||
roomsQueryDisposable = ncApi.getRooms(ApiUtils.getCredentials(userEntity.getUsername(),
|
roomsQueryDisposable = ncApi.getRooms(ApiUtils.getCredentials(currentUser.getUsername(),
|
||||||
userEntity.getToken()), ApiUtils.getUrlForGetRooms(userEntity.getBaseUrl()))
|
currentUser.getToken()), ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl()))
|
||||||
.subscribeOn(Schedulers.newThread())
|
.subscribeOn(Schedulers.newThread())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(roomsOverall -> {
|
.subscribe(roomsOverall -> {
|
||||||
|
|
||||||
if (roomsOverall != null) {
|
if (roomsOverall != null) {
|
||||||
for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) {
|
for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) {
|
||||||
callItems.add(new CallItem(roomsOverall.getOcs().getData().get(i), userEntity));
|
callItems.add(new CallItem(roomsOverall.getOcs().getData().get(i), currentUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.updateDataSet(callItems, true);
|
adapter.updateDataSet(callItems, true);
|
||||||
@ -305,7 +305,7 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
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())));
|
||||||
@ -494,18 +494,25 @@ public class CallsListController extends BaseController implements SearchView.On
|
|||||||
Room room = callItem.getModel();
|
Room room = callItem.getModel();
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, callItem.getModel().getToken());
|
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, callItem.getModel().getToken());
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(userEntity));
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(currentUser));
|
||||||
|
|
||||||
if (room.hasPassword && (room.participantType.equals(Participant.ParticipantType.GUEST) ||
|
if (room.hasPassword && (room.participantType.equals(Participant.ParticipantType.GUEST) ||
|
||||||
room.participantType.equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) {
|
room.participantType.equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) {
|
||||||
bundle.putInt(BundleKeys.KEY_OPERATION_CODE, 99);
|
bundle.putInt(BundleKeys.KEY_OPERATION_CODE, 99);
|
||||||
prepareAndShowBottomSheetWithBundle(bundle, false);
|
prepareAndShowBottomSheetWithBundle(bundle, false);
|
||||||
} else {
|
} else {
|
||||||
overridePushHandler(new NoOpControllerChangeHandler());
|
if (currentUser.hasSpreedCapabilityWithName("chat-v2")) {
|
||||||
overridePopHandler(new NoOpControllerChangeHandler());
|
bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, room.getDisplayName());
|
||||||
Intent callIntent = new Intent(getActivity(), CallActivity.class);
|
getParentController().getRouter().pushController((RouterTransaction.with(new ChatController(bundle))
|
||||||
callIntent.putExtras(bundle);
|
.pushChangeHandler(new HorizontalChangeHandler())
|
||||||
startActivity(callIntent);
|
.popChangeHandler(new HorizontalChangeHandler())));
|
||||||
|
} else {
|
||||||
|
overridePushHandler(new NoOpControllerChangeHandler());
|
||||||
|
overridePopHandler(new NoOpControllerChangeHandler());
|
||||||
|
Intent callIntent = new Intent(getActivity(), CallActivity.class);
|
||||||
|
callIntent.putExtras(bundle);
|
||||||
|
startActivity(callIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* 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.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
|
||||||
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
import com.nextcloud.talk.R;
|
||||||
|
import com.nextcloud.talk.api.NcApi;
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||||
|
import com.nextcloud.talk.controllers.base.BaseController;
|
||||||
|
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.utils.ApiUtils;
|
||||||
|
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||||
|
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||||
|
import com.nextcloud.talk.utils.glide.GlideApp;
|
||||||
|
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 org.parceler.Parcels;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import autodagger.AutoInjector;
|
||||||
|
import butterknife.BindView;
|
||||||
|
import io.reactivex.Observer;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication.class)
|
||||||
|
public class ChatController extends BaseController implements MessagesListAdapter.OnLoadMoreListener {
|
||||||
|
@Inject
|
||||||
|
NcApi ncApi;
|
||||||
|
@Inject
|
||||||
|
UserUtils userUtils;
|
||||||
|
@BindView(R.id.input)
|
||||||
|
MessageInput messageInput;
|
||||||
|
@BindView(R.id.messagesList)
|
||||||
|
MessagesList messagesList;
|
||||||
|
|
||||||
|
private String conversationName;
|
||||||
|
private String roomToken;
|
||||||
|
private UserEntity currentUser;
|
||||||
|
|
||||||
|
private boolean inChat = false;
|
||||||
|
private boolean historyRead = false;
|
||||||
|
private int globalLastKnownFutureMessageId = -1;
|
||||||
|
private int globalLastKnownPastMessageId = -1;
|
||||||
|
|
||||||
|
private MessagesListAdapter<ChatMessage> adapter;
|
||||||
|
|
||||||
|
public ChatController(Bundle args) {
|
||||||
|
super(args);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
this.conversationName = args.getString(BundleKeys.KEY_CONVERSATION_NAME);
|
||||||
|
this.currentUser = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_USER_ENTITY));
|
||||||
|
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||||
|
return inflater.inflate(R.layout.controller_chat, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onViewBound(@NonNull View view) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
messagesList.setAdapter(adapter);
|
||||||
|
adapter.setLoadMoreListener(this);
|
||||||
|
joinRoomWithPassword(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttach(@NonNull View view) {
|
||||||
|
super.onAttach(view);
|
||||||
|
if (getActionBar() != null) {
|
||||||
|
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTitle() {
|
||||||
|
return conversationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case android.R.id.home:
|
||||||
|
inChat = false;
|
||||||
|
getRouter().popCurrentController();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
inChat = false;
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void joinRoomWithPassword(@Nullable String password) {
|
||||||
|
ncApi.joinRoom(ApiUtils.getCredentials(currentUser.getUserId(), currentUser.getToken()), ApiUtils
|
||||||
|
.getUrlForRoomParticipants(currentUser.getBaseUrl(), roomToken), password)
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.retry(3)
|
||||||
|
.subscribe(new Observer<CallOverall>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(CallOverall callOverall) {
|
||||||
|
inChat = true;
|
||||||
|
pullChatMessages(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pullChatMessages(int lookIntoFuture) {
|
||||||
|
Map<String, Integer> fieldMap = new HashMap<>();
|
||||||
|
fieldMap.put("lookIntoFuture", lookIntoFuture);
|
||||||
|
fieldMap.put("limit", 2);
|
||||||
|
|
||||||
|
int lastKnown;
|
||||||
|
if (lookIntoFuture == 1) {
|
||||||
|
lastKnown = globalLastKnownFutureMessageId;
|
||||||
|
} else {
|
||||||
|
lastKnown = globalLastKnownPastMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldMap.put("lastKnownMessageId", lastKnown);
|
||||||
|
|
||||||
|
if (lookIntoFuture == 1) {
|
||||||
|
ncApi.pullChatMessages(ApiUtils.getCredentials(currentUser.getUserId(), currentUser.getToken()),
|
||||||
|
ApiUtils.getUrlForChat(currentUser.getBaseUrl(), roomToken), fieldMap)
|
||||||
|
.subscribeOn(Schedulers.newThread())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.takeWhile(observable -> inChat)
|
||||||
|
.retry(3, observable -> inChat)
|
||||||
|
.subscribe(new Observer<Response>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(Response response) {
|
||||||
|
processMessages(response, true);
|
||||||
|
pullChatMessages(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ncApi.pullChatMessages(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<Response>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(Response response) {
|
||||||
|
processMessages(response, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMessages(Response response, boolean isFromTheFuture) {
|
||||||
|
if (response.code() == 200) {
|
||||||
|
boolean shouldForceFuture = false;
|
||||||
|
if (globalLastKnownFutureMessageId == -1) {
|
||||||
|
shouldForceFuture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatOverall chatOverall = (ChatOverall) response.body();
|
||||||
|
List<ChatMessage> chatMessageList = chatOverall.getOcs().getData();
|
||||||
|
|
||||||
|
if (!isFromTheFuture) {
|
||||||
|
for (int i = 0; i < chatMessageList.size(); i++) {
|
||||||
|
chatMessageList.get(i).setBaseUrl(currentUser.getBaseUrl());
|
||||||
|
if (globalLastKnownPastMessageId == -1 || chatMessageList.get(i).getJsonMessageId() <
|
||||||
|
globalLastKnownPastMessageId) {
|
||||||
|
globalLastKnownPastMessageId = chatMessageList.get(i).getJsonMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldForceFuture) {
|
||||||
|
if (chatMessageList.get(i).getJsonMessageId() > globalLastKnownFutureMessageId) {
|
||||||
|
globalLastKnownFutureMessageId = chatMessageList.get(i).getJsonMessageId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
adapter.addToEnd(chatMessageList, false);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalLastKnownFutureMessageId = Integer.parseInt(response.headers().get("X-Chat-Last-Given"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldForceFuture) {
|
||||||
|
pullChatMessages(1);
|
||||||
|
}
|
||||||
|
} else if (response.code() == 304 && !isFromTheFuture) {
|
||||||
|
historyRead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadMore(int page, int totalItemsCount) {
|
||||||
|
if (!historyRead) {
|
||||||
|
pullChatMessages(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.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;
|
||||||
|
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
@Data
|
||||||
|
@JsonObject
|
||||||
|
public class ChatMessage implements IMessage {
|
||||||
|
String baseUrl;
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseUrl(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonField(name = "id")
|
||||||
|
int jsonMessageId;
|
||||||
|
|
||||||
|
@JsonField(name = "token")
|
||||||
|
String token;
|
||||||
|
|
||||||
|
// guests or users
|
||||||
|
@JsonField(name = "actorType")
|
||||||
|
String actorType;
|
||||||
|
|
||||||
|
@JsonField(name = "actorId")
|
||||||
|
String actorId;
|
||||||
|
|
||||||
|
// send when crafting a message
|
||||||
|
@JsonField(name = "actorDisplayName")
|
||||||
|
String actorDisplayName;
|
||||||
|
|
||||||
|
@JsonField(name = "timestamp")
|
||||||
|
long timestamp;
|
||||||
|
|
||||||
|
// send when crafting a message, max 1000 lines
|
||||||
|
@JsonField(name = "message")
|
||||||
|
String message;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return Integer.toString(jsonMessageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IUser getUser() {
|
||||||
|
return new IUser() {
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return actorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return actorDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAvatar() {
|
||||||
|
if ("guests".equals(actorType)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreatedAt() {
|
||||||
|
return TimeUtils.getDateCurrentTimeZone(timestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.models.json.chat;
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
|
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||||
|
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Parcel
|
||||||
|
@JsonObject
|
||||||
|
public class ChatOCS extends GenericOCS {
|
||||||
|
@JsonField(name = "data")
|
||||||
|
List<ChatMessage> data;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.models.json.chat;
|
||||||
|
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
|
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Parcel
|
||||||
|
@JsonObject
|
||||||
|
public class ChatOverall {
|
||||||
|
@JsonField(name = "ocs")
|
||||||
|
ChatOCS ocs;
|
||||||
|
}
|
@ -126,6 +126,10 @@ public class ApiUtils {
|
|||||||
return getUrlForCall(baseUrl, token) + "/ping";
|
return getUrlForCall(baseUrl, token) + "/ping";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUrlForChat(String baseUrl, String token) {
|
||||||
|
return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUrlForSignaling(String baseUrl, @Nullable String token) {
|
public static String getUrlForSignaling(String baseUrl, @Nullable String token) {
|
||||||
String signalingUrl = baseUrl + ocsApiVersion + spreedApiVersion + "/signaling";
|
String signalingUrl = baseUrl + ocsApiVersion + spreedApiVersion + "/signaling";
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
|
52
app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java
Normal file
52
app/src/main/java/com/nextcloud/talk/utils/TimeUtils.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
}
|
55
app/src/main/res/layout/controller_chat.xml
Normal file
55
app/src/main/res/layout/controller_chat.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.stfalcon.chatkit.messages.MessagesList
|
||||||
|
android:id="@+id/messagesList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_above="@+id/input"
|
||||||
|
app:textAutoLink="all"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_above="@+id/input"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:background="@color/nc_light_grey"/>
|
||||||
|
|
||||||
|
<com.stfalcon.chatkit.messages.MessageInput
|
||||||
|
android:id="@+id/input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:maxLength="1000"
|
||||||
|
app:inputButtonHeight="30dp"
|
||||||
|
app:inputButtonMargin="16dp"
|
||||||
|
app:inputButtonWidth="30dp"
|
||||||
|
app:inputHint="@string/hint_enter_a_message"
|
||||||
|
app:inputTextColor="@color/black"
|
||||||
|
app:inputTextSize="18sp"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -10,5 +10,6 @@
|
|||||||
<color name="nc_white_color_complete">#FFFFFF</color>
|
<color name="nc_white_color_complete">#FFFFFF</color>
|
||||||
<color name="nc_light_blue_color">#7FC0E3</color>
|
<color name="nc_light_blue_color">#7FC0E3</color>
|
||||||
<color name="nc_material_yellow">#FFEB3B</color>
|
<color name="nc_material_yellow">#FFEB3B</color>
|
||||||
|
<color name="nc_light_grey">#E8E8E8</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@ -164,4 +164,5 @@ 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>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user