talk-android/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java

319 lines
11 KiB
Java
Raw Normal View History

/*
* 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);
}
}
}