mirror of https://github.com/KokaKiwi/BarInsta
1872 lines
85 KiB
Java
1872 lines
85 KiB
Java
package awais.instagrabber.managers;
|
|
|
|
import android.content.ContentResolver;
|
|
import android.net.Uri;
|
|
import android.util.Log;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.util.Pair;
|
|
import androidx.lifecycle.LiveData;
|
|
import androidx.lifecycle.MutableLiveData;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.Iterables;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.net.HttpURLConnection;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.stream.Collectors;
|
|
|
|
import awais.instagrabber.R;
|
|
import awais.instagrabber.customviews.emoji.Emoji;
|
|
import awais.instagrabber.models.Resource;
|
|
import awais.instagrabber.models.Resource.Status;
|
|
import awais.instagrabber.models.UploadVideoOptions;
|
|
import awais.instagrabber.models.enums.DirectItemType;
|
|
import awais.instagrabber.repositories.requests.UploadFinishOptions;
|
|
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions.ThreadIdOrUserIds;
|
|
import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
|
|
import awais.instagrabber.repositories.responses.FriendshipRestrictResponse;
|
|
import awais.instagrabber.repositories.responses.User;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse.DirectItemSeenResponsePayload;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
|
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
|
|
import awais.instagrabber.utils.BitmapUtils;
|
|
import awais.instagrabber.utils.Constants;
|
|
import awais.instagrabber.utils.CookieUtils;
|
|
import awais.instagrabber.utils.DirectItemFactory;
|
|
import awais.instagrabber.utils.MediaController;
|
|
import awais.instagrabber.utils.MediaUploadHelper;
|
|
import awais.instagrabber.utils.MediaUploader;
|
|
import awais.instagrabber.utils.MediaUtils;
|
|
import awais.instagrabber.utils.TextUtils;
|
|
import awais.instagrabber.utils.Utils;
|
|
import awais.instagrabber.webservices.DirectMessagesService;
|
|
import awais.instagrabber.webservices.FriendshipService;
|
|
import awais.instagrabber.webservices.MediaService;
|
|
import awais.instagrabber.webservices.ServiceCallback;
|
|
import retrofit2.Call;
|
|
import retrofit2.Callback;
|
|
import retrofit2.Response;
|
|
|
|
import static androidx.lifecycle.Transformations.distinctUntilChanged;
|
|
import static androidx.lifecycle.Transformations.map;
|
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
|
|
|
public final class ThreadManager {
|
|
private static final String TAG = ThreadManager.class.getSimpleName();
|
|
private static final Object LOCK = new Object();
|
|
private static final Map<String, ThreadManager> INSTANCE_MAP = new ConcurrentHashMap<>();
|
|
|
|
private final MutableLiveData<Resource<Object>> fetching = new MutableLiveData<>();
|
|
private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
|
|
private final MutableLiveData<DirectThreadParticipantRequestsResponse> pendingRequests = new MutableLiveData<>(null);
|
|
|
|
private final String threadId;
|
|
private final long viewerId;
|
|
private final ThreadIdOrUserIds threadIdOrUserIds;
|
|
private final User currentUser;
|
|
private final ContentResolver contentResolver;
|
|
|
|
private DirectMessagesService service;
|
|
private MediaService mediaService;
|
|
private FriendshipService friendshipService;
|
|
private InboxManager inboxManager;
|
|
private LiveData<DirectThread> thread;
|
|
private LiveData<Integer> inputMode;
|
|
private LiveData<String> threadTitle;
|
|
private LiveData<List<User>> users;
|
|
private LiveData<List<User>> usersWithCurrent;
|
|
private LiveData<List<User>> leftUsers;
|
|
private LiveData<Pair<List<User>, List<User>>> usersAndLeftUsers;
|
|
private LiveData<Boolean> pending;
|
|
private LiveData<List<Long>> adminUserIds;
|
|
private LiveData<List<DirectItem>> items;
|
|
private LiveData<Boolean> isViewerAdmin;
|
|
private LiveData<Boolean> isGroup;
|
|
private LiveData<Boolean> isMuted;
|
|
private LiveData<Boolean> isApprovalRequiredToJoin;
|
|
private LiveData<Boolean> isMentionsMuted;
|
|
private LiveData<Integer> pendingRequestsCount;
|
|
private LiveData<User> inviter;
|
|
private boolean hasOlder = true;
|
|
private String cursor;
|
|
private Call<DirectThreadFeedResponse> chatsRequest;
|
|
|
|
public static ThreadManager getInstance(@NonNull final String threadId,
|
|
final boolean pending,
|
|
@NonNull final User currentUser,
|
|
@NonNull final ContentResolver contentResolver) {
|
|
ThreadManager instance = INSTANCE_MAP.get(threadId);
|
|
if (instance == null) {
|
|
synchronized (LOCK) {
|
|
instance = INSTANCE_MAP.get(threadId);
|
|
if (instance == null) {
|
|
instance = new ThreadManager(threadId, pending, currentUser, contentResolver);
|
|
INSTANCE_MAP.put(threadId, instance);
|
|
}
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
private String getThreadId() {
|
|
return threadId;
|
|
}
|
|
|
|
private ThreadManager(@NonNull final String threadId,
|
|
final boolean pending,
|
|
@NonNull final User currentUser,
|
|
@NonNull final ContentResolver contentResolver) {
|
|
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
|
|
this.inboxManager = pending ? messagesManager.getPendingInboxManager() : messagesManager.getInboxManager();
|
|
this.threadId = threadId;
|
|
this.threadIdOrUserIds = ThreadIdOrUserIds.of(threadId);
|
|
this.currentUser = currentUser;
|
|
this.contentResolver = contentResolver;
|
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
|
viewerId = CookieUtils.getUserIdFromCookie(cookie);
|
|
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
|
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
|
if (csrfToken == null) return;
|
|
// if (TextUtils.isEmpty(csrfToken) || viewerId <= 0 || TextUtils.isEmpty(deviceUuid)) {
|
|
// throw new IllegalArgumentException("User is not logged in!");
|
|
// }
|
|
service = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid);
|
|
mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId);
|
|
friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, viewerId);
|
|
setupTransformations();
|
|
// fetchChats();
|
|
}
|
|
|
|
public void moveFromPending() {
|
|
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
|
|
this.inboxManager = messagesManager.getInboxManager();
|
|
setupTransformations();
|
|
}
|
|
|
|
private void setupTransformations() {
|
|
// Transformations
|
|
thread = distinctUntilChanged(map(inboxManager.getInbox(), inboxResource -> {
|
|
if (inboxResource == null) {
|
|
return null;
|
|
}
|
|
final DirectInbox inbox = inboxResource.data;
|
|
if (inbox == null) {
|
|
return null;
|
|
}
|
|
final List<DirectThread> threads = inbox.getThreads();
|
|
if (threads == null || threads.isEmpty()) {
|
|
return null;
|
|
}
|
|
final DirectThread thread = threads.stream()
|
|
.filter(t -> t.getThreadId().equals(threadId))
|
|
.findFirst()
|
|
.orElse(null);
|
|
if (thread != null) {
|
|
cursor = thread.getOldestCursor();
|
|
hasOlder = thread.hasOlder();
|
|
}
|
|
return thread;
|
|
}));
|
|
inputMode = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return 1;
|
|
return t.getInputMode();
|
|
}));
|
|
threadTitle = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return null;
|
|
return t.getThreadTitle();
|
|
}));
|
|
users = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return Collections.emptyList();
|
|
return t.getUsers();
|
|
}));
|
|
usersWithCurrent = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return Collections.emptyList();
|
|
return getUsersWithCurrentUser(t);
|
|
}));
|
|
leftUsers = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return Collections.emptyList();
|
|
return t.getLeftUsers();
|
|
}));
|
|
usersAndLeftUsers = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) {
|
|
return new Pair<>(Collections.emptyList(), Collections.emptyList());
|
|
}
|
|
final List<User> users = getUsersWithCurrentUser(t);
|
|
final List<User> leftUsers = t.getLeftUsers();
|
|
return new Pair<>(users, leftUsers);
|
|
}));
|
|
pending = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return true;
|
|
return t.isPending();
|
|
}));
|
|
adminUserIds = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return Collections.emptyList();
|
|
return t.getAdminUserIds();
|
|
}));
|
|
items = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return Collections.emptyList();
|
|
return t.getItems();
|
|
}));
|
|
isViewerAdmin = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return false;
|
|
return t.getAdminUserIds().contains(viewerId);
|
|
}));
|
|
isGroup = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return false;
|
|
return t.isGroup();
|
|
}));
|
|
isMuted = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return false;
|
|
return t.isMuted();
|
|
}));
|
|
isApprovalRequiredToJoin = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return false;
|
|
return t.isApprovalRequiredForNewMembers();
|
|
}));
|
|
isMentionsMuted = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return false;
|
|
return t.isMentionsMuted();
|
|
}));
|
|
pendingRequestsCount = distinctUntilChanged(map(pendingRequests, p -> {
|
|
if (p == null) return 0;
|
|
return p.getTotalParticipantRequests();
|
|
}));
|
|
inviter = distinctUntilChanged(map(thread, t -> {
|
|
if (t == null) return null;
|
|
return t.getInviter();
|
|
}));
|
|
}
|
|
|
|
private List<User> getUsersWithCurrentUser(final DirectThread t) {
|
|
final ImmutableList.Builder<User> builder = ImmutableList.builder();
|
|
if (currentUser != null) {
|
|
builder.add(currentUser);
|
|
}
|
|
final List<User> users = t.getUsers();
|
|
if (users != null) {
|
|
builder.addAll(users);
|
|
}
|
|
return builder.build();
|
|
}
|
|
|
|
public LiveData<DirectThread> getThread() {
|
|
return thread;
|
|
}
|
|
|
|
public LiveData<Integer> getInputMode() {
|
|
return inputMode;
|
|
}
|
|
|
|
public LiveData<String> getThreadTitle() {
|
|
return threadTitle;
|
|
}
|
|
|
|
public LiveData<List<User>> getUsers() {
|
|
return users;
|
|
}
|
|
|
|
public LiveData<List<User>> getUsersWithCurrent() {
|
|
return usersWithCurrent;
|
|
}
|
|
|
|
public LiveData<List<User>> getLeftUsers() {
|
|
return leftUsers;
|
|
}
|
|
|
|
public LiveData<Pair<List<User>, List<User>>> getUsersAndLeftUsers() {
|
|
return usersAndLeftUsers;
|
|
}
|
|
|
|
public LiveData<Boolean> isPending() {
|
|
return pending;
|
|
}
|
|
|
|
public LiveData<List<Long>> getAdminUserIds() {
|
|
return adminUserIds;
|
|
}
|
|
|
|
public LiveData<List<DirectItem>> getItems() {
|
|
return items;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> isFetching() {
|
|
return fetching;
|
|
}
|
|
|
|
public LiveData<DirectItem> getReplyToItem() {
|
|
return replyToItem;
|
|
}
|
|
|
|
public LiveData<Integer> getPendingRequestsCount() {
|
|
return pendingRequestsCount;
|
|
}
|
|
|
|
public LiveData<DirectThreadParticipantRequestsResponse> getPendingRequests() {
|
|
return pendingRequests;
|
|
}
|
|
|
|
public LiveData<Boolean> isGroup() {
|
|
return isGroup;
|
|
}
|
|
|
|
public LiveData<Boolean> isMuted() {
|
|
return isMuted;
|
|
}
|
|
|
|
public LiveData<Boolean> isApprovalRequiredToJoin() {
|
|
return isApprovalRequiredToJoin;
|
|
}
|
|
|
|
public LiveData<Boolean> isViewerAdmin() {
|
|
return isViewerAdmin;
|
|
}
|
|
|
|
public LiveData<Boolean> isMentionsMuted() {
|
|
return isMentionsMuted;
|
|
}
|
|
|
|
public void fetchChats() {
|
|
final Resource<Object> fetchingValue = fetching.getValue();
|
|
if ((fetchingValue != null && fetchingValue.status == Status.LOADING) || !hasOlder) return;
|
|
fetching.postValue(Resource.loading(null));
|
|
chatsRequest = service.fetchThread(threadId, cursor);
|
|
chatsRequest.enqueue(new Callback<DirectThreadFeedResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadFeedResponse> call, @NonNull final Response<DirectThreadFeedResponse> response) {
|
|
final DirectThreadFeedResponse feedResponse = response.body();
|
|
if (feedResponse == null) {
|
|
fetching.postValue(Resource.error(R.string.generic_null_response, null));
|
|
Log.e(TAG, "onResponse: response was null!");
|
|
return;
|
|
}
|
|
if (!feedResponse.getStatus().equals("ok")) {
|
|
fetching.postValue(Resource.error(R.string.generic_not_ok_response, null));
|
|
return;
|
|
}
|
|
final DirectThread thread = feedResponse.getThread();
|
|
setThread(thread);
|
|
fetching.postValue(Resource.success(new Object()));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadFeedResponse> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "Failed fetching dm chats", t);
|
|
fetching.postValue(Resource.error(t.getMessage(), null));
|
|
hasOlder = false;
|
|
}
|
|
});
|
|
if (cursor == null) {
|
|
fetchPendingRequests();
|
|
}
|
|
}
|
|
|
|
public void fetchPendingRequests() {
|
|
final Boolean isGroup = this.isGroup.getValue();
|
|
if (isGroup == null || !isGroup) return;
|
|
final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
|
|
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
|
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadParticipantRequestsResponse> call,
|
|
@NonNull final Response<DirectThreadParticipantRequestsResponse> response) {
|
|
if (!response.isSuccessful()) {
|
|
if (response.errorBody() != null) {
|
|
try {
|
|
final String string = response.errorBody().string();
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
Log.e(TAG, msg);
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
return;
|
|
}
|
|
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
|
return;
|
|
}
|
|
final DirectThreadParticipantRequestsResponse body = response.body();
|
|
if (body == null) {
|
|
Log.e(TAG, "onResponse: response body was null");
|
|
return;
|
|
}
|
|
pendingRequests.postValue(body);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadParticipantRequestsResponse> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void setThread(@NonNull final DirectThread thread, final boolean skipItems) {
|
|
// if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) {
|
|
// fetchPendingRequests();
|
|
// }
|
|
final List<DirectItem> items = thread.getItems();
|
|
if (skipItems) {
|
|
final DirectThread currentThread = this.thread.getValue();
|
|
if (currentThread != null) {
|
|
thread.setItems(currentThread.getItems());
|
|
}
|
|
}
|
|
if (!skipItems && !TextUtils.isEmpty(cursor)) {
|
|
final DirectThread currentThread = this.thread.getValue();
|
|
if (currentThread != null) {
|
|
List<DirectItem> list = currentThread.getItems();
|
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
|
list.addAll(items);
|
|
thread.setItems(list);
|
|
}
|
|
}
|
|
inboxManager.setThread(threadId, thread);
|
|
}
|
|
|
|
private void setThread(@NonNull final DirectThread thread) {
|
|
setThread(thread, false);
|
|
}
|
|
|
|
private void setThreadUsers(final List<User> users, final List<User> leftUsers) {
|
|
final DirectThread currentThread = this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
final DirectThread thread;
|
|
try {
|
|
thread = (DirectThread) currentThread.clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "setThreadUsers: ", e);
|
|
return;
|
|
}
|
|
if (users != null) {
|
|
thread.setUsers(users);
|
|
}
|
|
if (leftUsers != null) {
|
|
thread.setLeftUsers(leftUsers);
|
|
}
|
|
inboxManager.setThread(threadId, thread);
|
|
}
|
|
|
|
private void addItems(final int index, final Collection<DirectItem> items) {
|
|
if (items == null) return;
|
|
inboxManager.addItemsToThread(threadId, index, items);
|
|
}
|
|
|
|
private void addReaction(final DirectItem item, final Emoji emoji) {
|
|
if (item == null || emoji == null || currentUser == null) return;
|
|
final boolean isLike = emoji.getUnicode().equals("❤️");
|
|
DirectItemReactions reactions = item.getReactions();
|
|
if (reactions == null) {
|
|
reactions = new DirectItemReactions(null, null);
|
|
} else {
|
|
try {
|
|
reactions = (DirectItemReactions) reactions.clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "addReaction: ", e);
|
|
return;
|
|
}
|
|
}
|
|
if (isLike) {
|
|
final List<DirectItemEmojiReaction> likes = addEmoji(reactions.getLikes(), null, false);
|
|
reactions.setLikes(likes);
|
|
}
|
|
final List<DirectItemEmojiReaction> emojis = addEmoji(reactions.getEmojis(), emoji.getUnicode(), true);
|
|
reactions.setEmojis(emojis);
|
|
List<DirectItem> list = this.items.getValue();
|
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
|
int index = getItemIndex(item, list);
|
|
if (index >= 0) {
|
|
try {
|
|
final DirectItem clone = (DirectItem) list.get(index).clone();
|
|
clone.setReactions(reactions);
|
|
list.set(index, clone);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "addReaction: error cloning", e);
|
|
}
|
|
}
|
|
inboxManager.setItemsToThread(threadId, list);
|
|
}
|
|
|
|
private void removeReaction(final DirectItem item) {
|
|
try {
|
|
final DirectItem itemClone = (DirectItem) item.clone();
|
|
final DirectItemReactions reactions = itemClone.getReactions();
|
|
final DirectItemReactions reactionsClone = (DirectItemReactions) reactions.clone();
|
|
final List<DirectItemEmojiReaction> likes = reactionsClone.getLikes();
|
|
if (likes != null) {
|
|
final List<DirectItemEmojiReaction> updatedLikes = likes.stream()
|
|
.filter(like -> like.getSenderId() != viewerId)
|
|
.collect(Collectors.toList());
|
|
reactionsClone.setLikes(updatedLikes);
|
|
}
|
|
final List<DirectItemEmojiReaction> emojis = reactionsClone.getEmojis();
|
|
if (emojis != null) {
|
|
final List<DirectItemEmojiReaction> updatedEmojis = emojis.stream()
|
|
.filter(emoji -> emoji.getSenderId() != viewerId)
|
|
.collect(Collectors.toList());
|
|
reactionsClone.setEmojis(updatedEmojis);
|
|
}
|
|
itemClone.setReactions(reactionsClone);
|
|
List<DirectItem> list = this.items.getValue();
|
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
|
int index = getItemIndex(item, list);
|
|
if (index >= 0) {
|
|
list.set(index, itemClone);
|
|
}
|
|
inboxManager.setItemsToThread(threadId, list);
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "removeReaction: ", e);
|
|
}
|
|
}
|
|
|
|
private int removeItem(final DirectItem item) {
|
|
if (item == null) return 0;
|
|
List<DirectItem> list = this.items.getValue();
|
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
|
int index = getItemIndex(item, list);
|
|
if (index >= 0) {
|
|
list.remove(index);
|
|
inboxManager.setItemsToThread(threadId, list);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
private List<DirectItemEmojiReaction> addEmoji(final List<DirectItemEmojiReaction> reactionList,
|
|
final String emoji,
|
|
final boolean shouldReplaceIfAlreadyReacted) {
|
|
if (currentUser == null) return reactionList;
|
|
final List<DirectItemEmojiReaction> temp = reactionList == null ? new ArrayList<>() : new ArrayList<>(reactionList);
|
|
int index = -1;
|
|
for (int i = 0; i < temp.size(); i++) {
|
|
final DirectItemEmojiReaction directItemEmojiReaction = temp.get(i);
|
|
if (directItemEmojiReaction.getSenderId() == currentUser.getPk()) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
final DirectItemEmojiReaction reaction = new DirectItemEmojiReaction(
|
|
currentUser.getPk(),
|
|
System.currentTimeMillis() * 1000,
|
|
emoji,
|
|
"none"
|
|
);
|
|
if (index < 0) {
|
|
temp.add(0, reaction);
|
|
} else if (shouldReplaceIfAlreadyReacted) {
|
|
temp.add(0, reaction);
|
|
temp.remove(index);
|
|
}
|
|
return temp;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendText(final String text) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) return data;
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final DirectItem replyToItemValue = replyToItem.getValue();
|
|
final DirectItem directItem = DirectItemFactory.createText(userId, clientContext, text, replyToItemValue);
|
|
// Log.d(TAG, "sendText: sending: itemId: " + directItem.getItemId());
|
|
directItem.setPending(true);
|
|
addItems(0, Collections.singletonList(directItem));
|
|
data.postValue(Resource.loading(directItem));
|
|
final String repliedToItemId = replyToItemValue != null ? replyToItemValue.getItemId() : null;
|
|
final String repliedToClientContext = replyToItemValue != null ? replyToItemValue.getClientContext() : null;
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastText(
|
|
clientContext,
|
|
threadIdOrUserIds,
|
|
text,
|
|
repliedToItemId,
|
|
repliedToClientContext
|
|
);
|
|
enqueueRequest(request, data, directItem);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendUri(final MediaController.MediaEntry entry) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (entry == null) {
|
|
data.postValue(Resource.error("Entry is null", null));
|
|
return data;
|
|
}
|
|
final Uri uri = Uri.fromFile(new File(entry.path));
|
|
if (!entry.isVideo) {
|
|
sendPhoto(data, uri, entry.width, entry.height);
|
|
return data;
|
|
}
|
|
sendVideo(data, uri, entry.size, entry.duration, entry.width, entry.height);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendUri(final Uri uri) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (uri == null) {
|
|
data.postValue(Resource.error("Uri is null", null));
|
|
return data;
|
|
}
|
|
final String mimeType = Utils.getMimeType(uri, contentResolver);
|
|
if (TextUtils.isEmpty(mimeType)) {
|
|
data.postValue(Resource.error("Unknown MediaType", null));
|
|
return data;
|
|
}
|
|
final boolean isPhoto = mimeType.startsWith("image");
|
|
if (isPhoto) {
|
|
sendPhoto(data, uri);
|
|
return data;
|
|
}
|
|
if (mimeType.startsWith("video")) {
|
|
sendVideo(data, uri);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendAnimatedMedia(@NonNull final GiphyGif giphyGif) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) return data;
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final DirectItem directItem = DirectItemFactory.createAnimatedMedia(userId, clientContext, giphyGif);
|
|
directItem.setPending(true);
|
|
addItems(0, Collections.singletonList(directItem));
|
|
data.postValue(Resource.loading(directItem));
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastAnimatedMedia(
|
|
clientContext,
|
|
threadIdOrUserIds,
|
|
giphyGif
|
|
);
|
|
enqueueRequest(request, data, directItem);
|
|
return data;
|
|
}
|
|
|
|
public void sendVoice(@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final Uri uri,
|
|
@NonNull final List<Float> waveform,
|
|
final int samplingFreq,
|
|
final long duration,
|
|
final long byteLength) {
|
|
if (duration > 60000) {
|
|
// instagram does not allow uploading audio longer than 60 secs for Direct messages
|
|
data.postValue(Resource.error(R.string.dms_ERROR_AUDIO_TOO_LONG, null));
|
|
return;
|
|
}
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) return;
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final DirectItem directItem = DirectItemFactory.createVoice(userId, clientContext, uri, duration, waveform, samplingFreq);
|
|
directItem.setPending(true);
|
|
addItems(0, Collections.singletonList(directItem));
|
|
data.postValue(Resource.loading(directItem));
|
|
final UploadVideoOptions uploadDmVoiceOptions = MediaUploadHelper.createUploadDmVoiceOptions(byteLength, duration);
|
|
MediaUploader.uploadVideo(uri, contentResolver, uploadDmVoiceOptions, new MediaUploader.OnMediaUploadCompleteListener() {
|
|
@Override
|
|
public void onUploadComplete(final MediaUploader.MediaUploadResponse response) {
|
|
// Log.d(TAG, "onUploadComplete: " + response);
|
|
if (handleInvalidResponse(data, response)) return;
|
|
final UploadFinishOptions uploadFinishOptions = new UploadFinishOptions()
|
|
.setUploadId(uploadDmVoiceOptions.getUploadId())
|
|
.setSourceType("4");
|
|
final Call<String> uploadFinishRequest = mediaService.uploadFinish(uploadFinishOptions);
|
|
uploadFinishRequest.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (response.isSuccessful()) {
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastVoice(
|
|
clientContext,
|
|
threadIdOrUserIds,
|
|
uploadDmVoiceOptions.getUploadId(),
|
|
waveform,
|
|
samplingFreq
|
|
);
|
|
enqueueRequest(request, data, directItem);
|
|
return;
|
|
}
|
|
if (response.errorBody() != null) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.error("uploadFinishRequest was not successful and response error body was null", directItem));
|
|
Log.e(TAG, "uploadFinishRequest was not successful and response error body was null");
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendReaction(final DirectItem item, final Emoji emoji) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) {
|
|
data.postValue(Resource.error("userId is null", null));
|
|
return data;
|
|
}
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
// Log.d(TAG, "sendText: sending: itemId: " + directItem.getItemId());
|
|
data.postValue(Resource.loading(item));
|
|
addReaction(item, emoji);
|
|
String emojiUnicode = null;
|
|
if (!emoji.getUnicode().equals("❤️")) {
|
|
emojiUnicode = emoji.getUnicode();
|
|
}
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastReaction(
|
|
clientContext, threadIdOrUserIds, item.getItemId(), emojiUnicode, false);
|
|
handleBroadcastReactionRequest(data, item, request);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> sendDeleteReaction(final String itemId) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final DirectItem item = getItem(itemId);
|
|
if (item == null) {
|
|
data.postValue(Resource.error("Invalid item", null));
|
|
return data;
|
|
}
|
|
final DirectItemReactions reactions = item.getReactions();
|
|
if (reactions == null) {
|
|
// already removed?
|
|
data.postValue(Resource.success(item));
|
|
return data;
|
|
}
|
|
removeReaction(item);
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastReaction(clientContext, threadIdOrUserIds, item.getItemId(), null, true);
|
|
handleBroadcastReactionRequest(data, item, request);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> unsend(final DirectItem item) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (item == null) {
|
|
data.postValue(Resource.error("item is null", null));
|
|
return data;
|
|
}
|
|
final int index = removeItem(item);
|
|
final Call<String> request = service.deleteItem(threadId, item.getItemId());
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (response.isSuccessful()) {
|
|
// Log.d(TAG, "onResponse: " + response.body());
|
|
return;
|
|
}
|
|
// add the item back if unsuccessful
|
|
addItems(index, Collections.singletonList(item));
|
|
if (response.errorBody() != null) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.error(R.string.generic_failed_request, item));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), item));
|
|
Log.e(TAG, "enqueueRequest: onFailure: ", t);
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public void forward(final Set<RankedRecipient> recipients, final DirectItem itemToForward) {
|
|
if (recipients == null || itemToForward == null) return;
|
|
for (final RankedRecipient recipient : recipients) {
|
|
forward(recipient, itemToForward);
|
|
}
|
|
}
|
|
|
|
public void forward(final RankedRecipient recipient, final DirectItem itemToForward) {
|
|
if (recipient == null || itemToForward == null) return;
|
|
if (recipient.getThread() == null && recipient.getUser() != null) {
|
|
// create thread and forward
|
|
final Call<DirectThread> createThreadRequest = service.createThread(Collections.singletonList(recipient.getUser().getPk()), null);
|
|
createThreadRequest.enqueue(new Callback<DirectThread>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThread> call, @NonNull final Response<DirectThread> response) {
|
|
if (!response.isSuccessful()) {
|
|
if (response.errorBody() != null) {
|
|
try {
|
|
final String string = response.errorBody().string();
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
Log.e(TAG, msg);
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
return;
|
|
}
|
|
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
|
return;
|
|
}
|
|
final DirectThread thread = response.body();
|
|
if (thread == null) {
|
|
Log.e(TAG, "onResponse: thread is null");
|
|
return;
|
|
}
|
|
forward(thread, itemToForward);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThread> call, @NonNull final Throwable t) {
|
|
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
if (recipient.getThread() != null) {
|
|
// just forward
|
|
final DirectThread thread = recipient.getThread();
|
|
forward(thread, itemToForward);
|
|
}
|
|
}
|
|
|
|
public void setReplyToItem(final DirectItem item) {
|
|
// Log.d(TAG, "setReplyToItem: " + item);
|
|
replyToItem.postValue(item);
|
|
}
|
|
|
|
private void forward(@NonNull final DirectThread thread, @NonNull final DirectItem itemToForward) {
|
|
final DirectItemType itemType = itemToForward.getItemType();
|
|
final String itemTypeName = itemType.getName();
|
|
if (itemTypeName == null) {
|
|
Log.e(TAG, "forward: itemTypeName was null!");
|
|
return;
|
|
}
|
|
final Call<DirectThreadBroadcastResponse> request = service.forward(thread.getThreadId(),
|
|
itemTypeName,
|
|
threadId,
|
|
itemToForward.getItemId());
|
|
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
|
@NonNull final Response<DirectThreadBroadcastResponse> response) {
|
|
if (response.isSuccessful()) return;
|
|
if (response.errorBody() != null) {
|
|
try {
|
|
final String string = response.errorBody().string();
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
Log.e(TAG, msg);
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
return;
|
|
}
|
|
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadBroadcastResponse> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
public LiveData<Resource<Object>> acceptRequest() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Call<String> request = service.approveRequest(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call,
|
|
@NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
try {
|
|
final String string = response.errorBody() != null ? response.errorBody().string() : "";
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
Log.e(TAG, msg);
|
|
data.postValue(Resource.error(msg, null));
|
|
return;
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> declineRequest() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Call<String> request = service.declineRequest(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call,
|
|
@NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
try {
|
|
final String string = response.errorBody() != null ? response.errorBody().string() : "";
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
Log.e(TAG, msg);
|
|
data.postValue(Resource.error(msg, null));
|
|
return;
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public void refreshChats() {
|
|
final Resource<Object> isFetching = fetching.getValue();
|
|
if (isFetching != null && isFetching.status == Status.LOADING) {
|
|
stopCurrentRequest();
|
|
}
|
|
cursor = null;
|
|
hasOlder = true;
|
|
fetchChats();
|
|
}
|
|
|
|
private void sendPhoto(@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final Uri uri) {
|
|
try {
|
|
final Pair<Integer, Integer> dimensions = BitmapUtils.decodeDimensions(contentResolver, uri);
|
|
if (dimensions == null) {
|
|
data.postValue(Resource.error("Decoding dimensions failed", null));
|
|
return;
|
|
}
|
|
sendPhoto(data, uri, dimensions.first, dimensions.second);
|
|
} catch (FileNotFoundException e) {
|
|
data.postValue(Resource.error(e.getMessage(), null));
|
|
Log.e(TAG, "sendPhoto: ", e);
|
|
}
|
|
}
|
|
|
|
private void sendPhoto(@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final Uri uri,
|
|
final int width,
|
|
final int height) {
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) return;
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final DirectItem directItem = DirectItemFactory.createImageOrVideo(userId, clientContext, uri, width, height, false);
|
|
directItem.setPending(true);
|
|
addItems(0, Collections.singletonList(directItem));
|
|
data.postValue(Resource.loading(directItem));
|
|
MediaUploader.uploadPhoto(uri, contentResolver, new MediaUploader.OnMediaUploadCompleteListener() {
|
|
@Override
|
|
public void onUploadComplete(final MediaUploader.MediaUploadResponse response) {
|
|
if (handleInvalidResponse(data, response)) return;
|
|
final String uploadId = response.getResponse().optString("upload_id");
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastPhoto(clientContext, threadIdOrUserIds, uploadId);
|
|
enqueueRequest(request, data, directItem);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void sendVideo(@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final Uri uri) {
|
|
MediaUtils.getVideoInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener<MediaUtils.VideoInfo>() {
|
|
@Override
|
|
public void onLoad(@Nullable final MediaUtils.VideoInfo info) {
|
|
if (info == null) {
|
|
data.postValue(Resource.error("Could not get the video info", null));
|
|
return;
|
|
}
|
|
sendVideo(data, uri, info.size, info.duration, info.width, info.height);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void sendVideo(@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final Uri uri,
|
|
final long byteLength,
|
|
final long duration,
|
|
final int width,
|
|
final int height) {
|
|
if (duration > 60000) {
|
|
// instagram does not allow uploading videos longer than 60 secs for Direct messages
|
|
data.postValue(Resource.error(R.string.dms_ERROR_VIDEO_TOO_LONG, null));
|
|
return;
|
|
}
|
|
final Long userId = getCurrentUserId(data);
|
|
if (userId == null) return;
|
|
final String clientContext = UUID.randomUUID().toString();
|
|
final DirectItem directItem = DirectItemFactory.createImageOrVideo(userId, clientContext, uri, width, height, true);
|
|
directItem.setPending(true);
|
|
addItems(0, Collections.singletonList(directItem));
|
|
data.postValue(Resource.loading(directItem));
|
|
final UploadVideoOptions uploadDmVideoOptions = MediaUploadHelper.createUploadDmVideoOptions(byteLength, duration, width, height);
|
|
MediaUploader.uploadVideo(uri, contentResolver, uploadDmVideoOptions, new MediaUploader.OnMediaUploadCompleteListener() {
|
|
@Override
|
|
public void onUploadComplete(final MediaUploader.MediaUploadResponse response) {
|
|
// Log.d(TAG, "onUploadComplete: " + response);
|
|
if (handleInvalidResponse(data, response)) return;
|
|
final UploadFinishOptions uploadFinishOptions = new UploadFinishOptions()
|
|
.setUploadId(uploadDmVideoOptions.getUploadId())
|
|
.setSourceType("2")
|
|
.setVideoOptions(new UploadFinishOptions.VideoOptions().setLength(duration / 1000f));
|
|
final Call<String> uploadFinishRequest = mediaService.uploadFinish(uploadFinishOptions);
|
|
uploadFinishRequest.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (response.isSuccessful()) {
|
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastVideo(
|
|
clientContext,
|
|
threadIdOrUserIds,
|
|
uploadDmVideoOptions.getUploadId(),
|
|
"",
|
|
true
|
|
);
|
|
enqueueRequest(request, data, directItem);
|
|
return;
|
|
}
|
|
if (response.errorBody() != null) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.error("uploadFinishRequest was not successful and response error body was null", directItem));
|
|
Log.e(TAG, "uploadFinishRequest was not successful and response error body was null");
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void enqueueRequest(@NonNull final Call<DirectThreadBroadcastResponse> request,
|
|
@NonNull final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final DirectItem directItem) {
|
|
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
|
@NonNull final Response<DirectThreadBroadcastResponse> response) {
|
|
if (response.isSuccessful()) {
|
|
final DirectThreadBroadcastResponse broadcastResponse = response.body();
|
|
if (broadcastResponse == null) {
|
|
data.postValue(Resource.error(R.string.generic_null_response, directItem));
|
|
Log.e(TAG, "enqueueRequest: onResponse: response body is null");
|
|
return;
|
|
}
|
|
final String payloadClientContext;
|
|
final long timestamp;
|
|
final String itemId;
|
|
final DirectThreadBroadcastResponsePayload payload = broadcastResponse.getPayload();
|
|
if (payload == null) {
|
|
final List<DirectThreadBroadcastResponseMessageMetadata> messageMetadata = broadcastResponse.getMessageMetadata();
|
|
if (messageMetadata == null || messageMetadata.isEmpty()) {
|
|
data.postValue(Resource.success(directItem));
|
|
return;
|
|
}
|
|
final DirectThreadBroadcastResponseMessageMetadata metadata = messageMetadata.get(0);
|
|
payloadClientContext = metadata.getClientContext();
|
|
itemId = metadata.getItemId();
|
|
timestamp = metadata.getTimestamp();
|
|
} else {
|
|
payloadClientContext = payload.getClientContext();
|
|
timestamp = payload.getTimestamp();
|
|
itemId = payload.getItemId();
|
|
}
|
|
updateItemSent(payloadClientContext, timestamp, itemId);
|
|
data.postValue(Resource.success(directItem));
|
|
return;
|
|
}
|
|
if (response.errorBody() != null) {
|
|
handleErrorBody(call, response, data);
|
|
}
|
|
data.postValue(Resource.error(R.string.generic_failed_request, directItem));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
|
@NonNull final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), directItem));
|
|
Log.e(TAG, "enqueueRequest: onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void updateItemSent(final String clientContext, final long timestamp, final String itemId) {
|
|
if (clientContext == null) return;
|
|
List<DirectItem> list = this.items.getValue();
|
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
|
final int index = Iterables.indexOf(list, item -> {
|
|
if (item == null) return false;
|
|
return item.getClientContext().equals(clientContext);
|
|
});
|
|
if (index < 0) return;
|
|
final DirectItem directItem = list.get(index);
|
|
try {
|
|
final DirectItem itemClone = (DirectItem) directItem.clone();
|
|
itemClone.setItemId(itemId);
|
|
itemClone.setPending(false);
|
|
itemClone.setTimestamp(timestamp);
|
|
list.set(index, itemClone);
|
|
inboxManager.setItemsToThread(threadId, list);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "updateItemSent: ", e);
|
|
}
|
|
}
|
|
|
|
private void handleErrorBody(@NonNull final Call<?> call,
|
|
@NonNull final Response<?> response,
|
|
final MutableLiveData<Resource<Object>> data) {
|
|
try {
|
|
final String string = response.errorBody() != null ? response.errorBody().string() : "";
|
|
final String msg = String.format(Locale.US,
|
|
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
|
call.request().url().toString(),
|
|
response.code(),
|
|
string);
|
|
if (data != null) {
|
|
data.postValue(Resource.error(msg, null));
|
|
}
|
|
Log.e(TAG, msg);
|
|
} catch (IOException e) {
|
|
if (data != null) {
|
|
data.postValue(Resource.error(e.getMessage(), null));
|
|
}
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
private boolean handleInvalidResponse(final MutableLiveData<Resource<Object>> data,
|
|
@NonNull final MediaUploader.MediaUploadResponse response) {
|
|
final JSONObject responseJson = response.getResponse();
|
|
if (responseJson == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
|
data.postValue(Resource.error(R.string.generic_not_ok_response, null));
|
|
return true;
|
|
}
|
|
final String status = responseJson.optString("status");
|
|
if (TextUtils.isEmpty(status) || !status.equals("ok")) {
|
|
data.postValue(Resource.error(R.string.generic_not_ok_response, null));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private int getItemIndex(final DirectItem item, final List<DirectItem> list) {
|
|
return Iterables.indexOf(list, i -> i != null && i.getItemId().equals(item.getItemId()));
|
|
}
|
|
|
|
@Nullable
|
|
private DirectItem getItem(final String itemId) {
|
|
if (itemId == null) return null;
|
|
final List<DirectItem> items = this.items.getValue();
|
|
if (items == null) return null;
|
|
return items.stream()
|
|
.filter(directItem -> directItem.getItemId().equals(itemId))
|
|
.findFirst()
|
|
.orElse(null);
|
|
}
|
|
|
|
private void handleBroadcastReactionRequest(final MutableLiveData<Resource<Object>> data,
|
|
final DirectItem item,
|
|
@NonNull final Call<DirectThreadBroadcastResponse> request) {
|
|
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
|
@NonNull final Response<DirectThreadBroadcastResponse> response) {
|
|
if (!response.isSuccessful()) {
|
|
if (response.errorBody() != null) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.error(R.string.generic_failed_request, item));
|
|
return;
|
|
}
|
|
final DirectThreadBroadcastResponse body = response.body();
|
|
if (body == null) {
|
|
data.postValue(Resource.error(R.string.generic_null_response, item));
|
|
}
|
|
// otherwise nothing to do? maybe update the timestamp in the emoji?
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadBroadcastResponse> call, @NonNull final Throwable t) {
|
|
data.postValue(Resource.error(t.getMessage(), item));
|
|
Log.e(TAG, "enqueueRequest: onFailure: ", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void stopCurrentRequest() {
|
|
if (chatsRequest == null || chatsRequest.isExecuted() || chatsRequest.isCanceled()) {
|
|
return;
|
|
}
|
|
chatsRequest.cancel();
|
|
fetching.postValue(Resource.success(new Object()));
|
|
}
|
|
|
|
@Nullable
|
|
private Long getCurrentUserId(final MutableLiveData<Resource<Object>> data) {
|
|
if (currentUser == null || currentUser.getPk() <= 0) {
|
|
data.postValue(Resource.error(R.string.dms_ERROR_INVALID_USER, null));
|
|
return null;
|
|
}
|
|
return currentUser.getPk();
|
|
}
|
|
|
|
public void removeThread() {
|
|
final Boolean pendingValue = pending.getValue();
|
|
final boolean threadInPending = pendingValue != null && pendingValue;
|
|
inboxManager.removeThread(threadId);
|
|
if (threadInPending) {
|
|
final Integer totalValue = inboxManager.getPendingRequestsTotal().getValue();
|
|
if (totalValue == null) return;
|
|
inboxManager.setPendingRequestsTotal(totalValue - 1);
|
|
}
|
|
}
|
|
|
|
public LiveData<Resource<Object>> updateTitle(final String newTitle) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Call<DirectThreadDetailsChangeResponse> addUsersRequest = service.updateTitle(threadId, newTitle.trim());
|
|
handleDetailsChangeRequest(data, addUsersRequest);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> addMembers(final Set<User> users) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
final Call<DirectThreadDetailsChangeResponse> addUsersRequest = service.addUsers(threadId,
|
|
users.stream()
|
|
.filter(Objects::nonNull)
|
|
.map(User::getPk)
|
|
.collect(Collectors.toList()));
|
|
handleDetailsChangeRequest(data, addUsersRequest);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> removeMember(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) {
|
|
data.postValue(Resource.error("user is null!", null));
|
|
return data;
|
|
}
|
|
final Call<String> request = service.removeUsers(threadId, Collections.singleton(user.getPk()));
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
List<User> activeUsers = users.getValue();
|
|
List<User> leftUsersValue = leftUsers.getValue();
|
|
if (activeUsers == null) {
|
|
activeUsers = Collections.emptyList();
|
|
}
|
|
if (leftUsersValue == null) {
|
|
leftUsersValue = Collections.emptyList();
|
|
}
|
|
final List<User> updatedActiveUsers = activeUsers.stream()
|
|
.filter(Objects::nonNull)
|
|
.filter(u -> u.getPk() != user.getPk())
|
|
.collect(Collectors.toList());
|
|
final ImmutableList.Builder<User> updatedLeftUsersBuilder = ImmutableList.<User>builder().addAll(leftUsersValue);
|
|
if (!leftUsersValue.contains(user)) {
|
|
updatedLeftUsersBuilder.add(user);
|
|
}
|
|
final ImmutableList<User> updatedLeftUsers = updatedLeftUsersBuilder.build();
|
|
setThreadUsers(updatedActiveUsers, updatedLeftUsers);
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public boolean isAdmin(final User user) {
|
|
if (user == null) return false;
|
|
final List<Long> adminUserIdsValue = adminUserIds.getValue();
|
|
return adminUserIdsValue != null && adminUserIdsValue.contains(user.getPk());
|
|
}
|
|
|
|
public LiveData<Resource<Object>> makeAdmin(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
if (isAdmin(user)) return data;
|
|
final Call<String> request = service.addAdmins(threadId, Collections.singleton(user.getPk()));
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
final List<Long> currentAdminIds = adminUserIds.getValue();
|
|
final ImmutableList<Long> updatedAdminIds = ImmutableList.<Long>builder()
|
|
.addAll(currentAdminIds != null ? currentAdminIds : Collections.emptyList())
|
|
.add(user.getPk())
|
|
.build();
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setAdminUserIds(updatedAdminIds);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> removeAdmin(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
if (!isAdmin(user)) return data;
|
|
final Call<String> request = service.removeAdmins(threadId, Collections.singleton(user.getPk()));
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
final List<Long> currentAdmins = adminUserIds.getValue();
|
|
if (currentAdmins == null) return;
|
|
final List<Long> updatedAdminUserIds = currentAdmins.stream()
|
|
.filter(Objects::nonNull)
|
|
.filter(userId1 -> userId1 != user.getPk())
|
|
.collect(Collectors.toList());
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setAdminUserIds(updatedAdminUserIds);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> mute() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean muted = isMuted.getValue();
|
|
if (muted != null && muted) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<String> request = service.mute(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setMuted(true);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> unmute() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean muted = isMuted.getValue();
|
|
if (muted != null && !muted) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<String> request = service.unmute(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setMuted(false);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> muteMentions() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean mentionsMuted = isMentionsMuted.getValue();
|
|
if (mentionsMuted != null && mentionsMuted) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<String> request = service.muteMentions(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setMentionsMuted(true);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> unmuteMentions() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean mentionsMuted = isMentionsMuted.getValue();
|
|
if (mentionsMuted != null && !mentionsMuted) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<String> request = service.unmuteMentions(threadId);
|
|
request.enqueue(new Callback<String>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setMentionsMuted(false);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> blockUser(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
friendshipService.block(user.getPk(), new ServiceCallback<FriendshipChangeResponse>() {
|
|
@Override
|
|
public void onSuccess(final FriendshipChangeResponse result) {
|
|
refreshChats();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> unblockUser(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
friendshipService.unblock(user.getPk(), new ServiceCallback<FriendshipChangeResponse>() {
|
|
@Override
|
|
public void onSuccess(final FriendshipChangeResponse result) {
|
|
refreshChats();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> restrictUser(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
friendshipService.toggleRestrict(user.getPk(), true, new ServiceCallback<FriendshipRestrictResponse>() {
|
|
@Override
|
|
public void onSuccess(final FriendshipRestrictResponse result) {
|
|
refreshChats();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> unRestrictUser(final User user) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
if (user == null) return data;
|
|
friendshipService.toggleRestrict(user.getPk(), false, new ServiceCallback<FriendshipRestrictResponse>() {
|
|
@Override
|
|
public void onSuccess(final FriendshipRestrictResponse result) {
|
|
refreshChats();
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> approveUsers(final List<User> users) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = service
|
|
.approveParticipantRequests(threadId,
|
|
users.stream()
|
|
.filter(Objects::nonNull)
|
|
.map(User::getPk)
|
|
.collect(Collectors.toList()));
|
|
handleDetailsChangeRequest(data, approveUsersRequest, () -> pendingUserApproveDenySuccessAction(users));
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> denyUsers(final List<User> users) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = service
|
|
.declineParticipantRequests(threadId,
|
|
users.stream().map(User::getPk).collect(Collectors.toList()));
|
|
handleDetailsChangeRequest(data, approveUsersRequest, () -> pendingUserApproveDenySuccessAction(users));
|
|
return data;
|
|
}
|
|
|
|
private void pendingUserApproveDenySuccessAction(final List<User> users) {
|
|
final DirectThreadParticipantRequestsResponse pendingRequestsValue = pendingRequests.getValue();
|
|
if (pendingRequestsValue == null) return;
|
|
final List<User> pendingUsers = pendingRequestsValue.getUsers();
|
|
if (pendingUsers == null || pendingUsers.isEmpty()) return;
|
|
final List<User> filtered = pendingUsers.stream()
|
|
.filter(o -> !users.contains(o))
|
|
.collect(Collectors.toList());
|
|
try {
|
|
final DirectThreadParticipantRequestsResponse clone = (DirectThreadParticipantRequestsResponse) pendingRequestsValue.clone();
|
|
clone.setUsers(filtered);
|
|
final int totalParticipantRequests = clone.getTotalParticipantRequests();
|
|
clone.setTotalParticipantRequests(totalParticipantRequests > 0 ? totalParticipantRequests - 1 : 0);
|
|
pendingRequests.postValue(clone);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "pendingUserApproveDenySuccessAction: ", e);
|
|
}
|
|
}
|
|
|
|
public LiveData<Resource<Object>> approvalRequired() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean approvalRequiredToJoin = isApprovalRequiredToJoin.getValue();
|
|
if (approvalRequiredToJoin != null && approvalRequiredToJoin) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<DirectThreadDetailsChangeResponse> request = service.approvalRequired(threadId);
|
|
handleDetailsChangeRequest(data, request, () -> {
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setApprovalRequiredForNewMembers(true);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> approvalNotRequired() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Boolean approvalRequiredToJoin = isApprovalRequiredToJoin.getValue();
|
|
if (approvalRequiredToJoin != null && !approvalRequiredToJoin) {
|
|
data.postValue(Resource.success(new Object()));
|
|
return data;
|
|
}
|
|
final Call<DirectThreadDetailsChangeResponse> request = service.approvalNotRequired(threadId);
|
|
handleDetailsChangeRequest(data, request, () -> {
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setApprovalRequiredForNewMembers(false);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> leave() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Call<DirectThreadDetailsChangeResponse> request = service.leave(threadId);
|
|
handleDetailsChangeRequest(data, request);
|
|
return data;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> end() {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Call<DirectThreadDetailsChangeResponse> request = service.end(threadId);
|
|
handleDetailsChangeRequest(data, request, () -> {
|
|
final DirectThread currentThread = ThreadManager.this.thread.getValue();
|
|
if (currentThread == null) return;
|
|
try {
|
|
final DirectThread thread = (DirectThread) currentThread.clone();
|
|
thread.setInputMode(1);
|
|
inboxManager.setThread(threadId, thread);
|
|
} catch (CloneNotSupportedException e) {
|
|
Log.e(TAG, "onResponse: ", e);
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
|
|
final Call<DirectThreadDetailsChangeResponse> request) {
|
|
handleDetailsChangeRequest(data, request, null);
|
|
}
|
|
|
|
private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
|
|
final Call<DirectThreadDetailsChangeResponse> request,
|
|
@Nullable final OnSuccessAction action) {
|
|
request.enqueue(new Callback<DirectThreadDetailsChangeResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectThreadDetailsChangeResponse> call,
|
|
@NonNull final Response<DirectThreadDetailsChangeResponse> response) {
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
final DirectThreadDetailsChangeResponse changeResponse = response.body();
|
|
if (changeResponse == null) {
|
|
data.postValue(Resource.error(R.string.generic_null_response, null));
|
|
return;
|
|
}
|
|
data.postValue(Resource.success(new Object()));
|
|
final DirectThread thread = changeResponse.getThread();
|
|
if (thread != null) {
|
|
setThread(thread, true);
|
|
}
|
|
if (action != null) {
|
|
action.onSuccess();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectThreadDetailsChangeResponse> call, @NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
}
|
|
|
|
public LiveData<User> getInviter() {
|
|
return inviter;
|
|
}
|
|
|
|
public LiveData<Resource<Object>> markAsSeen(@NonNull final DirectItem directItem) {
|
|
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
|
data.postValue(Resource.loading(null));
|
|
final Call<DirectItemSeenResponse> request = service.markAsSeen(threadId, directItem);
|
|
request.enqueue(new Callback<DirectItemSeenResponse>() {
|
|
@Override
|
|
public void onResponse(@NonNull final Call<DirectItemSeenResponse> call,
|
|
@NonNull final Response<DirectItemSeenResponse> response) {
|
|
if (currentUser == null) return;
|
|
if (!response.isSuccessful()) {
|
|
handleErrorBody(call, response, data);
|
|
return;
|
|
}
|
|
final DirectItemSeenResponse seenResponse = response.body();
|
|
if (seenResponse == null) {
|
|
data.postValue(Resource.error(R.string.generic_null_response, null));
|
|
return;
|
|
}
|
|
inboxManager.fetchUnseenCount();
|
|
final DirectItemSeenResponsePayload payload = seenResponse.getPayload();
|
|
if (payload == null) return;
|
|
final String timestamp = payload.getTimestamp();
|
|
final DirectThread thread = ThreadManager.this.thread.getValue();
|
|
if (thread == null) return;
|
|
Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();
|
|
lastSeenAt = lastSeenAt == null ? new HashMap<>() : new HashMap<>(lastSeenAt);
|
|
lastSeenAt.put(currentUser.getPk(), new DirectThreadLastSeenAt(timestamp, directItem.getItemId()));
|
|
thread.setLastSeenAt(lastSeenAt);
|
|
setThread(thread, true);
|
|
data.postValue(Resource.success(new Object()));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull final Call<DirectItemSeenResponse> call,
|
|
@NonNull final Throwable t) {
|
|
Log.e(TAG, "onFailure: ", t);
|
|
data.postValue(Resource.error(t.getMessage(), null));
|
|
}
|
|
});
|
|
return data;
|
|
}
|
|
|
|
private interface OnSuccessAction {
|
|
void onSuccess();
|
|
}
|
|
}
|