From 741a997424a349c6d09633b9ed2107ad2c886c20 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Mon, 31 May 2021 22:16:18 +0900 Subject: [PATCH] More suspend funs --- .../fragments/StoryViewerFragment.java | 35 +-- .../managers/DirectMessagesManager.kt | 64 ++-- .../instagrabber/managers/InboxManager.kt | 50 +--- .../instagrabber/managers/ThreadManager.kt | 279 +++++++++--------- .../models/enums/DirectItemType.kt | 25 +- .../repositories/DirectMessagesRepository.kt | 6 +- .../instagrabber/utils/CoroutineUtils.kt | 19 ++ .../viewmodels/DirectInboxViewModel.kt | 1 + .../viewmodels/DirectThreadViewModel.kt | 21 +- .../webservices/DirectMessagesService.kt | 42 ++- 10 files changed, 250 insertions(+), 292 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/utils/CoroutineUtils.kt diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 8b8ffb40..413ccb49 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -88,9 +88,9 @@ import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.StoryStickerResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThread; -import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -231,32 +231,21 @@ public class StoryViewerFragment extends Fragment { return; } final DirectThread thread = response.body(); - final Call request = directMessagesService.broadcastStoryReply( + directMessagesService.broadcastStoryReply( ThreadIdOrUserIds.of(thread.getThreadId()), input.getText().toString(), currentStory.getStoryMediaId(), - String.valueOf(currentStory.getUserId()) - ); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, - @NonNull final Response response) { - if (!response.isSuccessful()) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - return; - } - Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); - } + String.valueOf(currentStory.getUserId()), + CoroutineUtilsKt.getContinuation((directThreadBroadcastResponse, throwable) -> { + if (throwable != null) { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + Log.e(TAG, "onFailure: ", throwable); + return; + } + Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); + }) - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - try { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - Log.e(TAG, "onFailure: ", t); - } catch (Throwable ignored) { - } - } - }); + ); } @Override diff --git a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt index e4249ab0..08452a97 100644 --- a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt @@ -12,7 +12,6 @@ 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 @@ -21,6 +20,8 @@ import awais.instagrabber.utils.getUserIdFromCookie import awais.instagrabber.webservices.DirectMessagesService import awais.instagrabber.webservices.DirectMessagesService.Companion.getInstance import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -137,7 +138,7 @@ object DirectMessagesManager { // create thread and forward createThread(recipient.user.pk) { (threadId) -> val threadIdTemp = threadId ?: return@createThread - sendMedia(threadIdTemp, mediaId) { + sendMedia(threadIdTemp, mediaId, scope) { if (refreshInbox) { inboxManager.refresh(scope) } @@ -149,7 +150,7 @@ object DirectMessagesManager { // just forward val thread = recipient.thread val threadId = thread.threadId ?: return - sendMedia(threadId, mediaId) { + sendMedia(threadId, mediaId, scope) { if (refreshInbox) { inboxManager.refresh(scope) } @@ -160,55 +161,26 @@ object DirectMessagesManager { private fun sendMedia( threadId: String, mediaId: String, + scope: CoroutineScope, callback: (() -> Unit)?, ): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val request = service.broadcastMediaShare( - UUID.randomUUID().toString(), - of(threadId), - mediaId - ) - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - data.postValue(success(Any())) - callback?.invoke() - return - } - val errorBody = response.errorBody() - if (errorBody != null) { - try { - 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)) - } - 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)) + scope.launch(Dispatchers.IO) { + try { + service.broadcastMediaShare( + UUID.randomUUID().toString(), + of(threadId), + mediaId + ) + data.postValue(success(Any())) + callback?.invoke() + } catch (e: Exception) { + Log.e(TAG, "sendMedia: ", e) + data.postValue(error(e.message, null)) callback?.invoke() } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - callback?.invoke() - } - }) + } return data } diff --git a/app/src/main/java/awais/instagrabber/managers/InboxManager.kt b/app/src/main/java/awais/instagrabber/managers/InboxManager.kt index 40a76d61..2ea2448b 100644 --- a/app/src/main/java/awais/instagrabber/managers/InboxManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/InboxManager.kt @@ -21,8 +21,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response import java.util.* import java.util.concurrent.TimeUnit @@ -66,49 +64,23 @@ class InboxManager private constructor(private val pending: Boolean) { inbox.postValue(error(e.message, currentDirectInbox)) hasOlder = false } - // inboxRequest?.enqueue(object : Callback { - // override fun onResponse(call: Call, response: Response) { - // 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 - // } - // - // } - // - // override fun onFailure(call: Call, t: Throwable) { - // Log.e(TAG, "Failed fetching dm inbox", t) - // inbox.postValue(error(t.message, currentDirectInbox)) - // hasOlder = false - // } - // }) } } - fun fetchUnseenCount() { + fun fetchUnseenCount(scope: CoroutineScope) { 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 { - override fun onResponse(call: Call, response: Response) { - 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 - } + scope.launch(Dispatchers.IO) { + try { + val directBadgeCount = service.fetchUnseenCount() unseenCount.postValue(success(directBadgeCount.badgeCount)) + } catch (e: Exception) { + Log.e(TAG, "Failed fetching unseen count", e) + unseenCount.postValue(error(e.message, currentUnseenCount)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "Failed fetching unseen count", t) - unseenCount.postValue(error(t.message, currentUnseenCount)) - } - }) + } } fun refresh(scope: CoroutineScope) { @@ -117,7 +89,7 @@ class InboxManager private constructor(private val pending: Boolean) { hasOlder = true fetchInbox(scope) if (!pending) { - fetchUnseenCount() + fetchUnseenCount(scope) } } @@ -350,9 +322,5 @@ class InboxManager private constructor(private val pending: Boolean) { val threads = inbox?.threads ?: emptyList() ImmutableList.sortedCopyOf(THREAD_COMPARATOR, threads) }) - // fetchInbox() - if (!pending) { - fetchUnseenCount() - } } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt index af470afe..04499b7e 100644 --- a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt @@ -10,11 +10,11 @@ import androidx.lifecycle.Transformations.distinctUntilChanged import androidx.lifecycle.Transformations.map import awais.instagrabber.R import awais.instagrabber.customviews.emoji.Emoji -import awais.instagrabber.models.enums.DirectItemType.Companion.getName 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.models.enums.DirectItemType import awais.instagrabber.repositories.requests.UploadFinishOptions import awais.instagrabber.repositories.requests.VideoOptions import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds @@ -400,7 +400,7 @@ class ThreadManager private constructor( return temp } - fun sendText(text: String): LiveData> { + fun sendText(text: String, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val userId = getCurrentUserId(data) ?: return data val clientContext = UUID.randomUUID().toString() @@ -412,29 +412,36 @@ class ThreadManager private constructor( data.postValue(loading(directItem)) val repliedToItemId = replyToItemValue?.itemId val repliedToClientContext = replyToItemValue?.clientContext - val request = service.broadcastText( - clientContext, - threadIdOrUserIds, - text, - repliedToItemId, - repliedToClientContext - ) - enqueueRequest(request, data, directItem) + scope.launch(Dispatchers.IO) { + try { + val response = service.broadcastText( + clientContext, + threadIdOrUserIds, + text, + repliedToItemId, + repliedToClientContext + ) + parseResponse(response, data, directItem) + } catch (e: Exception) { + data.postValue(error(e.message, directItem)) + Log.e(TAG, "sendText: ", e) + } + } return data } - fun sendUri(entry: MediaController.MediaEntry): LiveData> { + fun sendUri(entry: MediaController.MediaEntry, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val uri = Uri.fromFile(File(entry.path)) if (!entry.isVideo) { - sendPhoto(data, uri, entry.width, entry.height) + sendPhoto(data, uri, entry.width, entry.height, scope) return data } - sendVideo(data, uri, entry.size, entry.duration, entry.width, entry.height) + sendVideo(data, uri, entry.size, entry.duration, entry.width, entry.height, scope) return data } - fun sendUri(uri: Uri): LiveData> { + fun sendUri(uri: Uri, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val mimeType = Utils.getMimeType(uri, contentResolver) if (isEmpty(mimeType)) { @@ -443,16 +450,16 @@ class ThreadManager private constructor( } val isPhoto = mimeType != null && mimeType.startsWith("image") if (isPhoto) { - sendPhoto(data, uri) + sendPhoto(data, uri, scope) return data } if (mimeType != null && mimeType.startsWith("video")) { - sendVideo(data, uri) + sendVideo(data, uri, scope) } return data } - fun sendAnimatedMedia(giphyGif: GiphyGif): LiveData> { + fun sendAnimatedMedia(giphyGif: GiphyGif, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val userId = getCurrentUserId(data) ?: return data val clientContext = UUID.randomUUID().toString() @@ -460,12 +467,19 @@ class ThreadManager private constructor( directItem.isPending = true addItems(0, listOf(directItem)) data.postValue(loading(directItem)) - val request = service.broadcastAnimatedMedia( - clientContext, - threadIdOrUserIds, - giphyGif - ) - enqueueRequest(request, data, directItem) + scope.launch(Dispatchers.IO) { + try { + val request = service.broadcastAnimatedMedia( + clientContext, + threadIdOrUserIds, + giphyGif + ) + parseResponse(request, data, directItem) + } catch (e: Exception) { + data.postValue(error(e.message, directItem)) + Log.e(TAG, "sendAnimatedMedia: ", e) + } + } return data } @@ -476,6 +490,7 @@ class ThreadManager private constructor( samplingFreq: Int, duration: Long, byteLength: Long, + scope: CoroutineScope, ) { if (duration > 60000) { // instagram does not allow uploading audio longer than 60 secs for Direct messages @@ -502,14 +517,21 @@ class ThreadManager private constructor( uploadFinishRequest.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { - val request = service.broadcastVoice( - clientContext, - threadIdOrUserIds, - uploadDmVoiceOptions.uploadId, - waveform, - samplingFreq - ) - enqueueRequest(request, data, directItem) + scope.launch(Dispatchers.IO) { + try { + val request = service.broadcastVoice( + clientContext, + threadIdOrUserIds, + uploadDmVoiceOptions.uploadId, + waveform, + samplingFreq + ) + parseResponse(request, data, directItem) + } catch (e: Exception) { + data.postValue(error(e.message, directItem)) + Log.e(TAG, "sendVoice: ", e) + } + } return } if (response.errorBody() != null) { @@ -537,6 +559,7 @@ class ThreadManager private constructor( fun sendReaction( item: DirectItem, emoji: Emoji, + scope: CoroutineScope, ): LiveData> { val data = MutableLiveData>() val userId = getCurrentUserId(data) @@ -557,18 +580,24 @@ class ThreadManager private constructor( data.postValue(error("itemId is null", null)) return data } - val request = service.broadcastReaction( - clientContext, - threadIdOrUserIds, - itemId, - emojiUnicode, - false - ) - handleBroadcastReactionRequest(data, item, request) + scope.launch(Dispatchers.IO) { + try { + service.broadcastReaction( + clientContext, + threadIdOrUserIds, + itemId, + emojiUnicode, + false + ) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + Log.e(TAG, "sendReaction: ", e) + } + } return data } - fun sendDeleteReaction(itemId: String): LiveData> { + fun sendDeleteReaction(itemId: String, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val item = getItem(itemId) if (item == null) { @@ -588,8 +617,14 @@ class ThreadManager private constructor( data.postValue(error("itemId is null", null)) return data } - val request = service.broadcastReaction(clientContext, threadIdOrUserIds, itemId1, null, true) - handleBroadcastReactionRequest(data, item, request) + scope.launch(Dispatchers.IO) { + try { + service.broadcastReaction(clientContext, threadIdOrUserIds, itemId1, null, true) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + Log.e(TAG, "sendDeleteReaction: ", e) + } + } return data } @@ -660,7 +695,7 @@ class ThreadManager private constructor( data.postValue(error("item type is null", null)) return data } - val itemTypeName = getName(itemType) + val itemTypeName = DirectItemType.getName(itemType) if (itemTypeName == null) { Log.e(TAG, "forward: itemTypeName was null!") data.postValue(error("itemTypeName is null", null)) @@ -798,6 +833,7 @@ class ThreadManager private constructor( private fun sendPhoto( data: MutableLiveData>, uri: Uri, + scope: CoroutineScope, ) { try { val dimensions = BitmapUtils.decodeDimensions(contentResolver, uri) @@ -805,7 +841,7 @@ class ThreadManager private constructor( data.postValue(error("Decoding dimensions failed", null)) return } - sendPhoto(data, uri, dimensions.first, dimensions.second) + sendPhoto(data, uri, dimensions.first, dimensions.second, scope) } catch (e: IOException) { data.postValue(error(e.message, null)) Log.e(TAG, "sendPhoto: ", e) @@ -817,6 +853,7 @@ class ThreadManager private constructor( uri: Uri, width: Int, height: Int, + scope: CoroutineScope, ) { val userId = getCurrentUserId(data) ?: return val clientContext = UUID.randomUUID().toString() @@ -829,8 +866,15 @@ class ThreadManager private constructor( if (handleInvalidResponse(data, response)) return val response1 = response.response ?: return val uploadId = response1.optString("upload_id") - val request = service.broadcastPhoto(clientContext, threadIdOrUserIds, uploadId) - enqueueRequest(request, data, directItem) + scope.launch(Dispatchers.IO) { + try { + val response2 = service.broadcastPhoto(clientContext, threadIdOrUserIds, uploadId) + parseResponse(response2, data, directItem) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + Log.e(TAG, "sendPhoto: ", e) + } + } } override fun onFailure(t: Throwable) { @@ -843,6 +887,7 @@ class ThreadManager private constructor( private fun sendVideo( data: MutableLiveData>, uri: Uri, + scope: CoroutineScope, ) { MediaUtils.getVideoInfo(contentResolver, uri, object : OnInfoLoadListener { override fun onLoad(info: VideoInfo?) { @@ -850,7 +895,7 @@ class ThreadManager private constructor( data.postValue(error("Could not get the video info", null)) return } - sendVideo(data, uri, info.size, info.duration, info.width, info.height) + sendVideo(data, uri, info.size, info.duration, info.width, info.height, scope) } override fun onFailure(t: Throwable) { @@ -866,6 +911,7 @@ class ThreadManager private constructor( duration: Long, width: Int, height: Int, + scope: CoroutineScope, ) { if (duration > 60000) { // instagram does not allow uploading videos longer than 60 secs for Direct messages @@ -892,14 +938,21 @@ class ThreadManager private constructor( uploadFinishRequest.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { - val request = service.broadcastVideo( - clientContext, - threadIdOrUserIds, - uploadDmVideoOptions.uploadId, - "", - true - ) - enqueueRequest(request, data, directItem) + scope.launch(Dispatchers.IO) { + try { + val response1 = service.broadcastVideo( + clientContext, + threadIdOrUserIds, + uploadDmVideoOptions.uploadId, + "", + true + ) + parseResponse(response1, data, directItem) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + Log.e(TAG, "sendVideo: ", e) + } + } return } if (response.errorBody() != null) { @@ -924,64 +977,36 @@ class ThreadManager private constructor( }) } - private fun enqueueRequest( - request: Call, + private fun parseResponse( + response: DirectThreadBroadcastResponse, data: MutableLiveData>, directItem: DirectItem, ) { - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - val broadcastResponse = response.body() - if (broadcastResponse == null) { - data.postValue(error(R.string.generic_null_response, directItem)) - Log.e(TAG, "enqueueRequest: onResponse: response body is null") - return - } - val payloadClientContext: String? - val timestamp: Long - val itemId: String? - val payload = broadcastResponse.payload - if (payload == null) { - val messageMetadata = broadcastResponse.messageMetadata - if (messageMetadata == null || messageMetadata.isEmpty()) { - data.postValue(success(directItem)) - return - } - val (clientContext, itemId1, timestamp1) = messageMetadata[0] - payloadClientContext = clientContext - itemId = itemId1 - timestamp = timestamp1 - } else { - payloadClientContext = payload.clientContext - timestamp = payload.timestamp - itemId = payload.itemId - } - if (payloadClientContext == null) { - data.postValue(error("clientContext in response was null", null)) - return - } - updateItemSent(payloadClientContext, timestamp, itemId) - data.postValue(success(directItem)) - return - } - if (response.errorBody() != null) { - handleErrorBody(call, response, data) - } - data.postValue(error(R.string.generic_failed_request, directItem)) + val payloadClientContext: String? + val timestamp: Long + val itemId: String? + val payload = response.payload + if (payload == null) { + val messageMetadata = response.messageMetadata + if (messageMetadata == null || messageMetadata.isEmpty()) { + data.postValue(success(directItem)) + return } - - override fun onFailure( - call: Call, - t: Throwable, - ) { - data.postValue(error(t.message, directItem)) - Log.e(TAG, "enqueueRequest: onFailure: ", t) - } - }) + val (clientContext, itemId1, timestamp1) = messageMetadata[0] + payloadClientContext = clientContext + itemId = itemId1 + timestamp = timestamp1 + } else { + payloadClientContext = payload.clientContext + timestamp = payload.timestamp + itemId = payload.itemId + } + if (payloadClientContext == null) { + data.postValue(error("clientContext in response was null", null)) + return + } + updateItemSent(payloadClientContext, timestamp, itemId) + data.postValue(success(directItem)) } private fun updateItemSent( @@ -1054,38 +1079,6 @@ class ThreadManager private constructor( .firstOrNull() } - private fun handleBroadcastReactionRequest( - data: MutableLiveData>, - item: DirectItem, - request: Call, - ) { - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (!response.isSuccessful) { - if (response.errorBody() != null) { - handleErrorBody(call, response, data) - return - } - data.postValue(error(R.string.generic_failed_request, item)) - return - } - val body = response.body() - if (body == null) { - data.postValue(error(R.string.generic_null_response, item)) - } - // otherwise nothing to do? maybe update the timestamp in the emoji? - } - - override fun onFailure(call: Call, t: Throwable) { - data.postValue(error(t.message, item)) - Log.e(TAG, "enqueueRequest: onFailure: ", t) - } - }) - } - private fun stopCurrentRequest() { chatsRequest?.let { if (it.isExecuted || it.isCanceled) return @@ -1583,7 +1576,7 @@ class ThreadManager private constructor( }) } - fun markAsSeen(directItem: DirectItem): LiveData> { + fun markAsSeen(directItem: DirectItem, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val request = service.markAsSeen(threadId, directItem) @@ -1606,7 +1599,7 @@ class ThreadManager private constructor( data.postValue(error(R.string.generic_null_response, null)) return } - inboxManager.fetchUnseenCount() + inboxManager.fetchUnseenCount(scope) val payload = seenResponse.payload ?: return val timestamp = payload.timestamp val thread = thread.value ?: return diff --git a/app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt b/app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt index 3215085a..1ac2171b 100755 --- a/app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt +++ b/app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt @@ -1,46 +1,62 @@ package awais.instagrabber.models.enums import com.google.gson.annotations.SerializedName - import java.io.Serializable -import java.util.* enum class DirectItemType(val id: Int) : Serializable { UNKNOWN(0), + @SerializedName("text") TEXT(1), + @SerializedName("like") LIKE(2), + @SerializedName("link") LINK(3), + @SerializedName("media") MEDIA(4), + @SerializedName("raven_media") RAVEN_MEDIA(5), + @SerializedName("profile") PROFILE(6), + @SerializedName("video_call_event") VIDEO_CALL_EVENT(7), + @SerializedName("animated_media") ANIMATED_MEDIA(8), + @SerializedName("voice_media") VOICE_MEDIA(9), + @SerializedName("media_share") MEDIA_SHARE(10), + @SerializedName("reel_share") REEL_SHARE(11), + @SerializedName("action_log") ACTION_LOG(12), + @SerializedName("placeholder") PLACEHOLDER(13), + @SerializedName("story_share") STORY_SHARE(14), + @SerializedName("clip") CLIP(15), // media_share but reel + @SerializedName("felix_share") FELIX_SHARE(16), // media_share but igtv + @SerializedName("location") LOCATION(17), + @SerializedName("xma") XMA(18); // self avatar stickers @@ -52,7 +68,6 @@ enum class DirectItemType(val id: Int) : Serializable { return map[id] } - @JvmStatic fun getName(directItemType: DirectItemType): String? { when (directItemType) { TEXT -> return "text" @@ -72,12 +87,12 @@ enum class DirectItemType(val id: Int) : Serializable { CLIP -> return "clip" FELIX_SHARE -> return "felix_share" LOCATION -> return "location" + else -> return null } - return null } init { - for (type in DirectItemType.values()) { + for (type in values()) { map[type.id] = type } } diff --git a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt index 9ebfb2ea..b3a89804 100644 --- a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt +++ b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt @@ -18,14 +18,14 @@ interface DirectMessagesRepository { ): DirectThreadFeedResponse @GET("/api/v1/direct_v2/get_badge_count/?no_raven=1") - fun fetchUnseenCount(): Call + suspend fun fetchUnseenCount(): DirectBadgeCount @FormUrlEncoded @POST("/api/v1/direct_v2/threads/broadcast/{item}/") - fun broadcast( + suspend fun broadcast( @Path("item") item: String, @FieldMap signedForm: Map, - ): Call + ): DirectThreadBroadcastResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/add_user/") diff --git a/app/src/main/java/awais/instagrabber/utils/CoroutineUtils.kt b/app/src/main/java/awais/instagrabber/utils/CoroutineUtils.kt new file mode 100644 index 00000000..6c28ae1d --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/CoroutineUtils.kt @@ -0,0 +1,19 @@ +package awais.instagrabber.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import java.util.function.BiConsumer +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext + +@JvmOverloads +fun getContinuation(onFinished: BiConsumer, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation { + return object : Continuation { + override val context: CoroutineContext + get() = dispatcher + + override fun resumeWith(result: Result) { + onFinished.accept(result.getOrNull(), result.exceptionOrNull()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt index a4546c2c..5c748b5a 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt @@ -32,5 +32,6 @@ class DirectInboxViewModel : ViewModel() { init { inboxManager.fetchInbox(viewModelScope) + inboxManager.fetchUnseenCount(viewModelScope) } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt index facb2495..621481f9 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt @@ -74,15 +74,15 @@ class DirectThreadViewModel( } fun sendText(text: String): LiveData> { - return threadManager.sendText(text) + return threadManager.sendText(text, viewModelScope) } fun sendUri(entry: MediaController.MediaEntry): LiveData> { - return threadManager.sendUri(entry) + return threadManager.sendUri(entry, viewModelScope) } fun sendUri(uri: Uri): LiveData> { - return threadManager.sendUri(uri) + return threadManager.sendUri(uri, viewModelScope) } fun startRecording(): LiveData> { @@ -106,12 +106,15 @@ class DirectThreadViewModel( MediaUtils.getVoiceInfo(contentResolver, uri, object : OnInfoLoadListener { override fun onLoad(videoInfo: VideoInfo?) { if (videoInfo == null) return - threadManager.sendVoice(data, + threadManager.sendVoice( + data, uri, result.waveform, result.samplingFreq, videoInfo.duration, - videoInfo.size) + videoInfo.size, + viewModelScope, + ) } override fun onFailure(t: Throwable) { @@ -133,11 +136,11 @@ class DirectThreadViewModel( } fun sendReaction(item: DirectItem, emoji: Emoji): LiveData> { - return threadManager.sendReaction(item, emoji) + return threadManager.sendReaction(item, emoji, viewModelScope) } fun sendDeleteReaction(itemId: String): LiveData> { - return threadManager.sendDeleteReaction(itemId) + return threadManager.sendDeleteReaction(itemId, viewModelScope) } fun unsend(item: DirectItem): LiveData> { @@ -145,7 +148,7 @@ class DirectThreadViewModel( } fun sendAnimatedMedia(giphyGif: GiphyGif): LiveData> { - return threadManager.sendAnimatedMedia(giphyGif) + return threadManager.sendAnimatedMedia(giphyGif, viewModelScope) } fun getUser(userId: Long): User? { @@ -197,7 +200,7 @@ class DirectThreadViewModel( return successEventResObjectLiveData } } - return threadManager.markAsSeen(directItem) + return threadManager.markAsSeen(directItem, viewModelScope) } private val successEventResObjectLiveData: MutableLiveData> diff --git a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt index 9b0dde7f..2ebe7cf0 100644 --- a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt +++ b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt @@ -53,17 +53,15 @@ class DirectMessagesService private constructor( return repository.fetchThread(threadId, queryMap) } - fun fetchUnseenCount(): Call { - return repository.fetchUnseenCount() - } + suspend fun fetchUnseenCount(): DirectBadgeCount = repository.fetchUnseenCount() - fun broadcastText( + suspend fun broadcastText( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, text: String, repliedToItemId: String?, repliedToClientContext: String?, - ): Call { + ): DirectThreadBroadcastResponse { val urls = extractUrls(text) if (urls.isNotEmpty()) { return broadcastLink(clientContext, threadIdOrUserIds, text, urls, repliedToItemId, repliedToClientContext) @@ -76,14 +74,14 @@ class DirectMessagesService private constructor( return broadcast(broadcastOptions) } - private fun broadcastLink( + private suspend fun broadcastLink( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, linkText: String, urls: List, repliedToItemId: String?, repliedToClientContext: String?, - ): Call { + ): DirectThreadBroadcastResponse { val broadcastOptions = LinkBroadcastOptions(clientContext, threadIdOrUserIds, linkText, urls) if (!repliedToItemId.isNullOrBlank() && !repliedToClientContext.isNullOrBlank()) { broadcastOptions.repliedToItemId = repliedToItemId @@ -92,70 +90,70 @@ class DirectMessagesService private constructor( return broadcast(broadcastOptions) } - fun broadcastPhoto( + suspend fun broadcastPhoto( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, uploadId: String, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(PhotoBroadcastOptions(clientContext, threadIdOrUserIds, true, uploadId)) } - fun broadcastVideo( + suspend fun broadcastVideo( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, uploadId: String, videoResult: String, sampled: Boolean, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(VideoBroadcastOptions(clientContext, threadIdOrUserIds, videoResult, uploadId, sampled)) } - fun broadcastVoice( + suspend fun broadcastVoice( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, uploadId: String, waveform: List, samplingFreq: Int, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(VoiceBroadcastOptions(clientContext, threadIdOrUserIds, uploadId, waveform, samplingFreq)) } - fun broadcastStoryReply( + suspend fun broadcastStoryReply( threadIdOrUserIds: ThreadIdOrUserIds, text: String, mediaId: String, reelId: String, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(StoryReplyBroadcastOptions(UUID.randomUUID().toString(), threadIdOrUserIds, text, mediaId, reelId)) } - fun broadcastReaction( + suspend fun broadcastReaction( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, itemId: String, emoji: String?, delete: Boolean, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(ReactionBroadcastOptions(clientContext, threadIdOrUserIds, itemId, emoji, delete)) } - fun broadcastAnimatedMedia( + suspend fun broadcastAnimatedMedia( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, giphyGif: GiphyGif, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(AnimatedMediaBroadcastOptions(clientContext, threadIdOrUserIds, giphyGif)) } - fun broadcastMediaShare( + suspend fun broadcastMediaShare( clientContext: String, threadIdOrUserIds: ThreadIdOrUserIds, mediaId: String, - ): Call { + ): DirectThreadBroadcastResponse { return broadcast(MediaShareBroadcastOptions(clientContext, threadIdOrUserIds, mediaId)) } - private fun broadcast(broadcastOptions: BroadcastOptions): Call { + private suspend fun broadcast(broadcastOptions: BroadcastOptions): DirectThreadBroadcastResponse { require(!isEmpty(broadcastOptions.clientContext)) { "Broadcast requires a valid client context value" } val form = mutableMapOf() val threadId = broadcastOptions.threadId