diff --git a/app/build.gradle b/app/build.gradle index 0841a8e0..f9ca6511 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -182,8 +182,13 @@ dependencies { def core_version = "1.6.0-beta01" implementation "androidx.core:core:$core_version" + // Fragment implementation "androidx.fragment:fragment-ktx:1.3.4" + // Lifecycle + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" + // Room def room_version = "2.3.0" implementation "androidx.room:room-runtime:$room_version" diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.kt b/app/src/main/java/awais/instagrabber/activities/MainActivity.kt index c921b075..8119b381 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.kt +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.kt @@ -185,7 +185,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL private fun initDmUnreadCount() { if (!isLoggedIn) return val directInboxViewModel = ViewModelProvider(this).get(DirectInboxViewModel::class.java) - directInboxViewModel.unseenCount.observe(this, { unseenCountResource: Resource? -> + directInboxViewModel.unseenCount.observe(this, { unseenCountResource: Resource? -> if (unseenCountResource == null) return@observe val unseenCount = unseenCountResource.data setNavBarDMUnreadCountBadge(unseenCount ?: 0) diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 71e2a43c..3c419971 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -217,7 +217,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme // wasPaused = true; if (settingsHelper.getBoolean(PreferenceKeys.PLAY_IN_BACKGROUND)) return; final Media media = viewModel.getMedia(); - if (media == null || media.getMediaType() == null) return; + if (media.getMediaType() == null) return; switch (media.getMediaType()) { case MEDIA_TYPE_VIDEO: if (videoPlayerViewHelper != null) { @@ -250,7 +250,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme super.onDestroyView(); showSystemUI(); final Media media = viewModel.getMedia(); - if (media == null || media.getMediaType() == null) return; + if (media.getMediaType() == null) return; switch (media.getMediaType()) { case MEDIA_TYPE_VIDEO: if (videoPlayerViewHelper != null) { @@ -269,7 +269,6 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); final Media media = viewModel.getMedia(); - if (media == null) return; if (media.getMediaType() == MediaItemType.MEDIA_TYPE_SLIDER) { outState.putInt(ARG_SLIDER_POSITION, sliderPosition); } @@ -1440,9 +1439,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme actionBar.hide(); } final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView(); - if (appbarLayout != null) { - appbarLayout.setVisibility(View.GONE); - } + appbarLayout.setVisibility(View.GONE); final Toolbar toolbar = activity.getToolbar(); if (toolbar != null) { toolbar.setVisibility(View.GONE); @@ -1467,9 +1464,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme actionBar.show(); } final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView(); - if (appbarLayout != null) { - appbarLayout.setVisibility(View.VISIBLE); - } + appbarLayout.setVisibility(View.VISIBLE); final Toolbar toolbar = activity.getToolbar(); if (toolbar != null) { toolbar.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt index 43a51601..e4249ab0 100644 --- a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt @@ -20,6 +20,7 @@ import awais.instagrabber.utils.getCsrfTokenFromCookie import awais.instagrabber.utils.getUserIdFromCookie import awais.instagrabber.webservices.DirectMessagesService import awais.instagrabber.webservices.DirectMessagesService.Companion.getInstance +import kotlinx.coroutines.CoroutineScope import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -108,21 +109,21 @@ object DirectMessagesManager { }) } - fun sendMedia(recipients: Set, mediaId: String) { + fun sendMedia(recipients: Set, mediaId: String, scope: CoroutineScope) { val resultsCount = intArrayOf(0) val callback: () -> Unit = { resultsCount[0]++ if (resultsCount[0] == recipients.size) { - inboxManager.refresh() + inboxManager.refresh(scope) } } for (recipient in recipients) { - sendMedia(recipient, mediaId, false, callback) + sendMedia(recipient, mediaId, false, callback, scope) } } - fun sendMedia(recipient: RankedRecipient, mediaId: String) { - sendMedia(recipient, mediaId, true, null) + fun sendMedia(recipient: RankedRecipient, mediaId: String, scope: CoroutineScope) { + sendMedia(recipient, mediaId, true, null, scope) } private fun sendMedia( @@ -130,6 +131,7 @@ object DirectMessagesManager { mediaId: String, refreshInbox: Boolean, callback: (() -> Unit)?, + scope: CoroutineScope, ) { if (recipient.thread == null && recipient.user != null) { // create thread and forward @@ -137,7 +139,7 @@ object DirectMessagesManager { val threadIdTemp = threadId ?: return@createThread sendMedia(threadIdTemp, mediaId) { if (refreshInbox) { - inboxManager.refresh() + inboxManager.refresh(scope) } callback?.invoke() } @@ -149,7 +151,7 @@ object DirectMessagesManager { val threadId = thread.threadId ?: return sendMedia(threadId, mediaId) { if (refreshInbox) { - inboxManager.refresh() + inboxManager.refresh(scope) } callback?.invoke() } diff --git a/app/src/main/java/awais/instagrabber/managers/InboxManager.kt b/app/src/main/java/awais/instagrabber/managers/InboxManager.kt index 963b2699..40a76d61 100644 --- a/app/src/main/java/awais/instagrabber/managers/InboxManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/InboxManager.kt @@ -11,15 +11,15 @@ 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.utils.* 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 kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -27,7 +27,9 @@ import java.util.* import java.util.concurrent.TimeUnit class InboxManager private constructor(private val pending: Boolean) { - private val inbox = MutableLiveData>() + // private val fetchInboxControlledRunner: ControlledRunner> = ControlledRunner() + // private val fetchPendingInboxControlledRunner: ControlledRunner> = ControlledRunner() + private val inbox = MutableLiveData>(success(null)) private val unseenCount = MutableLiveData>() private val pendingRequestsTotal = MutableLiveData(0) val threads: LiveData> @@ -52,30 +54,37 @@ class InboxManager private constructor(private val pending: Boolean) { return Transformations.distinctUntilChanged(pendingRequestsTotal) } - fun fetchInbox() { + fun fetchInbox(scope: CoroutineScope) { 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 { - 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 - } - parseInboxResponse(body) - } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "Failed fetching dm inbox", t) - inbox.postValue(error(t.message, currentDirectInbox)) + scope.launch(Dispatchers.IO) { + try { + val inboxValue = if (pending) service.fetchPendingInbox(cursor, seqId) else service.fetchInbox(cursor, seqId) + parseInboxResponse(inboxValue) + } catch (e: Exception) { + 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() { @@ -102,11 +111,11 @@ class InboxManager private constructor(private val pending: Boolean) { }) } - fun refresh() { + fun refresh(scope: CoroutineScope) { cursor = null seqId = 0 hasOlder = true - fetchInbox() + fetchInbox(scope) if (!pending) { fetchUnseenCount() } @@ -333,15 +342,15 @@ class InboxManager private constructor(private val pending: Boolean) { service = getInstance(csrfToken, viewerId, deviceUuid) // Transformations - threads = Transformations.distinctUntilChanged(Transformations.map(inbox) { inboxResource: Resource? -> - if (inboxResource == null) { - return@map emptyList() - } + threads = Transformations.distinctUntilChanged(Transformations.map(inbox) { inboxResource: Resource -> + // if (inboxResource == null) { + // return@map emptyList() + // } val inbox = inboxResource.data val threads = inbox?.threads ?: emptyList() ImmutableList.sortedCopyOf(THREAD_COMPARATOR, threads) }) - fetchInbox() + // fetchInbox() if (!pending) { fetchUnseenCount() } diff --git a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt index c716c3ca..070ef807 100644 --- a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt @@ -37,6 +37,9 @@ import awais.instagrabber.webservices.MediaService import awais.instagrabber.webservices.ServiceCallback import com.google.common.collect.ImmutableList import com.google.common.collect.Iterables +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -56,9 +59,12 @@ class ThreadManager private constructor( csrfToken: String, deviceUuid: String, ) { - private val fetching = MutableLiveData>() - private val replyToItem = MutableLiveData() - private val pendingRequests = MutableLiveData(null) + private val _fetching = MutableLiveData>() + val fetching: LiveData> = _fetching + private val _replyToItem = MutableLiveData() + val replyToItem: LiveData = _replyToItem + private val _pendingRequests = MutableLiveData(null) + val pendingRequests: LiveData = _pendingRequests private val inboxManager: InboxManager = if (pending) DirectMessagesManager.pendingInboxManager else DirectMessagesManager.inboxManager private val viewerId: Long private val threadIdOrUserIds: ThreadIdOrUserIds = of(threadId) @@ -106,7 +112,7 @@ class ThreadManager private constructor( val isMuted: LiveData by lazy { distinctUntilChanged(map(thread) { it?.muted ?: false }) } val isApprovalRequiredToJoin: LiveData by lazy { distinctUntilChanged(map(thread) { it?.approvalRequiredForNewMembers ?: false }) } val isMentionsMuted: LiveData by lazy { distinctUntilChanged(map(thread) { it?.mentionsMuted ?: false }) } - val pendingRequestsCount: LiveData by lazy { distinctUntilChanged(map(pendingRequests) { it?.totalParticipantRequests ?: 0 }) } + val pendingRequestsCount: LiveData by lazy { distinctUntilChanged(map(_pendingRequests) { it?.totalParticipantRequests ?: 0 }) } val inviter: LiveData by lazy { distinctUntilChanged(map(thread) { it?.inviter }) } private var hasOlder = true @@ -125,50 +131,58 @@ class ThreadManager private constructor( return builder.build() } - fun isFetching(): LiveData> { - return fetching - } - - fun getReplyToItem(): LiveData { - return replyToItem - } - - fun getPendingRequests(): LiveData { - return pendingRequests - } - - fun fetchChats() { - val fetchingValue = fetching.value + fun fetchChats(scope: CoroutineScope) { + val fetchingValue = _fetching.value if (fetchingValue != null && fetchingValue.status === Resource.Status.LOADING || !hasOlder) return - fetching.postValue(loading(null)) - chatsRequest = service.fetchThread(threadId, cursor) - chatsRequest?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - val feedResponse = response.body() - if (feedResponse == null) { - fetching.postValue(error(R.string.generic_null_response, null)) - Log.e(TAG, "onResponse: response was null!") - return + _fetching.postValue(loading(null)) + scope.launch(Dispatchers.IO) { + try { + val threadFeedResponse = service.fetchThread(threadId, cursor) + if (threadFeedResponse.status != null && threadFeedResponse.status != "ok") { + _fetching.postValue(error(R.string.generic_not_ok_response, null)) + return@launch } - if (feedResponse.status != null && feedResponse.status != "ok") { - fetching.postValue(error(R.string.generic_not_ok_response, null)) - return - } - val thread = feedResponse.thread + val thread = threadFeedResponse.thread if (thread == null) { - fetching.postValue(error("thread is null!", null)) - return + _fetching.postValue(error("thread is null!", null)) + return@launch } setThread(thread) - fetching.postValue(success(Any())) - } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "Failed fetching dm chats", t) - fetching.postValue(error(t.message, null)) + _fetching.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "Failed fetching dm chats", e) + _fetching.postValue(error(e.message, null)) hasOlder = false } - }) + + // chatsRequest?.enqueue(object : Callback { + // override fun onResponse(call: Call, response: Response) { + // val feedResponse = response.body() + // if (feedResponse == null) { + // fetching.postValue(error(R.string.generic_null_response, null)) + // Log.e(TAG, "onResponse: response was null!") + // return + // } + // if (feedResponse.status != null && feedResponse.status != "ok") { + // fetching.postValue(error(R.string.generic_not_ok_response, null)) + // return + // } + // val thread = feedResponse.thread + // if (thread == null) { + // fetching.postValue(error("thread is null!", null)) + // return + // } + // setThread(thread) + // fetching.postValue(success(Any())) + // } + // + // override fun onFailure(call: Call, t: Throwable) { + // Log.e(TAG, "Failed fetching dm chats", t) + // fetching.postValue(error(t.message, null)) + // hasOlder = false + // } + // }) + } if (cursor == null) { fetchPendingRequests() } @@ -206,7 +220,7 @@ class ThreadManager private constructor( Log.e(TAG, "onResponse: response body was null") return } - pendingRequests.postValue(body) + _pendingRequests.postValue(body) } override fun onFailure(call: Call, t: Throwable) { @@ -389,7 +403,7 @@ class ThreadManager private constructor( val data = MutableLiveData>() val userId = getCurrentUserId(data) ?: return data val clientContext = UUID.randomUUID().toString() - val replyToItemValue = replyToItem.value + val replyToItemValue = _replyToItem.value val directItem = createText(userId, clientContext, text, replyToItemValue) // Log.d(TAG, "sendText: sending: itemId: " + directItem.getItemId()); directItem.isPending = true @@ -630,7 +644,7 @@ class ThreadManager private constructor( fun setReplyToItem(item: DirectItem?) { // Log.d(TAG, "setReplyToItem: " + item); - replyToItem.postValue(item) + _replyToItem.postValue(item) } private fun forward(thread: DirectThread, itemToForward: DirectItem): LiveData> { @@ -770,14 +784,14 @@ class ThreadManager private constructor( return data } - fun refreshChats() { - val isFetching = fetching.value + fun refreshChats(scope: CoroutineScope) { + val isFetching = _fetching.value if (isFetching != null && isFetching.status === Resource.Status.LOADING) { stopCurrentRequest() } cursor = null hasOlder = true - fetchChats() + fetchChats(scope) } private fun sendPhoto( @@ -1076,7 +1090,7 @@ class ThreadManager private constructor( if (it.isExecuted || it.isCanceled) return it.cancel() } - fetching.postValue(success(Any())) + _fetching.postValue(success(Any())) } private fun getCurrentUserId(data: MutableLiveData>): Long? { @@ -1108,10 +1122,7 @@ class ThreadManager private constructor( val data = MutableLiveData>() val addUsersRequest = service.addUsers( threadId, - users.stream() - .filter { obj: User? -> Objects.nonNull(obj) } - .map { obj: User -> obj.pk } - .collect(Collectors.toList()) + users.map { obj: User -> obj.pk } ) handleDetailsChangeRequest(data, addUsersRequest) return data @@ -1135,10 +1146,7 @@ class ThreadManager private constructor( if (leftUsersValue == null) { leftUsersValue = emptyList() } - val updatedActiveUsers = activeUsers.stream() - .filter { obj: User? -> Objects.nonNull(obj) } - .filter { u: User -> u.pk != user.pk } - .collect(Collectors.toList()) + val updatedActiveUsers = activeUsers.filter { u: User -> u.pk != user.pk } val updatedLeftUsersBuilder = ImmutableList.builder().addAll(leftUsersValue) if (!leftUsersValue.contains(user)) { updatedLeftUsersBuilder.add(user) @@ -1204,10 +1212,7 @@ class ThreadManager private constructor( return } val currentAdmins = adminUserIds.value ?: return - val updatedAdminUserIds = currentAdmins.stream() - .filter { obj: Long? -> Objects.nonNull(obj) } - .filter { userId1: Long -> userId1 != user.pk } - .collect(Collectors.toList()) + val updatedAdminUserIds = currentAdmins.filter { userId1: Long -> userId1 != user.pk } val currentThread = thread.value ?: return try { val thread = currentThread.clone() as DirectThread @@ -1362,11 +1367,11 @@ class ThreadManager private constructor( return data } - fun blockUser(user: User): LiveData> { + fun blockUser(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() friendshipService.changeBlock(false, user.pk, object : ServiceCallback { override fun onSuccess(result: FriendshipChangeResponse?) { - refreshChats() + refreshChats(scope) } override fun onFailure(t: Throwable) { @@ -1377,11 +1382,11 @@ class ThreadManager private constructor( return data } - fun unblockUser(user: User): LiveData> { + fun unblockUser(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() friendshipService.changeBlock(true, user.pk, object : ServiceCallback { override fun onSuccess(result: FriendshipChangeResponse?) { - refreshChats() + refreshChats(scope) } override fun onFailure(t: Throwable) { @@ -1392,11 +1397,11 @@ class ThreadManager private constructor( return data } - fun restrictUser(user: User): LiveData> { + fun restrictUser(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() friendshipService.toggleRestrict(user.pk, true, object : ServiceCallback { override fun onSuccess(result: FriendshipRestrictResponse?) { - refreshChats() + refreshChats(scope) } override fun onFailure(t: Throwable) { @@ -1407,11 +1412,11 @@ class ThreadManager private constructor( return data } - fun unRestrictUser(user: User): LiveData> { + fun unRestrictUser(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() friendshipService.toggleRestrict(user.pk, false, object : ServiceCallback { override fun onSuccess(result: FriendshipRestrictResponse?) { - refreshChats() + refreshChats(scope) } override fun onFailure(t: Throwable) { @@ -1427,10 +1432,7 @@ class ThreadManager private constructor( data.postValue(loading(null)) val approveUsersRequest = service.approveParticipantRequests( threadId, - users.stream() - .filter { obj: User? -> Objects.nonNull(obj) } - .map { obj: User -> obj.pk } - .collect(Collectors.toList()) + users.map { obj: User -> obj.pk } ) handleDetailsChangeRequest(data, approveUsersRequest, object : OnSuccessAction { override fun onSuccess() { @@ -1445,9 +1447,7 @@ class ThreadManager private constructor( data.postValue(loading(null)) val approveUsersRequest = service.declineParticipantRequests( threadId, - users.stream() - .map { obj: User -> obj.pk } - .collect(Collectors.toList()) + users.map { obj: User -> obj.pk } ) handleDetailsChangeRequest(data, approveUsersRequest, object : OnSuccessAction { override fun onSuccess() { @@ -1458,18 +1458,16 @@ class ThreadManager private constructor( } private fun pendingUserApproveDenySuccessAction(users: List) { - val pendingRequestsValue = pendingRequests.value ?: return + val pendingRequestsValue = _pendingRequests.value ?: return val pendingUsers = pendingRequestsValue.users if (pendingUsers == null || pendingUsers.isEmpty()) return - val filtered = pendingUsers.stream() - .filter { o: User -> !users.contains(o) } - .collect(Collectors.toList()) + val filtered = pendingUsers.filter { o: User -> !users.contains(o) } try { val clone = pendingRequestsValue.clone() as DirectThreadParticipantRequestsResponse clone.users = filtered val totalParticipantRequests = clone.totalParticipantRequests clone.totalParticipantRequests = if (totalParticipantRequests > 0) totalParticipantRequests - 1 else 0 - pendingRequests.postValue(clone) + _pendingRequests.postValue(clone) } catch (e: CloneNotSupportedException) { Log.e(TAG, "pendingUserApproveDenySuccessAction: ", e) } diff --git a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt index 218e231c..9ebfb2ea 100644 --- a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt +++ b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt @@ -6,16 +6,16 @@ import retrofit2.http.* interface DirectMessagesRepository { @GET("/api/v1/direct_v2/inbox/") - fun fetchInbox(@QueryMap queryMap: Map): Call + suspend fun fetchInbox(@QueryMap queryMap: Map): DirectInboxResponse @GET("/api/v1/direct_v2/pending_inbox/") - fun fetchPendingInbox(@QueryMap queryMap: Map): Call + suspend fun fetchPendingInbox(@QueryMap queryMap: Map): DirectInboxResponse @GET("/api/v1/direct_v2/threads/{threadId}/") - fun fetchThread( + suspend fun fetchThread( @Path("threadId") threadId: String, @QueryMap queryMap: Map, - ): Call + ): DirectThreadFeedResponse @GET("/api/v1/direct_v2/get_badge_count/?no_raven=1") fun fetchUnseenCount(): Call diff --git a/app/src/main/java/awais/instagrabber/services/DMSyncService.java b/app/src/main/java/awais/instagrabber/services/DMSyncService.java index 279effed..6a299945 100644 --- a/app/src/main/java/awais/instagrabber/services/DMSyncService.java +++ b/app/src/main/java/awais/instagrabber/services/DMSyncService.java @@ -234,7 +234,7 @@ public class DMSyncService extends LifecycleService { parseUnread(directInbox); }); Log.d(TAG, "onStartCommand: refreshing inbox"); - inboxManager.refresh(); + // inboxManager.refresh(); return START_NOT_STICKY; } diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.java deleted file mode 100644 index ca1906f3..00000000 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.java +++ /dev/null @@ -1,56 +0,0 @@ -package awais.instagrabber.viewmodels; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.ViewModel; - -import java.util.List; - -import awais.instagrabber.managers.DirectMessagesManager; -import awais.instagrabber.managers.InboxManager; -import awais.instagrabber.models.Resource; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.directmessages.DirectInbox; -import awais.instagrabber.repositories.responses.directmessages.DirectThread; - -public class DirectInboxViewModel extends ViewModel { - private static final String TAG = DirectInboxViewModel.class.getSimpleName(); - - private final InboxManager inboxManager; - - public DirectInboxViewModel() { - final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE; - inboxManager = messagesManager.getInboxManager(); - } - - public LiveData> getInbox() { - return inboxManager.getInbox(); - } - - public LiveData> getThreads() { - return inboxManager.getThreads(); - } - - public LiveData> getUnseenCount() { - return inboxManager.getUnseenCount(); - } - - public LiveData getPendingRequestsTotal() { - return inboxManager.getPendingRequestsTotal(); - } - - public User getViewer() { - return inboxManager.getViewer(); - } - - public void fetchInbox() { - inboxManager.fetchInbox(); - } - - public void refresh() { - inboxManager.refresh(); - } - - public void onDestroy() { - inboxManager.onDestroy(); - } -} diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt new file mode 100644 index 00000000..a4546c2c --- /dev/null +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt @@ -0,0 +1,36 @@ +package awais.instagrabber.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import awais.instagrabber.managers.DirectMessagesManager +import awais.instagrabber.managers.InboxManager +import awais.instagrabber.models.Resource +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.repositories.responses.directmessages.DirectInbox +import awais.instagrabber.repositories.responses.directmessages.DirectThread + +class DirectInboxViewModel : ViewModel() { + private val inboxManager: InboxManager = DirectMessagesManager.inboxManager + val inbox: LiveData> = inboxManager.getInbox() + val threads: LiveData> = inboxManager.threads + val unseenCount: LiveData> = inboxManager.getUnseenCount() + val pendingRequestsTotal: LiveData = inboxManager.getPendingRequestsTotal() + val viewer: User? = inboxManager.viewer + + fun fetchInbox() { + inboxManager.fetchInbox(viewModelScope) + } + + fun refresh() { + inboxManager.refresh(viewModelScope) + } + + fun onDestroy() { + inboxManager.onDestroy() + } + + init { + inboxManager.fetchInbox(viewModelScope) + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.java deleted file mode 100644 index bf29e41e..00000000 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.java +++ /dev/null @@ -1,48 +0,0 @@ -package awais.instagrabber.viewmodels; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.ViewModel; - -import java.util.List; - -import awais.instagrabber.managers.DirectMessagesManager; -import awais.instagrabber.managers.InboxManager; -import awais.instagrabber.models.Resource; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.directmessages.DirectInbox; -import awais.instagrabber.repositories.responses.directmessages.DirectThread; - -public class DirectPendingInboxViewModel extends ViewModel { - private static final String TAG = DirectPendingInboxViewModel.class.getSimpleName(); - - private final InboxManager inboxManager; - - public DirectPendingInboxViewModel() { - inboxManager = DirectMessagesManager.INSTANCE.getPendingInboxManager(); - inboxManager.fetchInbox(); - } - - public LiveData> getThreads() { - return inboxManager.getThreads(); - } - - public LiveData> getInbox() { - return inboxManager.getInbox(); - } - - public User getViewer() { - return inboxManager.getViewer(); - } - - public void fetchInbox() { - inboxManager.fetchInbox(); - } - - public void refresh() { - inboxManager.refresh(); - } - - public void onDestroy() { - inboxManager.onDestroy(); - } -} diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.kt new file mode 100644 index 00000000..b269ac9f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.kt @@ -0,0 +1,34 @@ +package awais.instagrabber.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import awais.instagrabber.managers.DirectMessagesManager.pendingInboxManager +import awais.instagrabber.managers.InboxManager +import awais.instagrabber.models.Resource +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.repositories.responses.directmessages.DirectInbox +import awais.instagrabber.repositories.responses.directmessages.DirectThread + +class DirectPendingInboxViewModel : ViewModel() { + private val inboxManager: InboxManager = pendingInboxManager + val threads: LiveData> = inboxManager.threads + val inbox: LiveData> = inboxManager.getInbox() + val viewer: User? = inboxManager.viewer + + fun fetchInbox() { + inboxManager.fetchInbox(viewModelScope) + } + + fun refresh() { + inboxManager.refresh(viewModelScope) + } + + fun onDestroy() { + inboxManager.onDestroy() + } + + init { + inboxManager.fetchInbox(viewModelScope) + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java deleted file mode 100644 index ea6be8f1..00000000 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java +++ /dev/null @@ -1,299 +0,0 @@ -package awais.instagrabber.viewmodels; - -import android.app.Application; -import android.content.ContentResolver; -import android.content.res.Resources; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.core.util.Pair; -import androidx.lifecycle.AndroidViewModel; -import androidx.lifecycle.LiveData; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import awais.instagrabber.R; -import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option; -import awais.instagrabber.managers.DirectMessagesManager; -import awais.instagrabber.managers.ThreadManager; -import awais.instagrabber.models.Resource; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.directmessages.DirectThread; -import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.TextUtils; - -import static awais.instagrabber.utils.Utils.settingsHelper; - -public class DirectSettingsViewModel extends AndroidViewModel { - private static final String TAG = DirectSettingsViewModel.class.getSimpleName(); - private static final String ACTION_KICK = "kick"; - private static final String ACTION_MAKE_ADMIN = "make_admin"; - private static final String ACTION_REMOVE_ADMIN = "remove_admin"; - private static final String ACTION_BLOCK = "block"; - private static final String ACTION_UNBLOCK = "unblock"; - // private static final String ACTION_REPORT = "report"; - private static final String ACTION_RESTRICT = "restrict"; - private static final String ACTION_UNRESTRICT = "unrestrict"; - - private final long viewerId; - private final Resources resources; - private final ThreadManager threadManager; - - public DirectSettingsViewModel(final Application application, - @NonNull final String threadId, - final boolean pending, - @NonNull final User currentUser) { - super(application); - final String cookie = settingsHelper.getString(Constants.COOKIE); - viewerId = CookieUtils.getUserIdFromCookie(cookie); - final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); - final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); - if (TextUtils.isEmpty(csrfToken) || viewerId <= 0 || TextUtils.isEmpty(deviceUuid)) { - throw new IllegalArgumentException("User is not logged in!"); - } - final ContentResolver contentResolver = application.getContentResolver(); - resources = getApplication().getResources(); - final DirectMessagesManager messagesManager = DirectMessagesManager.INSTANCE; - threadManager = messagesManager.getThreadManager(threadId, pending, currentUser, contentResolver); - } - - @NonNull - public LiveData getThread() { - return threadManager.getThread(); - } - - // public void setThread(@NonNull final DirectThread thread) { - // this.thread = thread; - // inputMode.postValue(thread.getInputMode()); - // List users = thread.getUsers(); - // final ImmutableList.Builder builder = ImmutableList.builder().add(currentUser); - // if (users != null) { - // builder.addAll(users); - // } - // users = builder.build(); - // this.users.postValue(new Pair<>(users, thread.getLeftUsers())); - // // setTitle(thread.getThreadTitle()); - // final List adminUserIds = thread.getAdminUserIds(); - // this.adminUserIds.postValue(adminUserIds); - // viewerIsAdmin = adminUserIds.contains(viewerId); - // muted.postValue(thread.getMuted()); - // mentionsMuted.postValue(thread.isMentionsMuted()); - // approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers()); - // isPending.postValue(thread.isPending()); - // if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) { - // fetchPendingRequests(); - // } - // } - - public LiveData getInputMode() { - return threadManager.getInputMode(); - } - - public LiveData isGroup() { - return threadManager.isGroup(); - } - - public LiveData> getUsers() { - return threadManager.getUsersWithCurrent(); - } - - public LiveData> getLeftUsers() { - return threadManager.getLeftUsers(); - } - - public LiveData, List>> getUsersAndLeftUsers() { - return threadManager.getUsersAndLeftUsers(); - } - - public LiveData getTitle() { - return threadManager.getThreadTitle(); - } - - // public void setTitle(final String title) { - // if (title == null) { - // this.title.postValue(""); - // return; - // } - // this.title.postValue(title.trim()); - // } - - public LiveData> getAdminUserIds() { - return threadManager.getAdminUserIds(); - } - - public LiveData isMuted() { - return threadManager.isMuted(); - } - - public LiveData getApprovalRequiredToJoin() { - return threadManager.isApprovalRequiredToJoin(); - } - - public LiveData getPendingRequests() { - return threadManager.getPendingRequests(); - } - - public LiveData isPending() { - return threadManager.isPending(); - } - - public LiveData isViewerAdmin() { - return threadManager.isViewerAdmin(); - } - - public LiveData> updateTitle(final String newTitle) { - return threadManager.updateTitle(newTitle); - } - - public LiveData> addMembers(final Set users) { - return threadManager.addMembers(users); - } - - public LiveData> removeMember(final User user) { - return threadManager.removeMember(user); - } - - private LiveData> makeAdmin(final User user) { - return threadManager.makeAdmin(user); - } - - private LiveData> removeAdmin(final User user) { - return threadManager.removeAdmin(user); - } - - public LiveData> mute() { - return threadManager.mute(); - } - - public LiveData> unmute() { - return threadManager.unmute(); - } - - public LiveData> muteMentions() { - return threadManager.muteMentions(); - } - - public LiveData> unmuteMentions() { - return threadManager.unmuteMentions(); - } - - private LiveData> blockUser(final User user) { - return threadManager.blockUser(user); - } - - private LiveData> unblockUser(final User user) { - return threadManager.unblockUser(user); - } - - private LiveData> restrictUser(final User user) { - return threadManager.restrictUser(user); - } - - private LiveData> unRestrictUser(final User user) { - return threadManager.unRestrictUser(user); - } - - public LiveData> approveUsers(final List users) { - return threadManager.approveUsers(users); - } - - public LiveData> denyUsers(final List users) { - return threadManager.denyUsers(users); - } - - public LiveData> approvalRequired() { - return threadManager.approvalRequired(); - } - - public LiveData> approvalNotRequired() { - return threadManager.approvalNotRequired(); - } - - public LiveData> leave() { - return threadManager.leave(); - } - - public LiveData> end() { - return threadManager.end(); - } - - public ArrayList> createUserOptions(final User user) { - final ArrayList> options = new ArrayList<>(); - if (user == null || isSelf(user) || hasLeft(user)) { - return options; - } - final Boolean viewerIsAdmin = threadManager.isViewerAdmin().getValue(); - if (viewerIsAdmin != null && viewerIsAdmin) { - options.add(new Option<>(getString(R.string.dms_action_kick), ACTION_KICK)); - - final boolean isAdmin = threadManager.isAdmin(user); - options.add(new Option<>( - isAdmin ? getString(R.string.dms_action_remove_admin) : getString(R.string.dms_action_make_admin), - isAdmin ? ACTION_REMOVE_ADMIN : ACTION_MAKE_ADMIN - )); - } - - final boolean blocking = user.getFriendshipStatus().getBlocking(); - options.add(new Option<>( - blocking ? getString(R.string.unblock) : getString(R.string.block), - blocking ? ACTION_UNBLOCK : ACTION_BLOCK - )); - - // options.add(new Option<>(getString(R.string.report), ACTION_REPORT)); - final Boolean isGroup = threadManager.isGroup().getValue(); - if (isGroup != null && isGroup) { - final boolean restricted = user.getFriendshipStatus().isRestricted(); - options.add(new Option<>( - restricted ? getString(R.string.unrestrict) : getString(R.string.restrict), - restricted ? ACTION_UNRESTRICT : ACTION_RESTRICT - )); - } - return options; - } - - private boolean hasLeft(final User user) { - final List leftUsers = getLeftUsers().getValue(); - if (leftUsers == null) return false; - return leftUsers.contains(user); - } - - private boolean isSelf(final User user) { - return user.getPk() == viewerId; - } - - private String getString(@StringRes final int resId) { - return resources.getString(resId); - } - - public LiveData> doAction(final User user, final String action) { - if (user == null || action == null) return null; - switch (action) { - case ACTION_KICK: - return removeMember(user); - case ACTION_MAKE_ADMIN: - return makeAdmin(user); - case ACTION_REMOVE_ADMIN: - return removeAdmin(user); - case ACTION_BLOCK: - return blockUser(user); - case ACTION_UNBLOCK: - return unblockUser(user); - // case ACTION_REPORT: - // break; - case ACTION_RESTRICT: - return restrictUser(user); - case ACTION_UNRESTRICT: - return unRestrictUser(user); - default: - return null; - } - } - - public LiveData getInviter() { - return threadManager.getInviter(); - } -} diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt new file mode 100644 index 00000000..65588ee1 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt @@ -0,0 +1,201 @@ +package awais.instagrabber.viewmodels + +import android.app.Application +import androidx.annotation.StringRes +import androidx.core.util.Pair +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope +import awais.instagrabber.R +import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option +import awais.instagrabber.managers.DirectMessagesManager +import awais.instagrabber.models.Resource +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.repositories.responses.directmessages.DirectThread +import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse +import awais.instagrabber.utils.Constants +import awais.instagrabber.utils.Utils +import awais.instagrabber.utils.getCsrfTokenFromCookie +import awais.instagrabber.utils.getUserIdFromCookie + +class DirectSettingsViewModel( + application: Application, + threadId: String, + pending: Boolean, + currentUser: User, +) : AndroidViewModel(application) { + private val viewerId: Long + private val resources = application.resources + private val threadManager = DirectMessagesManager.getThreadManager(threadId, pending, currentUser, application.contentResolver) + + val thread: LiveData = threadManager.thread + + // public void setThread(@NonNull final DirectThread thread) { + // this.thread = thread; + // inputMode.postValue(thread.getInputMode()); + // List users = thread.getUsers(); + // final ImmutableList.Builder builder = ImmutableList.builder().add(currentUser); + // if (users != null) { + // builder.addAll(users); + // } + // users = builder.build(); + // this.users.postValue(new Pair<>(users, thread.getLeftUsers())); + // // setTitle(thread.getThreadTitle()); + // final List adminUserIds = thread.getAdminUserIds(); + // this.adminUserIds.postValue(adminUserIds); + // viewerIsAdmin = adminUserIds.contains(viewerId); + // muted.postValue(thread.getMuted()); + // mentionsMuted.postValue(thread.isMentionsMuted()); + // approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers()); + // isPending.postValue(thread.isPending()); + // if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) { + // fetchPendingRequests(); + // } + // } + val inputMode: LiveData = threadManager.inputMode + + fun isGroup(): LiveData = threadManager.isGroup + + fun getUsers(): LiveData> = threadManager.usersWithCurrent + + fun getLeftUsers(): LiveData> = threadManager.leftUsers + + fun getUsersAndLeftUsers(): LiveData, List>> = threadManager.usersAndLeftUsers + + fun getTitle(): LiveData = threadManager.threadTitle + + // public void setTitle(final String title) { + // if (title == null) { + // this.title.postValue(""); + // return; + // } + // this.title.postValue(title.trim()); + // } + fun getAdminUserIds(): LiveData> = threadManager.adminUserIds + + fun isMuted(): LiveData = threadManager.isMuted + + fun getApprovalRequiredToJoin(): LiveData = threadManager.isApprovalRequiredToJoin + + fun getPendingRequests(): LiveData = threadManager.pendingRequests + + fun isPending(): LiveData = threadManager.isPending + + fun isViewerAdmin(): LiveData = threadManager.isViewerAdmin + + fun updateTitle(newTitle: String): LiveData> = threadManager.updateTitle(newTitle) + + fun addMembers(users: Set): LiveData> = threadManager.addMembers(users) + + fun removeMember(user: User): LiveData> = threadManager.removeMember(user) + + private fun makeAdmin(user: User): LiveData> = threadManager.makeAdmin(user) + + private fun removeAdmin(user: User): LiveData> = threadManager.removeAdmin(user) + + fun mute(): LiveData> = threadManager.mute() + + fun unmute(): LiveData> = threadManager.unmute() + + fun muteMentions(): LiveData> = threadManager.muteMentions() + + fun unmuteMentions(): LiveData> = threadManager.unmuteMentions() + + private fun blockUser(user: User): LiveData> = threadManager.blockUser(user, viewModelScope) + + private fun unblockUser(user: User): LiveData> = threadManager.unblockUser(user, viewModelScope) + + private fun restrictUser(user: User): LiveData> = threadManager.restrictUser(user, viewModelScope) + + private fun unRestrictUser(user: User): LiveData> = threadManager.unRestrictUser(user, viewModelScope) + + fun approveUsers(users: List): LiveData> = threadManager.approveUsers(users) + + fun denyUsers(users: List): LiveData> = threadManager.denyUsers(users) + + fun approvalRequired(): LiveData> = threadManager.approvalRequired() + + fun approvalNotRequired(): LiveData> = threadManager.approvalNotRequired() + + fun leave(): LiveData> = threadManager.leave() + + fun end(): LiveData> = threadManager.end() + + fun createUserOptions(user: User?): ArrayList> { + val options: ArrayList> = ArrayList() + if (user == null || isSelf(user) || hasLeft(user)) { + return options + } + val viewerIsAdmin: Boolean? = threadManager.isViewerAdmin.value + if (viewerIsAdmin != null && viewerIsAdmin) { + options.add(Option(getString(R.string.dms_action_kick), ACTION_KICK)) + val isAdmin: Boolean = threadManager.isAdmin(user) + options.add(Option( + if (isAdmin) getString(R.string.dms_action_remove_admin) else getString(R.string.dms_action_make_admin), + if (isAdmin) ACTION_REMOVE_ADMIN else ACTION_MAKE_ADMIN + )) + } + val blocking: Boolean = user.friendshipStatus.blocking + options.add(Option( + if (blocking) getString(R.string.unblock) else getString(R.string.block), + if (blocking) ACTION_UNBLOCK else ACTION_BLOCK + )) + + // options.add(new Option<>(getString(R.string.report), ACTION_REPORT)); + val isGroup: Boolean? = threadManager.isGroup.value + if (isGroup != null && isGroup) { + val restricted: Boolean = user.friendshipStatus.isRestricted + options.add(Option( + if (restricted) getString(R.string.unrestrict) else getString(R.string.restrict), + if (restricted) ACTION_UNRESTRICT else ACTION_RESTRICT + )) + } + return options + } + + private fun hasLeft(user: User): Boolean { + val leftUsers: List = getLeftUsers().value ?: return false + return leftUsers.contains(user) + } + + private fun isSelf(user: User): Boolean = user.pk == viewerId + + private fun getString(@StringRes resId: Int): String { + return resources.getString(resId) + } + + fun doAction(user: User?, action: String?): LiveData>? { + return if (user == null || action == null) null else when (action) { + ACTION_KICK -> removeMember(user) + ACTION_MAKE_ADMIN -> makeAdmin(user) + ACTION_REMOVE_ADMIN -> removeAdmin(user) + ACTION_BLOCK -> blockUser(user) + ACTION_UNBLOCK -> unblockUser(user) + ACTION_RESTRICT -> restrictUser(user) + ACTION_UNRESTRICT -> unRestrictUser(user) + else -> null + } + } + + fun getInviter(): LiveData = threadManager.inviter + + companion object { + private const val ACTION_KICK = "kick" + private const val ACTION_MAKE_ADMIN = "make_admin" + private const val ACTION_REMOVE_ADMIN = "remove_admin" + private const val ACTION_BLOCK = "block" + private const val ACTION_UNBLOCK = "unblock" + + // private static final String ACTION_REPORT = "report"; + private const val ACTION_RESTRICT = "restrict" + private const val ACTION_UNRESTRICT = "unrestrict" + } + + init { + val cookie = Utils.settingsHelper.getString(Constants.COOKIE) + 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!" } + } +} \ 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 ab2c3067..facb2495 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt @@ -5,10 +5,7 @@ import android.content.ContentResolver import android.media.MediaScannerConnection import android.net.Uri import android.util.Log -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations +import androidx.lifecycle.* import awais.instagrabber.customviews.emoji.Emoji import awais.instagrabber.managers.DirectMessagesManager import awais.instagrabber.managers.DirectMessagesManager.inboxManager @@ -50,13 +47,13 @@ class DirectThreadViewModel( val items: LiveData> by lazy { Transformations.map(threadManager.items) { it.filter { thread -> thread.hideInThread == 0 } } } - val isFetching: LiveData> by lazy { threadManager.isFetching() } + val isFetching: LiveData> by lazy { threadManager.fetching } val users: LiveData> by lazy { threadManager.users } val leftUsers: LiveData> by lazy { threadManager.leftUsers } val pendingRequestsCount: LiveData by lazy { threadManager.pendingRequestsCount } val inputMode: LiveData by lazy { threadManager.inputMode } val isPending: LiveData by lazy { threadManager.isPending } - val replyToItem: LiveData by lazy { threadManager.getReplyToItem() } + val replyToItem: LiveData by lazy { threadManager.replyToItem } fun moveFromPending() { val messagesManager = DirectMessagesManager @@ -69,11 +66,11 @@ class DirectThreadViewModel( } fun fetchChats() { - threadManager.fetchChats() + threadManager.fetchChats(viewModelScope) } fun refreshChats() { - threadManager.refreshChats() + threadManager.refreshChats(viewModelScope) } fun sendText(text: String): LiveData> { diff --git a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.kt index e0c36a1b..f70aac34 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.kt @@ -1,347 +1,329 @@ -package awais.instagrabber.viewmodels; +package awais.instagrabber.viewmodels -import android.util.Log; +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import awais.instagrabber.R +import awais.instagrabber.managers.DirectMessagesManager +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.MediaItemType +import awais.instagrabber.repositories.responses.Caption +import awais.instagrabber.repositories.responses.Location +import awais.instagrabber.repositories.responses.Media +import awais.instagrabber.repositories.responses.User +import awais.instagrabber.repositories.responses.directmessages.RankedRecipient +import awais.instagrabber.utils.Constants +import awais.instagrabber.utils.Utils +import awais.instagrabber.utils.extensions.TAG +import awais.instagrabber.utils.getCsrfTokenFromCookie +import awais.instagrabber.utils.getUserIdFromCookie +import awais.instagrabber.webservices.MediaService +import awais.instagrabber.webservices.ServiceCallback +import com.google.common.collect.ImmutableList +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.util.* -import androidx.annotation.NonNull; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; +class PostViewV2ViewModel : ViewModel() { + private val user = MutableLiveData() + private val caption = MutableLiveData() + private val location = MutableLiveData() + private val date = MutableLiveData() + private val likeCount = MutableLiveData(0L) + private val commentCount = MutableLiveData(0L) + private val viewCount = MutableLiveData(0L) + private val type = MutableLiveData() + private val liked = MutableLiveData(false) + private val saved = MutableLiveData(false) + private val options = MutableLiveData>(ArrayList()) + private val viewerId: Long + val isLoggedIn: Boolean + lateinit var media: Media + private set + private var mediaService: MediaService? = null + private var messageManager: DirectMessagesManager? = null -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import awais.instagrabber.R; -import awais.instagrabber.managers.DirectMessagesManager; -import awais.instagrabber.models.Resource; -import awais.instagrabber.models.enums.MediaItemType; -import awais.instagrabber.repositories.responses.Caption; -import awais.instagrabber.repositories.responses.Location; -import awais.instagrabber.repositories.responses.Media; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.CookieUtils; -import awais.instagrabber.utils.TextUtils; -import awais.instagrabber.webservices.MediaService; -import awais.instagrabber.webservices.ServiceCallback; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -import static awais.instagrabber.utils.Utils.settingsHelper; - -public class PostViewV2ViewModel extends ViewModel { - private static final String TAG = PostViewV2ViewModel.class.getSimpleName(); - - private final MutableLiveData user = new MutableLiveData<>(); - private final MutableLiveData caption = new MutableLiveData<>(); - private final MutableLiveData location = new MutableLiveData<>(); - private final MutableLiveData date = new MutableLiveData<>(); - private final MutableLiveData likeCount = new MutableLiveData<>(0L); - private final MutableLiveData commentCount = new MutableLiveData<>(0L); - private final MutableLiveData viewCount = new MutableLiveData<>(0L); - private final MutableLiveData type = new MutableLiveData<>(); - private final MutableLiveData liked = new MutableLiveData<>(false); - private final MutableLiveData saved = new MutableLiveData<>(false); - private final MutableLiveData> options = new MutableLiveData<>(new ArrayList<>()); - private final MediaService mediaService; - private final long viewerId; - private final boolean isLoggedIn; - - private Media media; - private DirectMessagesManager messageManager; - - public PostViewV2ViewModel() { - final String cookie = settingsHelper.getString(Constants.COOKIE); - final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); - final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); - viewerId = CookieUtils.getUserIdFromCookie(cookie); - mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId); - isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; + fun setMedia(media: Media) { + this.media = media + user.postValue(media.user) + caption.postValue(media.caption) + location.postValue(media.location) + date.postValue(media.date) + likeCount.postValue(media.likeCount) + commentCount.postValue(media.commentCount) + viewCount.postValue(if (media.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) media.viewCount else null) + type.postValue(media.mediaType) + liked.postValue(media.hasLiked) + saved.postValue(media.hasViewerSaved) + initOptions() } - public void setMedia(final Media media) { - this.media = media; - user.postValue(media.getUser()); - caption.postValue(media.getCaption()); - location.postValue(media.getLocation()); - date.postValue(media.getDate()); - likeCount.postValue(media.getLikeCount()); - commentCount.postValue(media.getCommentCount()); - viewCount.postValue(media.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? media.getViewCount() : null); - type.postValue(media.getMediaType()); - liked.postValue(media.getHasLiked()); - saved.postValue(media.getHasViewerSaved()); - initOptions(); - } - - private void initOptions() { - final ImmutableList.Builder builder = ImmutableList.builder(); - if (isLoggedIn && media.getUser() != null && media.getUser().getPk() == viewerId) { - builder.add(R.id.edit_caption); - builder.add(R.id.delete); + private fun initOptions() { + val builder = ImmutableList.builder() + val user1 = media.user + if (isLoggedIn && user1 != null && user1.pk == viewerId) { + builder.add(R.id.edit_caption) + builder.add(R.id.delete) } - options.postValue(builder.build()); + options.postValue(builder.build()) } - public Media getMedia() { - return media; + fun getUser(): LiveData { + return user } - public boolean isLoggedIn() { - return isLoggedIn; + fun getCaption(): LiveData { + return caption } - public LiveData getUser() { - return user; + fun getLocation(): LiveData { + return location } - public LiveData getCaption() { - return caption; + fun getDate(): LiveData { + return date } - public LiveData getLocation() { - return location; + fun getLikeCount(): LiveData { + return likeCount } - public LiveData getDate() { - return date; + fun getCommentCount(): LiveData { + return commentCount } - public LiveData getLikeCount() { - return likeCount; + fun getViewCount(): LiveData { + return viewCount } - public LiveData getCommentCount() { - return commentCount; + fun getType(): LiveData { + return type } - public LiveData getViewCount() { - return viewCount; + fun getLiked(): LiveData { + return liked } - public LiveData getType() { - return type; + fun getSaved(): LiveData { + return saved } - public LiveData getLiked() { - return liked; + fun getOptions(): LiveData> { + return options } - public LiveData getSaved() { - return saved; + fun toggleLike(): LiveData> { + return if (media.hasLiked) { + unlike() + } else like() } - public LiveData> getOptions() { - return options; + fun like(): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + mediaService?.like(media.pk, getLikeUnlikeCallback(data)) + return data } - @NonNull - public LiveData> toggleLike() { - if (media.getHasLiked()) { - return unlike(); - } - return like(); + fun unlike(): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + mediaService?.unlike(media.pk, getLikeUnlikeCallback(data)) + return data } - public LiveData> like() { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - mediaService.like(media.getPk(), getLikeUnlikeCallback(data)); - return data; - } - - public LiveData> unlike() { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - mediaService.unlike(media.getPk(), getLikeUnlikeCallback(data)); - return data; - } - - @NonNull - private ServiceCallback getLikeUnlikeCallback(final MutableLiveData> data) { - return new ServiceCallback() { - @Override - public void onSuccess(final Boolean result) { - if (!result) { - data.postValue(Resource.error("", null)); - return; + private fun getLikeUnlikeCallback(data: MutableLiveData>): ServiceCallback { + return object : ServiceCallback { + override fun onSuccess(result: Boolean?) { + if (result != null && !result) { + data.postValue(error("", null)) + return } - data.postValue(Resource.success(true)); - final long currentLikesCount = media.getLikeCount(); - final long updatedCount; - if (!media.getHasLiked()) { - updatedCount = currentLikesCount + 1; - media.setHasLiked(true); + data.postValue(success(true)) + val currentLikesCount = media.likeCount + val updatedCount: Long + if (!media.hasLiked) { + updatedCount = currentLikesCount + 1 + media.hasLiked = true } else { - updatedCount = currentLikesCount - 1; - media.setHasLiked(false); + updatedCount = currentLikesCount - 1 + media.hasLiked = false } - media.setLikeCount(updatedCount); - likeCount.postValue(updatedCount); - liked.postValue(media.getHasLiked()); + media.likeCount = updatedCount + likeCount.postValue(updatedCount) + liked.postValue(media.hasLiked) } - @Override - public void onFailure(final Throwable t) { - data.postValue(Resource.error(t.getMessage(), null)); - Log.e(TAG, "Error during like/unlike", t); + override fun onFailure(t: Throwable) { + data.postValue(error(t.message, null)) + Log.e(TAG, "Error during like/unlike", t) } - }; - } - - @NonNull - public LiveData> toggleSave() { - if (!media.getHasViewerSaved()) { - return save(null, false); } - return unsave(); } - @NonNull - public LiveData> toggleSave(final String collection, final boolean ignoreSaveState) { - return save(collection, ignoreSaveState); + fun toggleSave(): LiveData> { + return if (!media.hasViewerSaved) { + save(null, false) + } else unsave() } - public LiveData> save(final String collection, final boolean ignoreSaveState) { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - mediaService.save(media.getPk(), collection, getSaveUnsaveCallback(data, ignoreSaveState)); - return data; + fun toggleSave(collection: String?, ignoreSaveState: Boolean): LiveData> { + return save(collection, ignoreSaveState) } - public LiveData> unsave() { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - mediaService.unsave(media.getPk(), getSaveUnsaveCallback(data, false)); - return data; + fun save(collection: String?, ignoreSaveState: Boolean): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + mediaService?.save(media.pk, collection, getSaveUnsaveCallback(data, ignoreSaveState)) + return data } - @NonNull - private ServiceCallback getSaveUnsaveCallback(final MutableLiveData> data, - final boolean ignoreSaveState) { - return new ServiceCallback() { - @Override - public void onSuccess(final Boolean result) { - if (!result) { - data.postValue(Resource.error("", null)); - return; + fun unsave(): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + mediaService?.unsave(media.pk, getSaveUnsaveCallback(data, false)) + return data + } + + private fun getSaveUnsaveCallback( + data: MutableLiveData>, + ignoreSaveState: Boolean, + ): ServiceCallback { + return object : ServiceCallback { + override fun onSuccess(result: Boolean?) { + if (result != null && !result) { + data.postValue(error("", null)) + return } - data.postValue(Resource.success(true)); - if (!ignoreSaveState) media.setHasViewerSaved(!media.getHasViewerSaved()); - saved.postValue(media.getHasViewerSaved()); + data.postValue(success(true)) + if (!ignoreSaveState) media.hasViewerSaved = !media.hasViewerSaved + saved.postValue(media.hasViewerSaved) } - @Override - public void onFailure(final Throwable t) { - data.postValue(Resource.error(t.getMessage(), null)); - Log.e(TAG, "Error during save/unsave", t); + override fun onFailure(t: Throwable) { + data.postValue(error(t.message, null)) + Log.e(TAG, "Error during save/unsave", t) } - }; + } } - public LiveData> updateCaption(final String caption) { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - mediaService.editCaption(media.getPk(), caption, new ServiceCallback() { - @Override - public void onSuccess(final Boolean result) { - if (result) { - data.postValue(Resource.success("")); - media.setPostCaption(caption); - PostViewV2ViewModel.this.caption.postValue(media.getCaption()); - return; + fun updateCaption(caption: String): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + mediaService?.editCaption(media.pk, caption, object : ServiceCallback { + override fun onSuccess(result: Boolean?) { + if (result != null && result) { + data.postValue(success("")) + media.setPostCaption(caption) + this@PostViewV2ViewModel.caption.postValue(media.caption) + return } - data.postValue(Resource.error("", null)); + data.postValue(error("", null)) } - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error editing caption", t); - data.postValue(Resource.error(t.getMessage(), null)); + override fun onFailure(t: Throwable) { + Log.e(TAG, "Error editing caption", t) + data.postValue(error(t.message, null)) } - }); - return data; + }) + return data } - public LiveData> translateCaption() { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - final Caption value = caption.getValue(); - if (value == null) return data; - mediaService.translate(value.getPk(), "1", new ServiceCallback() { - @Override - public void onSuccess(final String result) { - if (TextUtils.isEmpty(result)) { - data.postValue(Resource.error("", null)); - return; + fun translateCaption(): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + val value = caption.value ?: return data + mediaService?.translate(value.pk, "1", object : ServiceCallback { + override fun onSuccess(result: String?) { + if (result.isNullOrBlank()) { + data.postValue(error("", null)) + return } - data.postValue(Resource.success(result)); + data.postValue(success(result)) } - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error translating comment", t); - data.postValue(Resource.error(t.getMessage(), null)); + override fun onFailure(t: Throwable) { + Log.e(TAG, "Error translating comment", t) + data.postValue(error(t.message, null)) } - }); - return data; + }) + return data } - public boolean hasPk() { - return media.getPk() != null; + fun hasPk(): Boolean { + return media.pk != null } - public void setViewCount(final Long viewCount) { - this.viewCount.postValue(viewCount); + fun setViewCount(viewCount: Long?) { + this.viewCount.postValue(viewCount) } - public LiveData> delete() { - final MutableLiveData> data = new MutableLiveData<>(); - data.postValue(Resource.loading(null)); - final Call request = mediaService.delete(media.getId(), media.getMediaType()); + fun delete(): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + val mediaId = media.id + val mediaType = media.mediaType + if (mediaId == null || mediaType == null) { + data.postValue(error("media id or type is null", null)) + return data + } + val request = mediaService?.delete(mediaId, mediaType) if (request == null) { - data.postValue(Resource.success(new Object())); - return data; + data.postValue(success(Any())) + return data } - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - if (!response.isSuccessful()) { - data.postValue(Resource.error(R.string.generic_null_response, null)); - return; + request.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (!response.isSuccessful) { + data.postValue(error(R.string.generic_null_response, null)) + return } - final String body = response.body(); + val body = response.body() if (body == null) { - data.postValue(Resource.error(R.string.generic_null_response, null)); - return; + data.postValue(error(R.string.generic_null_response, null)) + return } - data.postValue(Resource.success(new Object())); + data.postValue(success(Any())) } - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Log.e(TAG, "onFailure: ", t); - data.postValue(Resource.error(t.getMessage(), null)); + override fun onFailure(call: Call, t: Throwable) { + Log.e(TAG, "onFailure: ", t) + data.postValue(error(t.message, null)) } - }); - return data; + }) + return data } - public void shareDm(@NonNull final RankedRecipient result) { + fun shareDm(result: RankedRecipient) { if (messageManager == null) { - messageManager = DirectMessagesManager.INSTANCE; + messageManager = DirectMessagesManager } - messageManager.sendMedia(result, media.getId()); + val mediaId = media.id ?: return + messageManager?.sendMedia(result, mediaId, viewModelScope) } - public void shareDm(@NonNull final Set recipients) { + fun shareDm(recipients: Set) { if (messageManager == null) { - messageManager = DirectMessagesManager.INSTANCE; + messageManager = DirectMessagesManager } - messageManager.sendMedia(recipients, media.getId()); + val mediaId = media.id ?: return + messageManager?.sendMedia(recipients, mediaId, viewModelScope) } -} + + init { + val cookie = Utils.settingsHelper.getString(Constants.COOKIE) + val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID) + val csrfToken: String? = getCsrfTokenFromCookie(cookie) + viewerId = getUserIdFromCookie(cookie) + isLoggedIn = cookie.isNotBlank() && viewerId != 0L + if (!csrfToken.isNullOrBlank()) { + mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt index f877ed99..9b0dde7f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt +++ b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt @@ -18,10 +18,10 @@ class DirectMessagesService private constructor( ) : BaseService() { private val repository: DirectMessagesRepository = RetrofitFactory.retrofit.create(DirectMessagesRepository::class.java) - fun fetchInbox( + suspend fun fetchInbox( cursor: String?, seqId: Long, - ): Call { + ): DirectInboxResponse { val queryMap = mutableMapOf( "visual_message_return_type" to "unseen", "thread_message_limit" to 10.toString(), @@ -38,10 +38,10 @@ class DirectMessagesService private constructor( return repository.fetchInbox(queryMap) } - fun fetchThread( + suspend fun fetchThread( threadId: String, cursor: String?, - ): Call { + ): DirectThreadFeedResponse { val queryMap = mutableMapOf( "visual_message_return_type" to "unseen", "limit" to 20.toString(), @@ -409,7 +409,7 @@ class DirectMessagesService private constructor( return repository.end(threadId, form) } - fun fetchPendingInbox(cursor: String?, seqId: Long): Call { + suspend fun fetchPendingInbox(cursor: String?, seqId: Long): DirectInboxResponse { val queryMap = mutableMapOf( "visual_message_return_type" to "unseen", "thread_message_limit" to 20.toString(),