Ammar Githam
4 years ago
63 changed files with 3548 additions and 1995 deletions
-
6app/build.gradle
-
7app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
2app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
4app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java
-
16app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectInboxItemViewHolder.java
-
7app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
33app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
94app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
184app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
32app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.java
-
81app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.java
-
358app/src/main/java/awais/instagrabber/managers/InboxManager.java
-
1780app/src/main/java/awais/instagrabber/managers/ThreadManager.java
-
17app/src/main/java/awais/instagrabber/models/Resource.java
-
19app/src/main/java/awais/instagrabber/repositories/responses/AnimatedMediaFixedHeight.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/AnimatedMediaImages.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/Audio.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/Caption.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java
-
17app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java
-
19app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java
-
24app/src/main/java/awais/instagrabber/repositories/responses/FriendshipStatus.java
-
14app/src/main/java/awais/instagrabber/repositories/responses/ImageVersions2.java
-
20app/src/main/java/awais/instagrabber/repositories/responses/Location.java
-
49app/src/main/java/awais/instagrabber/repositories/responses/Media.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/MediaCandidate.java
-
31app/src/main/java/awais/instagrabber/repositories/responses/User.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/UsertagIn.java
-
14app/src/main/java/awais/instagrabber/repositories/responses/Usertags.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/VideoVersion.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectInbox.java
-
61app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItem.java
-
32app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemAnimatedMedia.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemClip.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemFelixShare.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemLink.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemLinkContext.java
-
17app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemPlaceholder.java
-
22app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReelShare.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReelShareReactionInfo.java
-
21app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemStoryShare.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVideoCallEvent.java
-
22app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVisualMedia.java
-
17app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVoiceMedia.java
-
75app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadDirectStory.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadLastSeenAt.java
-
6app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadParticipantRequestsResponse.java
-
17app/src/main/java/awais/instagrabber/repositories/responses/directmessages/RavenExpiringMediaActionSummary.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/directmessages/ThreadContext.java
-
2app/src/main/java/awais/instagrabber/utils/BitmapUtils.java
-
47app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
8app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java
-
166app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.java
-
119app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.java
-
650app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java
-
1057app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
35app/src/main/java/awais/instagrabber/viewmodels/factories/DirectSettingsViewModelFactory.java
-
35app/src/main/java/awais/instagrabber/viewmodels/factories/DirectThreadViewModelFactory.java
-
10app/src/main/res/layout/fragment_direct_pending_inbox.xml
-
1app/src/main/res/values/strings.xml
-
2build.gradle
@ -0,0 +1,81 @@ |
|||
package awais.instagrabber.managers; |
|||
|
|||
import android.content.ContentResolver; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import com.google.common.collect.Iterables; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.repositories.responses.User; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectItem; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
|
|||
public final class DirectMessagesManager { |
|||
private static final String TAG = DirectMessagesManager.class.getSimpleName(); |
|||
private static final Object LOCK = new Object(); |
|||
|
|||
private static DirectMessagesManager instance; |
|||
|
|||
private final InboxManager inboxManager; |
|||
private final InboxManager pendingInboxManager; |
|||
|
|||
public static DirectMessagesManager getInstance() { |
|||
if (instance == null) { |
|||
synchronized (LOCK) { |
|||
if (instance == null) { |
|||
instance = new DirectMessagesManager(); |
|||
} |
|||
} |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
private DirectMessagesManager() { |
|||
inboxManager = InboxManager.getInstance(false); |
|||
pendingInboxManager = InboxManager.getInstance(true); |
|||
} |
|||
|
|||
public void moveThreadFromPending(@NonNull final String threadId) { |
|||
final List<DirectThread> pendingThreads = pendingInboxManager.getThreads().getValue(); |
|||
if (pendingThreads == null) return; |
|||
final int index = Iterables.indexOf(pendingThreads, t -> t.getThreadId().equals(threadId)); |
|||
if (index < 0) return; |
|||
final DirectThread thread = pendingThreads.get(index); |
|||
final DirectItem threadFirstDirectItem = thread.getFirstDirectItem(); |
|||
if (threadFirstDirectItem == null) return; |
|||
final List<DirectThread> threads = inboxManager.getThreads().getValue(); |
|||
int insertIndex = 0; |
|||
for (final DirectThread tempThread : threads) { |
|||
final DirectItem firstDirectItem = tempThread.getFirstDirectItem(); |
|||
if (firstDirectItem == null) continue; |
|||
final long timestamp = firstDirectItem.getTimestamp(); |
|||
if (timestamp < threadFirstDirectItem.getTimestamp()) { |
|||
break; |
|||
} |
|||
insertIndex++; |
|||
} |
|||
thread.setPending(false); |
|||
inboxManager.addThread(thread, insertIndex); |
|||
pendingInboxManager.removeThread(threadId); |
|||
final Integer currentTotal = inboxManager.getPendingRequestsTotal().getValue(); |
|||
if (currentTotal == null) return; |
|||
inboxManager.setPendingRequestsTotal(currentTotal - 1); |
|||
} |
|||
|
|||
public InboxManager getInboxManager() { |
|||
return inboxManager; |
|||
} |
|||
|
|||
public InboxManager getPendingInboxManager() { |
|||
return pendingInboxManager; |
|||
} |
|||
|
|||
public ThreadManager getThreadManager(@NonNull final String threadId, |
|||
final boolean pending, |
|||
@NonNull final User currentUser, |
|||
@NonNull final ContentResolver contentResolver) { |
|||
return ThreadManager.getInstance(threadId, pending, currentUser, contentResolver); |
|||
} |
|||
} |
@ -0,0 +1,358 @@ |
|||
package awais.instagrabber.managers; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.Transformations; |
|||
|
|||
import com.google.common.cache.CacheBuilder; |
|||
import com.google.common.cache.CacheLoader; |
|||
import com.google.common.cache.LoadingCache; |
|||
import com.google.common.collect.ImmutableList; |
|||
import com.google.common.collect.Iterables; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.Collections; |
|||
import java.util.Comparator; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import awais.instagrabber.models.Resource; |
|||
import awais.instagrabber.repositories.responses.User; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectItem; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.webservices.DirectMessagesService; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
|
|||
import static androidx.lifecycle.Transformations.distinctUntilChanged; |
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public final class InboxManager { |
|||
private static final String TAG = InboxManager.class.getSimpleName(); |
|||
private static final LoadingCache<String, Object> THREAD_LOCKS = CacheBuilder |
|||
.newBuilder() |
|||
.expireAfterAccess(1, TimeUnit.MINUTES) // max lock time ever expected |
|||
.build(CacheLoader.from(Object::new)); |
|||
private static final Comparator<DirectThread> THREAD_COMPARATOR = (t1, t2) -> { |
|||
final DirectItem t1FirstDirectItem = t1.getFirstDirectItem(); |
|||
final DirectItem t2FirstDirectItem = t2.getFirstDirectItem(); |
|||
if (t1FirstDirectItem == null && t2FirstDirectItem == null) return 0; |
|||
if (t1FirstDirectItem == null) return 1; |
|||
if (t2FirstDirectItem == null) return -1; |
|||
return Long.compare(t2FirstDirectItem.getTimestamp(), t1FirstDirectItem.getTimestamp()); |
|||
}; |
|||
|
|||
private final MutableLiveData<Resource<DirectInbox>> inbox = new MutableLiveData<>(); |
|||
private final MutableLiveData<Resource<Integer>> unseenCount = new MutableLiveData<>(); |
|||
private final MutableLiveData<Integer> pendingRequestsTotal = new MutableLiveData<>(0); |
|||
|
|||
private final LiveData<List<DirectThread>> threads; |
|||
private final DirectMessagesService service; |
|||
private final boolean pending; |
|||
|
|||
private Call<DirectInboxResponse> inboxRequest; |
|||
private Call<DirectBadgeCount> unseenCountRequest; |
|||
private long seqId; |
|||
private String cursor; |
|||
private boolean hasOlder = true; |
|||
private User viewer; |
|||
|
|||
@NonNull |
|||
public static InboxManager getInstance(final boolean pending) { |
|||
return new InboxManager(pending); |
|||
} |
|||
|
|||
private InboxManager(final boolean pending) { |
|||
this.pending = pending; |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
final long userId = CookieUtils.getUserIdFromCookie(cookie); |
|||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); |
|||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); |
|||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) { |
|||
throw new IllegalArgumentException("User is not logged in!"); |
|||
} |
|||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); |
|||
|
|||
// Transformations |
|||
threads = distinctUntilChanged(Transformations.map(inbox, inboxResource -> { |
|||
if (inboxResource == null) { |
|||
return Collections.emptyList(); |
|||
} |
|||
final DirectInbox inbox = inboxResource.data; |
|||
if (inbox == null) { |
|||
return Collections.emptyList(); |
|||
} |
|||
return ImmutableList.sortedCopyOf(THREAD_COMPARATOR, inbox.getThreads()); |
|||
})); |
|||
|
|||
fetchInbox(); |
|||
if (!pending) { |
|||
fetchUnseenCount(); |
|||
} |
|||
} |
|||
|
|||
public LiveData<Resource<DirectInbox>> getInbox() { |
|||
return distinctUntilChanged(inbox); |
|||
} |
|||
|
|||
public LiveData<List<DirectThread>> getThreads() { |
|||
return threads; |
|||
} |
|||
|
|||
public LiveData<Resource<Integer>> getUnseenCount() { |
|||
return distinctUntilChanged(unseenCount); |
|||
} |
|||
|
|||
public LiveData<Integer> getPendingRequestsTotal() { |
|||
return distinctUntilChanged(pendingRequestsTotal); |
|||
} |
|||
|
|||
public User getViewer() { |
|||
return viewer; |
|||
} |
|||
|
|||
public void fetchInbox() { |
|||
final Resource<DirectInbox> inboxResource = inbox.getValue(); |
|||
if ((inboxResource != null && inboxResource.status == Resource.Status.LOADING) || !hasOlder) return; |
|||
stopCurrentInboxRequest(); |
|||
inbox.postValue(Resource.loading(getCurrentDirectInbox())); |
|||
inboxRequest = pending ? service.fetchPendingInbox(cursor, seqId) : service.fetchInbox(cursor, seqId); |
|||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) { |
|||
parseInboxResponse(response.body()); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching dm inbox", t); |
|||
inbox.postValue(Resource.error(t.getMessage(), getCurrentDirectInbox())); |
|||
hasOlder = false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchUnseenCount() { |
|||
final Resource<Integer> unseenCountResource = unseenCount.getValue(); |
|||
if ((unseenCountResource != null && unseenCountResource.status == Resource.Status.LOADING)) return; |
|||
stopCurrentUnseenCountRequest(); |
|||
unseenCount.postValue(Resource.loading(getCurrentUnseenCount())); |
|||
unseenCountRequest = service.fetchUnseenCount(); |
|||
unseenCountRequest.enqueue(new Callback<DirectBadgeCount>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectBadgeCount> call, @NonNull final Response<DirectBadgeCount> response) { |
|||
final DirectBadgeCount directBadgeCount = response.body(); |
|||
if (directBadgeCount == null) { |
|||
Log.e(TAG, "onResponse: directBadgeCount Response is null"); |
|||
unseenCount.postValue(Resource.error("Unseen count response is null", getCurrentUnseenCount())); |
|||
return; |
|||
} |
|||
unseenCount.postValue(Resource.success(directBadgeCount.getBadgeCount())); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectBadgeCount> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching unseen count", t); |
|||
unseenCount.postValue(Resource.error(t.getMessage(), getCurrentUnseenCount())); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void refresh() { |
|||
cursor = null; |
|||
seqId = 0; |
|||
hasOlder = true; |
|||
fetchInbox(); |
|||
if (!pending) { |
|||
fetchUnseenCount(); |
|||
} |
|||
} |
|||
|
|||
private DirectInbox getCurrentDirectInbox() { |
|||
final Resource<DirectInbox> inboxResource = inbox.getValue(); |
|||
return inboxResource != null ? inboxResource.data : null; |
|||
} |
|||
|
|||
private void parseInboxResponse(final DirectInboxResponse response) { |
|||
if (response == null) { |
|||
Log.e(TAG, "parseInboxResponse: Response is null"); |
|||
inbox.postValue(Resource.error("Response is null", getCurrentDirectInbox())); |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
if (!response.getStatus().equals("ok")) { |
|||
final String msg = "DM inbox fetch response: status not ok"; |
|||
Log.e(TAG, msg); |
|||
inbox.postValue(Resource.error(msg, getCurrentDirectInbox())); |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
seqId = response.getSeqId(); |
|||
if (viewer == null) { |
|||
viewer = response.getViewer(); |
|||
} |
|||
final DirectInbox inbox = response.getInbox(); |
|||
if (!TextUtils.isEmpty(cursor)) { |
|||
final DirectInbox currentDirectInbox = getCurrentDirectInbox(); |
|||
if (currentDirectInbox != null) { |
|||
List<DirectThread> threads = currentDirectInbox.getThreads(); |
|||
threads = threads == null ? new LinkedList<>() : new LinkedList<>(threads); |
|||
threads.addAll(inbox.getThreads()); |
|||
inbox.setThreads(threads); |
|||
} |
|||
} |
|||
this.inbox.postValue(Resource.success(inbox)); |
|||
cursor = inbox.getOldestCursor(); |
|||
hasOlder = inbox.hasOlder(); |
|||
pendingRequestsTotal.postValue(response.getPendingRequestsTotal()); |
|||
} |
|||
|
|||
public void setThread(@NonNull final String threadId, |
|||
@NonNull final DirectThread thread) { |
|||
final DirectInbox inbox = getCurrentDirectInbox(); |
|||
if (inbox == null) return; |
|||
final int index = getThreadIndex(threadId, inbox); |
|||
setThread(inbox, index, thread); |
|||
} |
|||
|
|||
private void setThread(@NonNull final DirectInbox inbox, |
|||
final int index, |
|||
@NonNull final DirectThread thread) { |
|||
if (index < 0) return; |
|||
synchronized (this.inbox) { |
|||
final List<DirectThread> threadsCopy = new LinkedList<>(inbox.getThreads()); |
|||
threadsCopy.set(index, thread); |
|||
try { |
|||
final DirectInbox clone = (DirectInbox) inbox.clone(); |
|||
clone.setThreads(threadsCopy); |
|||
this.inbox.postValue(Resource.success(clone)); |
|||
} catch (CloneNotSupportedException e) { |
|||
Log.e(TAG, "setThread: ", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void addItemsToThread(@NonNull final String threadId, |
|||
final int insertIndex, |
|||
@NonNull final Collection<DirectItem> items) { |
|||
final DirectInbox inbox = getCurrentDirectInbox(); |
|||
if (inbox == null) return; |
|||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) { |
|||
final int index = getThreadIndex(threadId, inbox); |
|||
if (index < 0) return; |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
final DirectThread thread = threads.get(index); |
|||
List<DirectItem> list = thread.getItems(); |
|||
list = list == null ? new LinkedList<>() : new LinkedList<>(list); |
|||
if (insertIndex >= 0) { |
|||
list.addAll(insertIndex, items); |
|||
} else { |
|||
list.addAll(items); |
|||
} |
|||
try { |
|||
final DirectThread threadClone = (DirectThread) thread.clone(); |
|||
threadClone.setItems(list); |
|||
setThread(inbox, index, threadClone); |
|||
} catch (Exception e) { |
|||
Log.e(TAG, "addItemsToThread: ", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void setItemsToThread(@NonNull final String threadId, |
|||
@NonNull final List<DirectItem> updatedItems) { |
|||
final DirectInbox inbox = getCurrentDirectInbox(); |
|||
if (inbox == null) return; |
|||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) { |
|||
final int index = getThreadIndex(threadId, inbox); |
|||
if (index < 0) return; |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
final DirectThread thread = threads.get(index); |
|||
thread.setItems(updatedItems); |
|||
setThread(inbox, index, thread); |
|||
} |
|||
} |
|||
|
|||
private int getThreadIndex(@NonNull final String threadId, |
|||
@NonNull final DirectInbox inbox) { |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
if (threads == null || threads.isEmpty()) { |
|||
return -1; |
|||
} |
|||
return Iterables.indexOf(threads, t -> { |
|||
if (t == null) return false; |
|||
return t.getThreadId().equals(threadId); |
|||
}); |
|||
} |
|||
|
|||
private Integer getCurrentUnseenCount() { |
|||
final Resource<Integer> unseenCountResource = unseenCount.getValue(); |
|||
return unseenCountResource != null ? unseenCountResource.data : null; |
|||
} |
|||
|
|||
private void stopCurrentInboxRequest() { |
|||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return; |
|||
inboxRequest.cancel(); |
|||
inboxRequest = null; |
|||
} |
|||
|
|||
private void stopCurrentUnseenCountRequest() { |
|||
if (unseenCountRequest == null || unseenCountRequest.isCanceled() || unseenCountRequest.isExecuted()) return; |
|||
unseenCountRequest.cancel(); |
|||
unseenCountRequest = null; |
|||
} |
|||
|
|||
public void onDestroy() { |
|||
stopCurrentInboxRequest(); |
|||
stopCurrentUnseenCountRequest(); |
|||
} |
|||
|
|||
public void addThread(@NonNull final DirectThread thread, final int insertIndex) { |
|||
if (insertIndex < 0) return; |
|||
synchronized (this.inbox) { |
|||
final DirectInbox currentDirectInbox = getCurrentDirectInbox(); |
|||
final List<DirectThread> threadsCopy = new LinkedList<>(currentDirectInbox.getThreads()); |
|||
threadsCopy.add(insertIndex, thread); |
|||
try { |
|||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone(); |
|||
clone.setThreads(threadsCopy); |
|||
this.inbox.setValue(Resource.success(clone)); |
|||
} catch (CloneNotSupportedException e) { |
|||
Log.e(TAG, "setThread: ", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void removeThread(@NonNull final String threadId) { |
|||
synchronized (this.inbox) { |
|||
final DirectInbox currentDirectInbox = getCurrentDirectInbox(); |
|||
final List<DirectThread> threadsCopy = currentDirectInbox.getThreads() |
|||
.stream() |
|||
.filter(t -> !t.getThreadId().equals(threadId)) |
|||
.collect(Collectors.toList()); |
|||
try { |
|||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone(); |
|||
clone.setThreads(threadsCopy); |
|||
this.inbox.postValue(Resource.success(clone)); |
|||
} catch (CloneNotSupportedException e) { |
|||
Log.e(TAG, "setThread: ", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void setPendingRequestsTotal(final int total) { |
|||
pendingRequestsTotal.postValue(total); |
|||
} |
|||
} |
1780
app/src/main/java/awais/instagrabber/managers/ThreadManager.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,188 +1,56 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.LinkedList; |
|||
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.DirectBadgeCount; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.webservices.DirectMessagesService; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class DirectInboxViewModel extends ViewModel { |
|||
private static final String TAG = DirectInboxViewModel.class.getSimpleName(); |
|||
|
|||
private final DirectMessagesService service; |
|||
private final MutableLiveData<Boolean> fetchingInbox = new MutableLiveData<>(false); |
|||
private final MutableLiveData<List<DirectThread>> threads = new MutableLiveData<>(); |
|||
private final MutableLiveData<Boolean> fetchingUnseenCount = new MutableLiveData<>(false); |
|||
private final MutableLiveData<Integer> unseenCount = new MutableLiveData<>(0); |
|||
private final MutableLiveData<Integer> pendingRequestsTotal = new MutableLiveData<>(0); |
|||
|
|||
private Call<DirectInboxResponse> inboxRequest; |
|||
private Call<DirectBadgeCount> unseenCountRequest; |
|||
private long seqId; |
|||
private String cursor; |
|||
private boolean hasOlder = true; |
|||
private User viewer; |
|||
private final InboxManager inboxManager; |
|||
|
|||
public DirectInboxViewModel() { |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
final long userId = CookieUtils.getUserIdFromCookie(cookie); |
|||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); |
|||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); |
|||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) { |
|||
throw new IllegalArgumentException("User is not logged in!"); |
|||
} |
|||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); |
|||
fetchInbox(); |
|||
fetchUnseenCount(); |
|||
} |
|||
|
|||
public LiveData<List<DirectThread>> getThreads() { |
|||
return threads; |
|||
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance(); |
|||
inboxManager = messagesManager.getInboxManager(); |
|||
} |
|||
|
|||
public void setThreads(final List<DirectThread> threads) { |
|||
this.threads.postValue(threads); |
|||
public LiveData<Resource<DirectInbox>> getInbox() { |
|||
return inboxManager.getInbox(); |
|||
} |
|||
|
|||
public void addThreads(final Collection<DirectThread> threads) { |
|||
if (threads == null) return; |
|||
List<DirectThread> list = getThreads().getValue(); |
|||
list = list == null ? new LinkedList<>() : new LinkedList<>(list); |
|||
list.addAll(threads); |
|||
this.threads.postValue(list); |
|||
} |
|||
|
|||
public LiveData<Integer> getUnseenCount() { |
|||
return unseenCount; |
|||
public LiveData<List<DirectThread>> getThreads() { |
|||
return inboxManager.getThreads(); |
|||
} |
|||
|
|||
public LiveData<Boolean> getFetchingInbox() { |
|||
return fetchingInbox; |
|||
public LiveData<Resource<Integer>> getUnseenCount() { |
|||
return inboxManager.getUnseenCount(); |
|||
} |
|||
|
|||
public LiveData<Integer> getPendingRequestsTotal() { |
|||
return pendingRequestsTotal; |
|||
return inboxManager.getPendingRequestsTotal(); |
|||
} |
|||
|
|||
public User getViewer() { |
|||
return viewer; |
|||
return inboxManager.getViewer(); |
|||
} |
|||
|
|||
public void fetchInbox() { |
|||
if ((fetchingInbox.getValue() != null && fetchingInbox.getValue()) || !hasOlder) return; |
|||
stopCurrentInboxRequest(); |
|||
fetchingInbox.postValue(true); |
|||
inboxRequest = service.fetchInbox(cursor, seqId); |
|||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) { |
|||
parseInboxResponse(response.body()); |
|||
fetchingInbox.postValue(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching dm inbox", t); |
|||
fetchingInbox.postValue(false); |
|||
hasOlder = false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private void parseInboxResponse(final DirectInboxResponse response) { |
|||
if (response == null) { |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
if (!response.getStatus().equals("ok")) { |
|||
Log.e(TAG, "DM inbox fetch response: status not ok"); |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
seqId = response.getSeqId(); |
|||
if (viewer == null) { |
|||
viewer = response.getViewer(); |
|||
} |
|||
final DirectInbox inbox = response.getInbox(); |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
if (!TextUtils.isEmpty(cursor)) { |
|||
addThreads(threads); |
|||
} else { |
|||
setThreads(threads); |
|||
} |
|||
cursor = inbox.getOldestCursor(); |
|||
hasOlder = inbox.hasOlder(); |
|||
pendingRequestsTotal.postValue(response.getPendingRequestsTotal()); |
|||
// unseenCount.postValue(inbox.getUnseenCount()); |
|||
} |
|||
|
|||
private void stopCurrentInboxRequest() { |
|||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return; |
|||
inboxRequest.cancel(); |
|||
inboxRequest = null; |
|||
} |
|||
|
|||
public void fetchUnseenCount() { |
|||
if ((fetchingUnseenCount.getValue() != null && fetchingUnseenCount.getValue())) return; |
|||
stopCurrentUnseenCountRequest(); |
|||
fetchingUnseenCount.postValue(true); |
|||
unseenCountRequest = service.fetchUnseenCount(); |
|||
unseenCountRequest.enqueue(new Callback<DirectBadgeCount>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectBadgeCount> call, @NonNull final Response<DirectBadgeCount> response) { |
|||
parseUnseenCountResponse(response.body()); |
|||
fetchingUnseenCount.postValue(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectBadgeCount> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching unseen count", t); |
|||
fetchingUnseenCount.postValue(false); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private void parseUnseenCountResponse(final DirectBadgeCount directBadgeCount) { |
|||
if (directBadgeCount == null) return; |
|||
unseenCount.postValue(directBadgeCount.getBadgeCount()); |
|||
} |
|||
|
|||
private void stopCurrentUnseenCountRequest() { |
|||
if (unseenCountRequest == null || unseenCountRequest.isCanceled() || unseenCountRequest.isExecuted()) return; |
|||
unseenCountRequest.cancel(); |
|||
unseenCountRequest = null; |
|||
inboxManager.fetchInbox(); |
|||
} |
|||
|
|||
public void refresh() { |
|||
cursor = null; |
|||
seqId = 0; |
|||
hasOlder = true; |
|||
fetchInbox(); |
|||
fetchUnseenCount(); |
|||
inboxManager.refresh(); |
|||
} |
|||
|
|||
public void onDestroy() { |
|||
stopCurrentInboxRequest(); |
|||
// getThreads().postValue(Collections.emptyList()); |
|||
inboxManager.onDestroy(); |
|||
} |
|||
} |
@ -1,141 +1,48 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.LinkedList; |
|||
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.DirectBadgeCount; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.webservices.DirectMessagesService; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class DirectPendingInboxViewModel extends ViewModel { |
|||
private static final String TAG = DirectPendingInboxViewModel.class.getSimpleName(); |
|||
|
|||
private final DirectMessagesService service; |
|||
private final MutableLiveData<Boolean> fetchingInbox = new MutableLiveData<>(false); |
|||
private final MutableLiveData<List<DirectThread>> threads = new MutableLiveData<>(); |
|||
|
|||
private Call<DirectInboxResponse> inboxRequest; |
|||
private Call<DirectBadgeCount> unseenCountRequest; |
|||
private long seqId; |
|||
private String cursor; |
|||
private boolean hasOlder = true; |
|||
private User viewer; |
|||
private final InboxManager inboxManager; |
|||
|
|||
public DirectPendingInboxViewModel() { |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
final long userId = CookieUtils.getUserIdFromCookie(cookie); |
|||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); |
|||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); |
|||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) { |
|||
throw new IllegalArgumentException("User is not logged in!"); |
|||
} |
|||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); |
|||
fetchInbox(); |
|||
inboxManager = DirectMessagesManager.getInstance().getPendingInboxManager(); |
|||
inboxManager.fetchInbox(); |
|||
} |
|||
|
|||
public LiveData<List<DirectThread>> getThreads() { |
|||
return threads; |
|||
} |
|||
|
|||
public void setThreads(final List<DirectThread> threads) { |
|||
this.threads.postValue(threads); |
|||
} |
|||
|
|||
public void addThreads(final Collection<DirectThread> threads) { |
|||
if (threads == null) return; |
|||
List<DirectThread> list = getThreads().getValue(); |
|||
list = list == null ? new LinkedList<>() : new LinkedList<>(list); |
|||
list.addAll(threads); |
|||
this.threads.postValue(list); |
|||
return inboxManager.getThreads(); |
|||
} |
|||
|
|||
public LiveData<Boolean> getFetchingInbox() { |
|||
return fetchingInbox; |
|||
public LiveData<Resource<DirectInbox>> getInbox() { |
|||
return inboxManager.getInbox(); |
|||
} |
|||
|
|||
public User getViewer() { |
|||
return viewer; |
|||
return inboxManager.getViewer(); |
|||
} |
|||
|
|||
public void fetchInbox() { |
|||
if ((fetchingInbox.getValue() != null && fetchingInbox.getValue()) || !hasOlder) return; |
|||
stopCurrentInboxRequest(); |
|||
fetchingInbox.postValue(true); |
|||
inboxRequest = service.fetchPendingInbox(cursor, seqId); |
|||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) { |
|||
parseInboxResponse(response.body()); |
|||
fetchingInbox.postValue(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching pending inbox", t); |
|||
fetchingInbox.postValue(false); |
|||
hasOlder = false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private void parseInboxResponse(final DirectInboxResponse response) { |
|||
if (response == null) { |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
if (!response.getStatus().equals("ok")) { |
|||
Log.e(TAG, "DM pending inbox fetch response: status not ok"); |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
seqId = response.getSeqId(); |
|||
if (viewer == null) { |
|||
viewer = response.getViewer(); |
|||
} |
|||
final DirectInbox inbox = response.getInbox(); |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
if (!TextUtils.isEmpty(cursor)) { |
|||
addThreads(threads); |
|||
} else { |
|||
setThreads(threads); |
|||
} |
|||
cursor = inbox.getOldestCursor(); |
|||
hasOlder = inbox.hasOlder(); |
|||
} |
|||
|
|||
private void stopCurrentInboxRequest() { |
|||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return; |
|||
inboxRequest.cancel(); |
|||
inboxRequest = null; |
|||
inboxManager.fetchInbox(); |
|||
} |
|||
|
|||
public void refresh() { |
|||
cursor = null; |
|||
seqId = 0; |
|||
hasOlder = true; |
|||
fetchInbox(); |
|||
inboxManager.refresh(); |
|||
} |
|||
|
|||
public void onDestroy() { |
|||
stopCurrentInboxRequest(); |
|||
inboxManager.onDestroy(); |
|||
} |
|||
} |
1057
app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,35 @@ |
|||
package awais.instagrabber.viewmodels.factories; |
|||
|
|||
import android.app.Application; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.ViewModel; |
|||
import androidx.lifecycle.ViewModelProvider; |
|||
|
|||
import awais.instagrabber.repositories.responses.User; |
|||
import awais.instagrabber.viewmodels.DirectSettingsViewModel; |
|||
|
|||
public class DirectSettingsViewModelFactory implements ViewModelProvider.Factory { |
|||
|
|||
private final Application application; |
|||
private final String threadId; |
|||
private final boolean pending; |
|||
private final User currentUser; |
|||
|
|||
public DirectSettingsViewModelFactory(@NonNull final Application application, |
|||
@NonNull final String threadId, |
|||
final boolean pending, |
|||
@NonNull final User currentUser) { |
|||
this.application = application; |
|||
this.threadId = threadId; |
|||
this.pending = pending; |
|||
this.currentUser = currentUser; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) { |
|||
//noinspection unchecked |
|||
return (T) new DirectSettingsViewModel(application, threadId, pending, currentUser); |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
package awais.instagrabber.viewmodels.factories; |
|||
|
|||
import android.app.Application; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.ViewModel; |
|||
import androidx.lifecycle.ViewModelProvider; |
|||
|
|||
import awais.instagrabber.repositories.responses.User; |
|||
import awais.instagrabber.viewmodels.DirectThreadViewModel; |
|||
|
|||
public class DirectThreadViewModelFactory implements ViewModelProvider.Factory { |
|||
|
|||
private final Application application; |
|||
private final String threadId; |
|||
private final boolean pending; |
|||
private final User currentUser; |
|||
|
|||
public DirectThreadViewModelFactory(@NonNull final Application application, |
|||
@NonNull final String threadId, |
|||
final boolean pending, |
|||
@NonNull final User currentUser) { |
|||
this.application = application; |
|||
this.threadId = threadId; |
|||
this.pending = pending; |
|||
this.currentUser = currentUser; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) { |
|||
//noinspection unchecked |
|||
return (T) new DirectThreadViewModel(application, threadId, pending, currentUser); |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue