mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-12 17:57:29 +00:00
Convert DM managers to kotlin
This commit is contained in:
parent
229cde6074
commit
59750b1026
@ -1088,7 +1088,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager();
|
||||
final InboxManager inboxManager = DirectMessagesManager.INSTANCE.getInboxManager();
|
||||
final DirectThread thread = response.body();
|
||||
if (!inboxManager.containsThread(thread.getThreadId())) {
|
||||
thread.setTemp(true);
|
||||
|
@ -1,275 +1,222 @@
|
||||
package awais.instagrabber.managers;
|
||||
package awais.instagrabber.managers
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.util.Log;
|
||||
import android.content.ContentResolver
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import awais.instagrabber.managers.ThreadManager.Companion.getInstance
|
||||
import awais.instagrabber.models.Resource
|
||||
import awais.instagrabber.models.Resource.Companion.error
|
||||
import awais.instagrabber.models.Resource.Companion.loading
|
||||
import awais.instagrabber.models.Resource.Companion.success
|
||||
import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds.Companion.of
|
||||
import awais.instagrabber.repositories.responses.User
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse
|
||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
|
||||
import awais.instagrabber.utils.Constants
|
||||
import awais.instagrabber.utils.Utils
|
||||
import awais.instagrabber.utils.getCsrfTokenFromCookie
|
||||
import awais.instagrabber.utils.getUserIdFromCookie
|
||||
import awais.instagrabber.webservices.DirectMessagesService
|
||||
import awais.instagrabber.webservices.DirectMessagesService.Companion.getInstance
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
object DirectMessagesManager {
|
||||
val inboxManager: InboxManager by lazy { InboxManager.getInstance(false) }
|
||||
val pendingInboxManager: InboxManager by lazy { InboxManager.getInstance(true) }
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
private val TAG = DirectMessagesManager::class.java.simpleName
|
||||
private val viewerId: Long
|
||||
private val deviceUuid: String
|
||||
private val csrfToken: String
|
||||
private val service: DirectMessagesService
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class DirectMessagesManager {
|
||||
private static final String TAG = DirectMessagesManager.class.getSimpleName();
|
||||
private static final Object LOCK = new Object();
|
||||
|
||||
private static DirectMessagesManager instance;
|
||||
|
||||
private final InboxManager inboxManager;
|
||||
private final InboxManager pendingInboxManager;
|
||||
|
||||
private DirectMessagesService service;
|
||||
|
||||
public static DirectMessagesManager getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (LOCK) {
|
||||
if (instance == null) {
|
||||
instance = new DirectMessagesManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private DirectMessagesManager() {
|
||||
inboxManager = InboxManager.getInstance(false);
|
||||
pendingInboxManager = InboxManager.getInstance(true);
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final long viewerId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (csrfToken == null) return;
|
||||
service = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid);
|
||||
}
|
||||
|
||||
public void moveThreadFromPending(@NonNull final String threadId) {
|
||||
final List<DirectThread> pendingThreads = pendingInboxManager.getThreads().getValue();
|
||||
if (pendingThreads == null) return;
|
||||
final int index = Iterables.indexOf(pendingThreads, t -> t != null && t.getThreadId().equals(threadId));
|
||||
if (index < 0) return;
|
||||
final DirectThread thread = pendingThreads.get(index);
|
||||
final DirectItem threadFirstDirectItem = thread.getFirstDirectItem();
|
||||
if (threadFirstDirectItem == null) return;
|
||||
final List<DirectThread> threads = inboxManager.getThreads().getValue();
|
||||
int insertIndex = 0;
|
||||
fun moveThreadFromPending(threadId: String) {
|
||||
val pendingThreads = pendingInboxManager.threads.value ?: return
|
||||
val index = pendingThreads.indexOfFirst { it.threadId == threadId }
|
||||
if (index < 0) return
|
||||
val thread = pendingThreads[index]
|
||||
val threadFirstDirectItem = thread.firstDirectItem ?: return
|
||||
val threads = inboxManager.threads.value
|
||||
var insertIndex = 0
|
||||
if (threads != null) {
|
||||
for (final DirectThread tempThread : threads) {
|
||||
final DirectItem firstDirectItem = tempThread.getFirstDirectItem();
|
||||
if (firstDirectItem == null) continue;
|
||||
final long timestamp = firstDirectItem.getTimestamp();
|
||||
for (tempThread in threads) {
|
||||
val firstDirectItem = tempThread.firstDirectItem ?: continue
|
||||
val timestamp = firstDirectItem.getTimestamp()
|
||||
if (timestamp < threadFirstDirectItem.getTimestamp()) {
|
||||
break;
|
||||
break
|
||||
}
|
||||
insertIndex++;
|
||||
insertIndex++
|
||||
}
|
||||
}
|
||||
thread.setPending(false);
|
||||
inboxManager.addThread(thread, insertIndex);
|
||||
pendingInboxManager.removeThread(threadId);
|
||||
final Integer currentTotal = inboxManager.getPendingRequestsTotal().getValue();
|
||||
if (currentTotal == null) return;
|
||||
inboxManager.setPendingRequestsTotal(currentTotal - 1);
|
||||
thread.pending = false
|
||||
inboxManager.addThread(thread, insertIndex)
|
||||
pendingInboxManager.removeThread(threadId)
|
||||
val currentTotal = inboxManager.getPendingRequestsTotal().value ?: return
|
||||
inboxManager.setPendingRequestsTotal(currentTotal - 1)
|
||||
}
|
||||
|
||||
public InboxManager getInboxManager() {
|
||||
return inboxManager;
|
||||
fun getThreadManager(
|
||||
threadId: String,
|
||||
pending: Boolean,
|
||||
currentUser: User,
|
||||
contentResolver: ContentResolver,
|
||||
): ThreadManager {
|
||||
return getInstance(threadId, pending, currentUser, contentResolver, viewerId, csrfToken, deviceUuid)
|
||||
}
|
||||
|
||||
public InboxManager getPendingInboxManager() {
|
||||
return pendingInboxManager;
|
||||
}
|
||||
|
||||
public ThreadManager getThreadManager(@NonNull final String threadId,
|
||||
final boolean pending,
|
||||
@NonNull final User currentUser,
|
||||
@NonNull final ContentResolver contentResolver) {
|
||||
return ThreadManager.getInstance(threadId, pending, currentUser, contentResolver);
|
||||
}
|
||||
|
||||
public void createThread(final long userPk,
|
||||
@Nullable final Function<DirectThread, Void> callback) {
|
||||
if (service == null) return;
|
||||
final Call<DirectThread> createThreadRequest = service.createThread(Collections.singletonList(userPk), 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) {
|
||||
fun createThread(
|
||||
userPk: Long,
|
||||
callback: ((DirectThread) -> Unit)?,
|
||||
) {
|
||||
val createThreadRequest = service.createThread(listOf(userPk), null)
|
||||
createThreadRequest.enqueue(object : Callback<DirectThread?> {
|
||||
override fun onResponse(call: Call<DirectThread?>, response: Response<DirectThread?>) {
|
||||
if (!response.isSuccessful) {
|
||||
val errorBody = response.errorBody()
|
||||
if (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);
|
||||
val string = errorBody.string()
|
||||
val msg = String.format(Locale.US,
|
||||
"onResponse: url: %s, responseCode: %d, errorBody: %s",
|
||||
call.request().url().toString(),
|
||||
response.code(),
|
||||
string)
|
||||
Log.e(TAG, msg)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "onResponse: ", e)
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
Log.e(TAG, "onResponse: request was not successful and response error body was null");
|
||||
return;
|
||||
Log.e(TAG, "onResponse: request was not successful and response error body was null")
|
||||
return
|
||||
}
|
||||
final DirectThread thread = response.body();
|
||||
val thread = response.body()
|
||||
if (thread == null) {
|
||||
Log.e(TAG, "onResponse: thread is null");
|
||||
return;
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.apply(thread);
|
||||
Log.e(TAG, "onResponse: thread is null")
|
||||
return
|
||||
}
|
||||
callback?.invoke(thread)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectThread> call, @NonNull final Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
override fun onFailure(call: Call<DirectThread?>, t: Throwable) {}
|
||||
})
|
||||
}
|
||||
|
||||
public void sendMedia(@NonNull final Set<RankedRecipient> recipients, final String mediaId) {
|
||||
final int[] resultsCount = {0};
|
||||
final Function<Void, Void> callback = unused -> {
|
||||
resultsCount[0]++;
|
||||
if (resultsCount[0] == recipients.size()) {
|
||||
inboxManager.refresh();
|
||||
fun sendMedia(recipients: Set<RankedRecipient>, mediaId: String) {
|
||||
val resultsCount = intArrayOf(0)
|
||||
val callback: () -> Unit = {
|
||||
resultsCount[0]++
|
||||
if (resultsCount[0] == recipients.size) {
|
||||
inboxManager.refresh()
|
||||
}
|
||||
return null;
|
||||
};
|
||||
for (final RankedRecipient recipient : recipients) {
|
||||
if (recipient == null) continue;
|
||||
sendMedia(recipient, mediaId, false, callback);
|
||||
}
|
||||
for (recipient in recipients) {
|
||||
sendMedia(recipient, mediaId, false, callback)
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMedia(@NonNull final RankedRecipient recipient, final String mediaId) {
|
||||
sendMedia(recipient, mediaId, true, null);
|
||||
fun sendMedia(recipient: RankedRecipient, mediaId: String) {
|
||||
sendMedia(recipient, mediaId, true, null)
|
||||
}
|
||||
|
||||
private void sendMedia(@NonNull final RankedRecipient recipient,
|
||||
@NonNull final String mediaId,
|
||||
final boolean refreshInbox,
|
||||
@Nullable final Function<Void, Void> callback) {
|
||||
if (recipient.getThread() == null && recipient.getUser() != null) {
|
||||
private fun sendMedia(
|
||||
recipient: RankedRecipient,
|
||||
mediaId: String,
|
||||
refreshInbox: Boolean,
|
||||
callback: (() -> Unit)?,
|
||||
) {
|
||||
if (recipient.thread == null && recipient.user != null) {
|
||||
// create thread and forward
|
||||
createThread(recipient.getUser().getPk(), directThread -> {
|
||||
sendMedia(directThread, mediaId, unused -> {
|
||||
createThread(recipient.user.pk) { (threadId) ->
|
||||
val threadIdTemp = threadId ?: return@createThread
|
||||
sendMedia(threadIdTemp, mediaId) {
|
||||
if (refreshInbox) {
|
||||
inboxManager.refresh();
|
||||
inboxManager.refresh()
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
callback?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (recipient.getThread() == null) return;
|
||||
if (recipient.thread == null) return
|
||||
// just forward
|
||||
final DirectThread thread = recipient.getThread();
|
||||
sendMedia(thread, mediaId, unused -> {
|
||||
val thread = recipient.thread
|
||||
val threadId = thread.threadId ?: return
|
||||
sendMedia(threadId, mediaId) {
|
||||
if (refreshInbox) {
|
||||
inboxManager.refresh();
|
||||
inboxManager.refresh()
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
callback?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public LiveData<Resource<Object>> sendMedia(@NonNull final DirectThread thread,
|
||||
@NonNull final String mediaId,
|
||||
@Nullable final Function<Void, Void> callback) {
|
||||
return sendMedia(thread.getThreadId(), mediaId, callback);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public LiveData<Resource<Object>> sendMedia(@NonNull final String threadId,
|
||||
@NonNull final String mediaId,
|
||||
@Nullable final Function<Void, Void> callback) {
|
||||
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
|
||||
data.postValue(Resource.loading(null));
|
||||
final Call<DirectThreadBroadcastResponse> request = service.broadcastMediaShare(
|
||||
UUID.randomUUID().toString(),
|
||||
ThreadIdOrUserIds.of(threadId),
|
||||
mediaId
|
||||
);
|
||||
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
||||
@NonNull final Response<DirectThreadBroadcastResponse> response) {
|
||||
if (response.isSuccessful()) {
|
||||
data.postValue(Resource.success(new Object()));
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
}
|
||||
return;
|
||||
private fun sendMedia(
|
||||
threadId: String,
|
||||
mediaId: String,
|
||||
callback: (() -> Unit)?,
|
||||
): LiveData<Resource<Any?>> {
|
||||
val data = MutableLiveData<Resource<Any?>>()
|
||||
data.postValue(loading(null))
|
||||
val request = service.broadcastMediaShare(
|
||||
UUID.randomUUID().toString(),
|
||||
of(threadId),
|
||||
mediaId
|
||||
)
|
||||
request.enqueue(object : Callback<DirectThreadBroadcastResponse?> {
|
||||
override fun onResponse(
|
||||
call: Call<DirectThreadBroadcastResponse?>,
|
||||
response: Response<DirectThreadBroadcastResponse?>,
|
||||
) {
|
||||
if (response.isSuccessful) {
|
||||
data.postValue(success(Any()))
|
||||
callback?.invoke()
|
||||
return
|
||||
}
|
||||
if (response.errorBody() != null) {
|
||||
val errorBody = response.errorBody()
|
||||
if (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);
|
||||
data.postValue(Resource.error(msg, null));
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "onResponse: ", e);
|
||||
data.postValue(Resource.error(e.getMessage(), null));
|
||||
val string = errorBody.string()
|
||||
val 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(error(msg, null))
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "onResponse: ", e)
|
||||
data.postValue(error(e.message, null))
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final String msg = "onResponse: request was not successful and response error body was null";
|
||||
Log.e(TAG, msg);
|
||||
data.postValue(Resource.error(msg, null));
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
callback?.invoke()
|
||||
return
|
||||
}
|
||||
val msg = "onResponse: request was not successful and response error body was null"
|
||||
Log.e(TAG, msg)
|
||||
data.postValue(error(msg, null))
|
||||
callback?.invoke()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectThreadBroadcastResponse> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
data.postValue(Resource.error(t.getMessage(), null));
|
||||
if (callback != null) {
|
||||
callback.apply(null);
|
||||
}
|
||||
override fun onFailure(call: Call<DirectThreadBroadcastResponse?>, t: Throwable) {
|
||||
Log.e(TAG, "onFailure: ", t)
|
||||
data.postValue(error(t.message, null))
|
||||
callback?.invoke()
|
||||
}
|
||||
});
|
||||
return data;
|
||||
})
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val cookie = Utils.settingsHelper.getString(Constants.COOKIE)
|
||||
viewerId = getUserIdFromCookie(cookie)
|
||||
deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)
|
||||
val csrfToken = getCsrfTokenFromCookie(cookie)
|
||||
require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { "User is not logged in!" }
|
||||
this.csrfToken = csrfToken
|
||||
service = getInstance(csrfToken, viewerId, deviceUuid)
|
||||
}
|
||||
}
|
@ -1,382 +1,349 @@
|
||||
package awais.instagrabber.managers;
|
||||
package awais.instagrabber.managers
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import awais.instagrabber.R
|
||||
import awais.instagrabber.models.Resource
|
||||
import awais.instagrabber.models.Resource.Companion.error
|
||||
import awais.instagrabber.models.Resource.Companion.loading
|
||||
import awais.instagrabber.models.Resource.Companion.success
|
||||
import awais.instagrabber.repositories.responses.User
|
||||
import awais.instagrabber.repositories.responses.directmessages.*
|
||||
import awais.instagrabber.utils.Constants
|
||||
import awais.instagrabber.utils.Utils
|
||||
import awais.instagrabber.utils.getCsrfTokenFromCookie
|
||||
import awais.instagrabber.utils.getUserIdFromCookie
|
||||
import awais.instagrabber.webservices.DirectMessagesService
|
||||
import awais.instagrabber.webservices.DirectMessagesService.Companion.getInstance
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.common.cache.CacheLoader
|
||||
import com.google.common.collect.ImmutableList
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
class InboxManager private constructor(private val pending: Boolean) {
|
||||
private val inbox = MutableLiveData<Resource<DirectInbox?>>()
|
||||
private val unseenCount = MutableLiveData<Resource<Int?>>()
|
||||
private val pendingRequestsTotal = MutableLiveData(0)
|
||||
val threads: LiveData<List<DirectThread>>
|
||||
private val service: DirectMessagesService
|
||||
private var inboxRequest: Call<DirectInboxResponse?>? = null
|
||||
private var unseenCountRequest: Call<DirectBadgeCount?>? = null
|
||||
private var seqId: Long = 0
|
||||
private var cursor: String? = null
|
||||
private var hasOlder = true
|
||||
var viewer: User? = null
|
||||
private set
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
fun getInbox(): LiveData<Resource<DirectInbox?>> {
|
||||
return Transformations.distinctUntilChanged(inbox)
|
||||
}
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
fun getUnseenCount(): LiveData<Resource<Int?>> {
|
||||
return Transformations.distinctUntilChanged(unseenCount)
|
||||
}
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
fun getPendingRequestsTotal(): LiveData<Int> {
|
||||
return Transformations.distinctUntilChanged(pendingRequestsTotal)
|
||||
}
|
||||
|
||||
import static androidx.lifecycle.Transformations.distinctUntilChanged;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
fun fetchInbox() {
|
||||
val inboxResource = inbox.value
|
||||
if (inboxResource != null && inboxResource.status === Resource.Status.LOADING || !hasOlder) return
|
||||
stopCurrentInboxRequest()
|
||||
inbox.postValue(loading(currentDirectInbox))
|
||||
inboxRequest = if (pending) service.fetchPendingInbox(cursor, seqId) else service.fetchInbox(cursor, seqId)
|
||||
inboxRequest?.enqueue(object : Callback<DirectInboxResponse?> {
|
||||
override fun onResponse(call: Call<DirectInboxResponse?>, response: Response<DirectInboxResponse?>) {
|
||||
val body = response.body()
|
||||
if (body == null) {
|
||||
Log.e(TAG, "parseInboxResponse: Response is null")
|
||||
inbox.postValue(error(R.string.generic_null_response, currentDirectInbox))
|
||||
hasOlder = false
|
||||
return
|
||||
}
|
||||
parseInboxResponse(body)
|
||||
}
|
||||
|
||||
public final class InboxManager {
|
||||
private static final String TAG = InboxManager.class.getSimpleName();
|
||||
private static final LoadingCache<String, Object> THREAD_LOCKS = CacheBuilder
|
||||
override fun onFailure(call: Call<DirectInboxResponse?>, t: Throwable) {
|
||||
Log.e(TAG, "Failed fetching dm inbox", t)
|
||||
inbox.postValue(error(t.message, currentDirectInbox))
|
||||
hasOlder = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchUnseenCount() {
|
||||
val unseenCountResource = unseenCount.value
|
||||
if (unseenCountResource != null && unseenCountResource.status === Resource.Status.LOADING) return
|
||||
stopCurrentUnseenCountRequest()
|
||||
unseenCount.postValue(loading(currentUnseenCount))
|
||||
unseenCountRequest = service.fetchUnseenCount()
|
||||
unseenCountRequest?.enqueue(object : Callback<DirectBadgeCount?> {
|
||||
override fun onResponse(call: Call<DirectBadgeCount?>, response: Response<DirectBadgeCount?>) {
|
||||
val directBadgeCount = response.body()
|
||||
if (directBadgeCount == null) {
|
||||
Log.e(TAG, "onResponse: directBadgeCount Response is null")
|
||||
unseenCount.postValue(error(R.string.dms_inbox_error_null_count, currentUnseenCount))
|
||||
return
|
||||
}
|
||||
unseenCount.postValue(success(directBadgeCount.badgeCount))
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<DirectBadgeCount?>, t: Throwable) {
|
||||
Log.e(TAG, "Failed fetching unseen count", t)
|
||||
unseenCount.postValue(error(t.message, currentUnseenCount))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
cursor = null
|
||||
seqId = 0
|
||||
hasOlder = true
|
||||
fetchInbox()
|
||||
if (!pending) {
|
||||
fetchUnseenCount()
|
||||
}
|
||||
}
|
||||
|
||||
private val currentDirectInbox: DirectInbox?
|
||||
get() {
|
||||
val inboxResource = inbox.value
|
||||
return inboxResource?.data
|
||||
}
|
||||
|
||||
private fun parseInboxResponse(response: DirectInboxResponse) {
|
||||
if (response.status != "ok") {
|
||||
Log.e(TAG, "DM inbox fetch response: status not ok")
|
||||
inbox.postValue(error(R.string.generic_not_ok_response, currentDirectInbox))
|
||||
hasOlder = false
|
||||
return
|
||||
}
|
||||
seqId = response.seqId
|
||||
if (viewer == null) {
|
||||
viewer = response.viewer
|
||||
}
|
||||
val inbox = response.inbox ?: return
|
||||
if (!cursor.isNullOrBlank()) {
|
||||
val currentDirectInbox = currentDirectInbox
|
||||
currentDirectInbox?.let {
|
||||
val threads = it.threads
|
||||
val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)
|
||||
threadsCopy.addAll(inbox.threads ?: emptyList())
|
||||
inbox.threads = threads
|
||||
}
|
||||
}
|
||||
this.inbox.postValue(success(inbox))
|
||||
cursor = inbox.oldestCursor
|
||||
hasOlder = inbox.hasOlder
|
||||
pendingRequestsTotal.postValue(response.pendingRequestsTotal)
|
||||
}
|
||||
|
||||
fun setThread(
|
||||
threadId: String,
|
||||
thread: DirectThread,
|
||||
) {
|
||||
val inbox = currentDirectInbox ?: return
|
||||
val index = getThreadIndex(threadId, inbox)
|
||||
setThread(inbox, index, thread)
|
||||
}
|
||||
|
||||
private fun setThread(
|
||||
inbox: DirectInbox,
|
||||
index: Int,
|
||||
thread: DirectThread,
|
||||
) {
|
||||
if (index < 0) return
|
||||
synchronized(this.inbox) {
|
||||
val threads = inbox.threads
|
||||
val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)
|
||||
threadsCopy[index] = thread
|
||||
try {
|
||||
val clone = inbox.clone() as DirectInbox
|
||||
clone.threads = threadsCopy
|
||||
this.inbox.postValue(success(clone))
|
||||
} catch (e: CloneNotSupportedException) {
|
||||
Log.e(TAG, "setThread: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addItemsToThread(
|
||||
threadId: String,
|
||||
insertIndex: Int,
|
||||
items: Collection<DirectItem>,
|
||||
) {
|
||||
val inbox = currentDirectInbox ?: return
|
||||
synchronized(THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
val index = getThreadIndex(threadId, inbox)
|
||||
if (index < 0) return
|
||||
val threads = inbox.threads ?: return
|
||||
val thread = threads[index]
|
||||
val threadItems = thread.items
|
||||
val list = if (threadItems == null) LinkedList() else LinkedList(threadItems)
|
||||
if (insertIndex >= 0) {
|
||||
list.addAll(insertIndex, items)
|
||||
} else {
|
||||
list.addAll(items)
|
||||
}
|
||||
try {
|
||||
val threadClone = thread.clone() as DirectThread
|
||||
threadClone.items = list
|
||||
setThread(inbox, index, threadClone)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "addItemsToThread: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setItemsToThread(
|
||||
threadId: String,
|
||||
updatedItems: List<DirectItem>,
|
||||
) {
|
||||
val inbox = currentDirectInbox ?: return
|
||||
synchronized(THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
val index = getThreadIndex(threadId, inbox)
|
||||
if (index < 0) return
|
||||
val threads = inbox.threads ?: return
|
||||
val thread = threads[index]
|
||||
try {
|
||||
val threadClone = thread.clone() as DirectThread
|
||||
threadClone.items = updatedItems
|
||||
setThread(inbox, index, threadClone)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "setItemsToThread: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThreadIndex(
|
||||
threadId: String,
|
||||
inbox: DirectInbox,
|
||||
): Int {
|
||||
val threads = inbox.threads
|
||||
return if (threads == null || threads.isEmpty()) {
|
||||
-1
|
||||
} else threads.indexOfFirst { it.threadId == threadId }
|
||||
}
|
||||
|
||||
private val currentUnseenCount: Int?
|
||||
get() {
|
||||
val unseenCountResource = unseenCount.value
|
||||
return unseenCountResource?.data
|
||||
}
|
||||
|
||||
private fun stopCurrentInboxRequest() {
|
||||
inboxRequest?.let {
|
||||
if (it.isCanceled || it.isExecuted) return
|
||||
it.cancel()
|
||||
}
|
||||
inboxRequest = null
|
||||
}
|
||||
|
||||
private fun stopCurrentUnseenCountRequest() {
|
||||
unseenCountRequest?.let {
|
||||
if (it.isCanceled || it.isExecuted) return
|
||||
it.cancel()
|
||||
}
|
||||
unseenCountRequest = null
|
||||
}
|
||||
|
||||
fun onDestroy() {
|
||||
stopCurrentInboxRequest()
|
||||
stopCurrentUnseenCountRequest()
|
||||
}
|
||||
|
||||
fun addThread(thread: DirectThread, insertIndex: Int) {
|
||||
if (insertIndex < 0) return
|
||||
synchronized(inbox) {
|
||||
val currentDirectInbox = currentDirectInbox ?: return
|
||||
val threads = currentDirectInbox.threads
|
||||
val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)
|
||||
threadsCopy.add(insertIndex, thread)
|
||||
try {
|
||||
val clone = currentDirectInbox.clone() as DirectInbox
|
||||
clone.threads = threadsCopy
|
||||
inbox.setValue(success(clone))
|
||||
} catch (e: CloneNotSupportedException) {
|
||||
Log.e(TAG, "setThread: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeThread(threadId: String) {
|
||||
synchronized(inbox) {
|
||||
val currentDirectInbox = currentDirectInbox ?: return
|
||||
val threads = currentDirectInbox.threads ?: return
|
||||
val threadsCopy = threads.asSequence().filter { it.threadId != threadId }.toList()
|
||||
try {
|
||||
val clone = currentDirectInbox.clone() as DirectInbox
|
||||
clone.threads = threadsCopy
|
||||
inbox.postValue(success(clone))
|
||||
} catch (e: CloneNotSupportedException) {
|
||||
Log.e(TAG, "setThread: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPendingRequestsTotal(total: Int) {
|
||||
pendingRequestsTotal.postValue(total)
|
||||
}
|
||||
|
||||
fun containsThread(threadId: String?): Boolean {
|
||||
if (threadId == null) return false
|
||||
synchronized(inbox) {
|
||||
val currentDirectInbox = currentDirectInbox ?: return false
|
||||
val threads = currentDirectInbox.threads ?: return false
|
||||
return threads.any { it.threadId == threadId }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = InboxManager::class.java.simpleName
|
||||
private val THREAD_LOCKS = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES) // max lock time ever expected
|
||||
.build(CacheLoader.from(Object::new));
|
||||
private static final Comparator<DirectThread> THREAD_COMPARATOR = (t1, t2) -> {
|
||||
final DirectItem t1FirstDirectItem = t1.getFirstDirectItem();
|
||||
final DirectItem t2FirstDirectItem = t2.getFirstDirectItem();
|
||||
if (t1FirstDirectItem == null && t2FirstDirectItem == null) return 0;
|
||||
if (t1FirstDirectItem == null) return 1;
|
||||
if (t2FirstDirectItem == null) return -1;
|
||||
return Long.compare(t2FirstDirectItem.getTimestamp(), t1FirstDirectItem.getTimestamp());
|
||||
};
|
||||
.build<String, Any>(CacheLoader.from<Any> { Object() })
|
||||
private val THREAD_COMPARATOR = Comparator { t1: DirectThread, t2: DirectThread ->
|
||||
val t1FirstDirectItem = t1.firstDirectItem
|
||||
val t2FirstDirectItem = t2.firstDirectItem
|
||||
if (t1FirstDirectItem == null && t2FirstDirectItem == null) return@Comparator 0
|
||||
if (t1FirstDirectItem == null) return@Comparator 1
|
||||
if (t2FirstDirectItem == null) return@Comparator -1
|
||||
t2FirstDirectItem.getTimestamp().compareTo(t1FirstDirectItem.getTimestamp())
|
||||
}
|
||||
|
||||
private final MutableLiveData<Resource<DirectInbox>> inbox = new MutableLiveData<>();
|
||||
private final MutableLiveData<Resource<Integer>> unseenCount = new MutableLiveData<>();
|
||||
private final MutableLiveData<Integer> pendingRequestsTotal = new MutableLiveData<>(0);
|
||||
|
||||
private final LiveData<List<DirectThread>> threads;
|
||||
private final DirectMessagesService service;
|
||||
private final boolean pending;
|
||||
|
||||
private Call<DirectInboxResponse> inboxRequest;
|
||||
private Call<DirectBadgeCount> unseenCountRequest;
|
||||
private long seqId;
|
||||
private String cursor;
|
||||
private boolean hasOlder = true;
|
||||
private User viewer;
|
||||
|
||||
@NonNull
|
||||
public static InboxManager getInstance(final boolean pending) {
|
||||
return new InboxManager(pending);
|
||||
fun getInstance(pending: Boolean): InboxManager {
|
||||
return InboxManager(pending)
|
||||
}
|
||||
}
|
||||
|
||||
private InboxManager(final boolean pending) {
|
||||
this.pending = pending;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final long userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (TextUtils.isEmpty(csrfToken)) {
|
||||
throw new IllegalArgumentException("csrfToken is empty!");
|
||||
} else if (userId == 0) {
|
||||
throw new IllegalArgumentException("user id invalid");
|
||||
} else if (TextUtils.isEmpty(deviceUuid)) {
|
||||
throw new IllegalArgumentException("device uuid is empty!");
|
||||
}
|
||||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid);
|
||||
init {
|
||||
val cookie = Utils.settingsHelper.getString(Constants.COOKIE)
|
||||
val viewerId = getUserIdFromCookie(cookie)
|
||||
val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)
|
||||
val csrfToken = getCsrfTokenFromCookie(cookie)
|
||||
require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { "User is not logged in!" }
|
||||
service = getInstance(csrfToken, viewerId, deviceUuid)
|
||||
|
||||
// Transformations
|
||||
threads = distinctUntilChanged(Transformations.map(inbox, inboxResource -> {
|
||||
threads = Transformations.distinctUntilChanged(Transformations.map(inbox) { inboxResource: Resource<DirectInbox?>? ->
|
||||
if (inboxResource == null) {
|
||||
return Collections.emptyList();
|
||||
return@map emptyList()
|
||||
}
|
||||
final DirectInbox inbox = inboxResource.data;
|
||||
if (inbox == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return ImmutableList.sortedCopyOf(THREAD_COMPARATOR, inbox.getThreads());
|
||||
}));
|
||||
|
||||
fetchInbox();
|
||||
val inbox = inboxResource.data
|
||||
val threads = inbox?.threads ?: emptyList()
|
||||
ImmutableList.sortedCopyOf(THREAD_COMPARATOR, threads)
|
||||
})
|
||||
fetchInbox()
|
||||
if (!pending) {
|
||||
fetchUnseenCount();
|
||||
fetchUnseenCount()
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<DirectInbox>> getInbox() {
|
||||
return distinctUntilChanged(inbox);
|
||||
}
|
||||
|
||||
public LiveData<List<DirectThread>> getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public LiveData<Resource<Integer>> getUnseenCount() {
|
||||
return distinctUntilChanged(unseenCount);
|
||||
}
|
||||
|
||||
public LiveData<Integer> getPendingRequestsTotal() {
|
||||
return distinctUntilChanged(pendingRequestsTotal);
|
||||
}
|
||||
|
||||
public User getViewer() {
|
||||
return viewer;
|
||||
}
|
||||
|
||||
public void fetchInbox() {
|
||||
final Resource<DirectInbox> inboxResource = inbox.getValue();
|
||||
if ((inboxResource != null && inboxResource.status == Resource.Status.LOADING) || !hasOlder) return;
|
||||
stopCurrentInboxRequest();
|
||||
inbox.postValue(Resource.loading(getCurrentDirectInbox()));
|
||||
inboxRequest = pending ? service.fetchPendingInbox(cursor, seqId) : service.fetchInbox(cursor, seqId);
|
||||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) {
|
||||
parseInboxResponse(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Failed fetching dm inbox", t);
|
||||
inbox.postValue(Resource.error(t.getMessage(), getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchUnseenCount() {
|
||||
final Resource<Integer> unseenCountResource = unseenCount.getValue();
|
||||
if ((unseenCountResource != null && unseenCountResource.status == Resource.Status.LOADING)) return;
|
||||
stopCurrentUnseenCountRequest();
|
||||
unseenCount.postValue(Resource.loading(getCurrentUnseenCount()));
|
||||
unseenCountRequest = service.fetchUnseenCount();
|
||||
unseenCountRequest.enqueue(new Callback<DirectBadgeCount>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectBadgeCount> call, @NonNull final Response<DirectBadgeCount> response) {
|
||||
final DirectBadgeCount directBadgeCount = response.body();
|
||||
if (directBadgeCount == null) {
|
||||
Log.e(TAG, "onResponse: directBadgeCount Response is null");
|
||||
unseenCount.postValue(Resource.error(R.string.dms_inbox_error_null_count, getCurrentUnseenCount()));
|
||||
return;
|
||||
}
|
||||
unseenCount.postValue(Resource.success(directBadgeCount.getBadgeCount()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectBadgeCount> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Failed fetching unseen count", t);
|
||||
unseenCount.postValue(Resource.error(t.getMessage(), getCurrentUnseenCount()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
cursor = null;
|
||||
seqId = 0;
|
||||
hasOlder = true;
|
||||
fetchInbox();
|
||||
if (!pending) {
|
||||
fetchUnseenCount();
|
||||
}
|
||||
}
|
||||
|
||||
private DirectInbox getCurrentDirectInbox() {
|
||||
final Resource<DirectInbox> inboxResource = inbox.getValue();
|
||||
return inboxResource != null ? inboxResource.data : null;
|
||||
}
|
||||
|
||||
private void parseInboxResponse(final DirectInboxResponse response) {
|
||||
if (response == null) {
|
||||
Log.e(TAG, "parseInboxResponse: Response is null");
|
||||
inbox.postValue(Resource.error(R.string.generic_null_response, getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(response.getStatus(), "ok")) {
|
||||
Log.e(TAG, "DM inbox fetch response: status not ok");
|
||||
inbox.postValue(Resource.error(R.string.generic_not_ok_response, getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
seqId = response.getSeqId();
|
||||
if (viewer == null) {
|
||||
viewer = response.getViewer();
|
||||
}
|
||||
final DirectInbox inbox = response.getInbox();
|
||||
if (inbox == null) return;
|
||||
if (!TextUtils.isEmpty(cursor)) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox != null) {
|
||||
List<DirectThread> threads = currentDirectInbox.getThreads();
|
||||
threads = threads == null ? new LinkedList<>() : new LinkedList<>(threads);
|
||||
threads.addAll(inbox.getThreads() == null ? Collections.emptyList() : inbox.getThreads());
|
||||
inbox.setThreads(threads);
|
||||
}
|
||||
}
|
||||
this.inbox.postValue(Resource.success(inbox));
|
||||
cursor = inbox.getOldestCursor();
|
||||
hasOlder = inbox.getHasOlder();
|
||||
pendingRequestsTotal.postValue(response.getPendingRequestsTotal());
|
||||
}
|
||||
|
||||
public void setThread(@NonNull final String threadId,
|
||||
@NonNull final DirectThread thread) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
setThread(inbox, index, thread);
|
||||
}
|
||||
|
||||
private void setThread(@NonNull final DirectInbox inbox,
|
||||
final int index,
|
||||
@NonNull final DirectThread thread) {
|
||||
if (index < 0) return;
|
||||
synchronized (this.inbox) {
|
||||
final List<DirectThread> threadsCopy = new LinkedList<>(inbox.getThreads());
|
||||
threadsCopy.set(index, thread);
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) inbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.postValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addItemsToThread(@NonNull final String threadId,
|
||||
final int insertIndex,
|
||||
@NonNull final Collection<DirectItem> items) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
if (index < 0) return;
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
final DirectThread thread = threads.get(index);
|
||||
List<DirectItem> list = thread.getItems();
|
||||
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
||||
if (insertIndex >= 0) {
|
||||
list.addAll(insertIndex, items);
|
||||
} else {
|
||||
list.addAll(items);
|
||||
}
|
||||
try {
|
||||
final DirectThread threadClone = (DirectThread) thread.clone();
|
||||
threadClone.setItems(list);
|
||||
setThread(inbox, index, threadClone);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "addItemsToThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setItemsToThread(@NonNull final String threadId,
|
||||
@NonNull final List<DirectItem> updatedItems) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
if (index < 0) return;
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
final DirectThread thread = threads.get(index);
|
||||
try {
|
||||
final DirectThread threadClone = (DirectThread) thread.clone();
|
||||
threadClone.setItems(updatedItems);
|
||||
setThread(inbox, index, threadClone);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setItemsToThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getThreadIndex(@NonNull final String threadId,
|
||||
@NonNull final DirectInbox inbox) {
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
if (threads == null || threads.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
return Iterables.indexOf(threads, t -> {
|
||||
if (t == null) return false;
|
||||
return t.getThreadId().equals(threadId);
|
||||
});
|
||||
}
|
||||
|
||||
private Integer getCurrentUnseenCount() {
|
||||
final Resource<Integer> unseenCountResource = unseenCount.getValue();
|
||||
return unseenCountResource != null ? unseenCountResource.data : null;
|
||||
}
|
||||
|
||||
private void stopCurrentInboxRequest() {
|
||||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return;
|
||||
inboxRequest.cancel();
|
||||
inboxRequest = null;
|
||||
}
|
||||
|
||||
private void stopCurrentUnseenCountRequest() {
|
||||
if (unseenCountRequest == null || unseenCountRequest.isCanceled() || unseenCountRequest.isExecuted()) return;
|
||||
unseenCountRequest.cancel();
|
||||
unseenCountRequest = null;
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
stopCurrentInboxRequest();
|
||||
stopCurrentUnseenCountRequest();
|
||||
}
|
||||
|
||||
public void addThread(@NonNull final DirectThread thread, final int insertIndex) {
|
||||
if (insertIndex < 0) return;
|
||||
synchronized (this.inbox) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox == null) return;
|
||||
final List<DirectThread> threadsCopy = new LinkedList<>(currentDirectInbox.getThreads());
|
||||
threadsCopy.add(insertIndex, thread);
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.setValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeThread(@NonNull final String threadId) {
|
||||
synchronized (this.inbox) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox == null) return;
|
||||
final List<DirectThread> threadsCopy = currentDirectInbox.getThreads()
|
||||
.stream()
|
||||
.filter(t -> !t.getThreadId().equals(threadId))
|
||||
.collect(Collectors.toList());
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.postValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPendingRequestsTotal(final int total) {
|
||||
pendingRequestsTotal.postValue(total);
|
||||
}
|
||||
|
||||
public boolean containsThread(final String threadId) {
|
||||
if (threadId == null) return false;
|
||||
synchronized (this.inbox) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox == null) return false;
|
||||
final List<DirectThread> threads = currentDirectInbox.getThreads();
|
||||
if (threads == null) return false;
|
||||
return threads.stream().anyMatch(thread -> Objects.equals(thread.getThreadId(), threadId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
1675
app/src/main/java/awais/instagrabber/managers/ThreadManager.kt
Normal file
1675
app/src/main/java/awais/instagrabber/managers/ThreadManager.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -59,7 +59,7 @@ public class DMSyncService extends LifecycleService {
|
||||
super.onCreate();
|
||||
startForeground(Constants.DM_CHECK_NOTIFICATION_ID, buildForegroundNotification());
|
||||
Log.d(TAG, "onCreate: Service created");
|
||||
final DirectMessagesManager directMessagesManager = DirectMessagesManager.getInstance();
|
||||
final DirectMessagesManager directMessagesManager = DirectMessagesManager.INSTANCE;
|
||||
inboxManager = directMessagesManager.getInboxManager();
|
||||
dmLastNotifiedRepository = DMLastNotifiedRepository.getInstance(DMLastNotifiedDataSource.getInstance(getApplicationContext()));
|
||||
}
|
||||
|
@ -1,193 +0,0 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.models.UploadPhotoOptions;
|
||||
import awais.instagrabber.models.UploadVideoOptions;
|
||||
import awais.instagrabber.webservices.interceptors.AddCookiesInterceptor;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
|
||||
public final class MediaUploader {
|
||||
private static final String TAG = MediaUploader.class.getSimpleName();
|
||||
private static final String HOST = "https://i.instagram.com";
|
||||
private static final AppExecutors appExecutors = AppExecutors.INSTANCE;
|
||||
|
||||
public static void uploadPhoto(@NonNull final Uri uri,
|
||||
@NonNull final ContentResolver contentResolver,
|
||||
@NonNull final OnMediaUploadCompleteListener listener) {
|
||||
BitmapUtils.loadBitmap(contentResolver, uri, 1000, false, new BitmapUtils.ThumbnailLoadCallback() {
|
||||
@Override
|
||||
public void onLoad(@Nullable final Bitmap bitmap, final int width, final int height) {
|
||||
if (bitmap == null) {
|
||||
listener.onFailure(new RuntimeException("Bitmap result was null"));
|
||||
return;
|
||||
}
|
||||
uploadPhoto(bitmap, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Throwable t) {
|
||||
listener.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void uploadPhoto(@NonNull final Bitmap bitmap,
|
||||
@NonNull final OnMediaUploadCompleteListener listener) {
|
||||
appExecutors.getTasksThread().submit(() -> {
|
||||
final File file;
|
||||
final long byteLength;
|
||||
try {
|
||||
file = BitmapUtils.convertToJpegAndSaveToFile(bitmap, null);
|
||||
byteLength = file.length();
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
return;
|
||||
}
|
||||
final UploadPhotoOptions options = MediaUploadHelper.createUploadPhotoOptions(byteLength);
|
||||
final Map<String, String> headers = MediaUploadHelper.getUploadPhotoHeaders(options);
|
||||
final String url = HOST + "/rupload_igphoto/" + options.getName() + "/";
|
||||
appExecutors.getNetworkIO().execute(() -> {
|
||||
try (FileInputStream input = new FileInputStream(file)) {
|
||||
upload(input, url, headers, listener);
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
} finally {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void uploadVideo(final Uri uri,
|
||||
final ContentResolver contentResolver,
|
||||
final UploadVideoOptions options,
|
||||
final OnMediaUploadCompleteListener listener) {
|
||||
appExecutors.getTasksThread().submit(() -> {
|
||||
final Map<String, String> headers = MediaUploadHelper.getUploadVideoHeaders(options);
|
||||
final String url = HOST + "/rupload_igvideo/" + options.getName() + "/";
|
||||
appExecutors.getNetworkIO().execute(() -> {
|
||||
try (InputStream input = contentResolver.openInputStream(uri)) {
|
||||
if (input == null) {
|
||||
listener.onFailure(new RuntimeException("InputStream was null"));
|
||||
return;
|
||||
}
|
||||
upload(input, url, headers, listener);
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void upload(@NonNull final InputStream input,
|
||||
@NonNull final String url,
|
||||
@NonNull final Map<String, String> headers,
|
||||
@NonNull final OnMediaUploadCompleteListener listener) {
|
||||
try {
|
||||
final OkHttpClient client = new OkHttpClient.Builder()
|
||||
// .addInterceptor(new LoggingInterceptor())
|
||||
.addInterceptor(new AddCookiesInterceptor())
|
||||
.followRedirects(false)
|
||||
.followSslRedirects(false)
|
||||
.build();
|
||||
final Request request = new Request.Builder()
|
||||
.headers(Headers.of(headers))
|
||||
.url(url)
|
||||
.post(create(MediaType.parse("application/octet-stream"), input))
|
||||
.build();
|
||||
final Call call = client.newCall(request);
|
||||
final Response response = call.execute();
|
||||
final ResponseBody body = response.body();
|
||||
if (!response.isSuccessful()) {
|
||||
listener.onFailure(new IOException("Unexpected code " + response + (body != null ? ": " + body.string() : "")));
|
||||
return;
|
||||
}
|
||||
listener.onUploadComplete(new MediaUploadResponse(response.code(), body != null ? new JSONObject(body.string()) : null));
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnMediaUploadCompleteListener {
|
||||
void onUploadComplete(MediaUploadResponse response);
|
||||
|
||||
void onFailure(Throwable t);
|
||||
}
|
||||
|
||||
private static RequestBody create(final MediaType mediaType, final InputStream inputStream) {
|
||||
return new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
try {
|
||||
return inputStream.available();
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
||||
try (Source source = Okio.source(inputStream)) {
|
||||
sink.writeAll(source);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class MediaUploadResponse {
|
||||
private final int responseCode;
|
||||
private final JSONObject response;
|
||||
|
||||
public MediaUploadResponse(int responseCode, JSONObject response) {
|
||||
this.responseCode = responseCode;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
public JSONObject getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MediaUploadResponse{" +
|
||||
"responseCode=" + responseCode +
|
||||
", response=" + response +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
155
app/src/main/java/awais/instagrabber/utils/MediaUploader.kt
Normal file
155
app/src/main/java/awais/instagrabber/utils/MediaUploader.kt
Normal file
@ -0,0 +1,155 @@
|
||||
package awais.instagrabber.utils
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import awais.instagrabber.models.UploadVideoOptions
|
||||
import awais.instagrabber.utils.BitmapUtils.ThumbnailLoadCallback
|
||||
import awais.instagrabber.webservices.interceptors.AddCookiesInterceptor
|
||||
import okhttp3.*
|
||||
import okio.BufferedSink
|
||||
import okio.source
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
object MediaUploader {
|
||||
private const val HOST = "https://i.instagram.com"
|
||||
private val appExecutors = AppExecutors
|
||||
|
||||
fun uploadPhoto(
|
||||
uri: Uri,
|
||||
contentResolver: ContentResolver,
|
||||
listener: OnMediaUploadCompleteListener,
|
||||
) {
|
||||
BitmapUtils.loadBitmap(contentResolver, uri, 1000f, false, object : ThumbnailLoadCallback {
|
||||
override fun onLoad(bitmap: Bitmap?, width: Int, height: Int) {
|
||||
if (bitmap == null) {
|
||||
listener.onFailure(RuntimeException("Bitmap result was null"))
|
||||
return
|
||||
}
|
||||
uploadPhoto(bitmap, listener)
|
||||
}
|
||||
|
||||
override fun onFailure(t: Throwable) {
|
||||
listener.onFailure(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadPhoto(
|
||||
bitmap: Bitmap,
|
||||
listener: OnMediaUploadCompleteListener,
|
||||
) {
|
||||
appExecutors.tasksThread.submit {
|
||||
val file: File
|
||||
val byteLength: Long
|
||||
try {
|
||||
file = BitmapUtils.convertToJpegAndSaveToFile(bitmap, null)
|
||||
byteLength = file.length()
|
||||
} catch (e: Exception) {
|
||||
listener.onFailure(e)
|
||||
return@submit
|
||||
}
|
||||
val options = createUploadPhotoOptions(byteLength)
|
||||
val headers = getUploadPhotoHeaders(options)
|
||||
val url = HOST + "/rupload_igphoto/" + options.name + "/"
|
||||
appExecutors.networkIO.execute {
|
||||
try {
|
||||
FileInputStream(file).use { input -> upload(input, url, headers, listener) }
|
||||
} catch (e: IOException) {
|
||||
listener.onFailure(e)
|
||||
} finally {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun uploadVideo(
|
||||
uri: Uri,
|
||||
contentResolver: ContentResolver,
|
||||
options: UploadVideoOptions,
|
||||
listener: OnMediaUploadCompleteListener,
|
||||
) {
|
||||
appExecutors.tasksThread.submit {
|
||||
val headers = getUploadVideoHeaders(options)
|
||||
val url = HOST + "/rupload_igvideo/" + options.name + "/"
|
||||
appExecutors.networkIO.execute {
|
||||
try {
|
||||
contentResolver.openInputStream(uri).use { input ->
|
||||
if (input == null) {
|
||||
listener.onFailure(RuntimeException("InputStream was null"))
|
||||
return@execute
|
||||
}
|
||||
upload(input, url, headers, listener)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
listener.onFailure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun upload(
|
||||
input: InputStream,
|
||||
url: String,
|
||||
headers: Map<String, String>,
|
||||
listener: OnMediaUploadCompleteListener,
|
||||
) {
|
||||
try {
|
||||
val client = OkHttpClient.Builder()
|
||||
// .addInterceptor(new LoggingInterceptor())
|
||||
.addInterceptor(AddCookiesInterceptor())
|
||||
.followRedirects(false)
|
||||
.followSslRedirects(false)
|
||||
.build()
|
||||
val request = Request.Builder()
|
||||
.headers(Headers.of(headers))
|
||||
.url(url)
|
||||
.post(create(MediaType.parse("application/octet-stream"), input))
|
||||
.build()
|
||||
val call = client.newCall(request)
|
||||
val response = call.execute()
|
||||
val body = response.body()
|
||||
if (!response.isSuccessful) {
|
||||
listener.onFailure(IOException("Unexpected code " + response + if (body != null) ": " + body.string() else ""))
|
||||
return
|
||||
}
|
||||
listener.onUploadComplete(MediaUploadResponse(response.code(), if (body != null) JSONObject(body.string()) else null))
|
||||
} catch (e: Exception) {
|
||||
listener.onFailure(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun create(mediaType: MediaType?, inputStream: InputStream): RequestBody {
|
||||
return object : RequestBody() {
|
||||
override fun contentType(): MediaType? {
|
||||
return mediaType
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return try {
|
||||
inputStream.available().toLong()
|
||||
} catch (e: IOException) {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
inputStream.source().use { sink.writeAll(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnMediaUploadCompleteListener {
|
||||
fun onUploadComplete(response: MediaUploadResponse)
|
||||
fun onFailure(t: Throwable)
|
||||
}
|
||||
|
||||
data class MediaUploadResponse(val responseCode: Int, val response: JSONObject?)
|
||||
}
|
@ -18,7 +18,7 @@ public class DirectInboxViewModel extends ViewModel {
|
||||
private final InboxManager inboxManager;
|
||||
|
||||
public DirectInboxViewModel() {
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE;
|
||||
inboxManager = messagesManager.getInboxManager();
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ public class DirectPendingInboxViewModel extends ViewModel {
|
||||
private final InboxManager inboxManager;
|
||||
|
||||
public DirectPendingInboxViewModel() {
|
||||
inboxManager = DirectMessagesManager.getInstance().getPendingInboxManager();
|
||||
inboxManager = DirectMessagesManager.INSTANCE.getPendingInboxManager();
|
||||
inboxManager.fetchInbox();
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
|
||||
}
|
||||
final ContentResolver contentResolver = application.getContentResolver();
|
||||
resources = getApplication().getResources();
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE;
|
||||
threadManager = messagesManager.getThreadManager(threadId, pending, currentUser, contentResolver);
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
private final long viewerId;
|
||||
private final String threadId;
|
||||
private final User currentUser;
|
||||
private final ThreadManager threadManager;
|
||||
|
||||
private ThreadManager threadManager;
|
||||
private VoiceRecorder voiceRecorder;
|
||||
|
||||
public DirectThreadViewModel(@NonNull final Application application,
|
||||
@ -73,14 +73,15 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
}
|
||||
contentResolver = application.getContentResolver();
|
||||
recordingsDir = DirectoryUtils.getOutputMediaDirectory(application, "Recordings");
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE;
|
||||
threadManager = messagesManager.getThreadManager(threadId, pending, currentUser, contentResolver);
|
||||
threadManager.fetchPendingRequests();
|
||||
}
|
||||
|
||||
public void moveFromPending() {
|
||||
DirectMessagesManager.getInstance().moveThreadFromPending(threadId);
|
||||
threadManager.moveFromPending();
|
||||
final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE;
|
||||
messagesManager.moveThreadFromPending(threadId);
|
||||
threadManager = messagesManager.getThreadManager(threadId, false, currentUser, contentResolver);
|
||||
}
|
||||
|
||||
public void removeThread() {
|
||||
@ -268,7 +269,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
threadManager.forward(recipient, itemToForward);
|
||||
}
|
||||
|
||||
public void setReplyToItem(final DirectItem item) {
|
||||
public void setReplyToItem(@Nullable final DirectItem item) {
|
||||
// Log.d(TAG, "setReplyToItem: " + item);
|
||||
threadManager.setReplyToItem(item);
|
||||
}
|
||||
@ -327,7 +328,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
||||
final DirectThread thread = getThread().getValue();
|
||||
if (thread == null) return;
|
||||
if (thread.isTemp() && (thread.getItems() == null || thread.getItems().isEmpty())) {
|
||||
final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager();
|
||||
final InboxManager inboxManager = DirectMessagesManager.INSTANCE.getInboxManager();
|
||||
inboxManager.removeThread(threadId);
|
||||
}
|
||||
}
|
||||
|
@ -333,14 +333,14 @@ public class PostViewV2ViewModel extends ViewModel {
|
||||
|
||||
public void shareDm(@NonNull final RankedRecipient result) {
|
||||
if (messageManager == null) {
|
||||
messageManager = DirectMessagesManager.getInstance();
|
||||
messageManager = DirectMessagesManager.INSTANCE;
|
||||
}
|
||||
messageManager.sendMedia(result, media.getId());
|
||||
}
|
||||
|
||||
public void shareDm(@NonNull final Set<RankedRecipient> recipients) {
|
||||
if (messageManager == null) {
|
||||
messageManager = DirectMessagesManager.getInstance();
|
||||
messageManager = DirectMessagesManager.INSTANCE;
|
||||
}
|
||||
messageManager.sendMedia(recipients, media.getId());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user