Transition conversation loading from a Loader to a Repository.
This commit is contained in:
parent
2b65916344
commit
e1a90bcb00
5 changed files with 348 additions and 200 deletions
|
@ -0,0 +1,82 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public final class ConversationData {
|
||||
private final Cursor cursor;
|
||||
private final int offset;
|
||||
private final int limit;
|
||||
private final long lastSeen;
|
||||
private final int previousOffset;
|
||||
private final boolean firstLoad;
|
||||
private final boolean hasSent;
|
||||
private final boolean isMessageRequestAccepted;
|
||||
private final boolean hasPreMessageRequestMessages;
|
||||
|
||||
public ConversationData(Cursor cursor,
|
||||
int offset,
|
||||
int limit,
|
||||
long lastSeen,
|
||||
int previousOffset,
|
||||
boolean firstLoad,
|
||||
boolean hasSent,
|
||||
boolean isMessageRequestAccepted,
|
||||
boolean hasPreMessageRequestMessages)
|
||||
{
|
||||
this.cursor = cursor;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.lastSeen = lastSeen;
|
||||
this.previousOffset = previousOffset;
|
||||
this.firstLoad = firstLoad;
|
||||
this.hasSent = hasSent;
|
||||
this.isMessageRequestAccepted = isMessageRequestAccepted;
|
||||
this.hasPreMessageRequestMessages = hasPreMessageRequestMessages;
|
||||
}
|
||||
|
||||
public @NonNull Cursor getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public boolean hasLimit() {
|
||||
return limit > 0;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public boolean hasOffset() {
|
||||
return offset > 0;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public int getPreviousOffset() {
|
||||
return previousOffset;
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public boolean isFirstLoad() {
|
||||
return firstLoad;
|
||||
}
|
||||
|
||||
public boolean hasSent() {
|
||||
return hasSent;
|
||||
}
|
||||
|
||||
public boolean isMessageRequestAccepted() {
|
||||
return isMessageRequestAccepted;
|
||||
}
|
||||
|
||||
public boolean hasPreMessageRequestMessages() {
|
||||
return hasPreMessageRequestMessages;
|
||||
}
|
||||
}
|
|
@ -51,6 +51,7 @@ import androidx.core.app.ActivityCompat;
|
|||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
|
@ -81,7 +82,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
|
@ -135,9 +135,7 @@ import java.util.Locale;
|
|||
import java.util.Set;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class ConversationFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
public class ConversationFragment extends Fragment {
|
||||
private static final String TAG = ConversationFragment.class.getSimpleName();
|
||||
private static final String KEY_LIMIT = "limit";
|
||||
|
||||
|
@ -152,11 +150,6 @@ public class ConversationFragment extends Fragment
|
|||
|
||||
private LiveRecipient recipient;
|
||||
private long threadId;
|
||||
private long lastSeen;
|
||||
private int startingPosition;
|
||||
private int previousOffset;
|
||||
private int activeOffset;
|
||||
private boolean firstLoad;
|
||||
private boolean isReacting;
|
||||
private ActionMode actionMode;
|
||||
private Locale locale;
|
||||
|
@ -172,6 +165,7 @@ public class ConversationFragment extends Fragment
|
|||
private ConversationBannerView conversationBanner;
|
||||
private ConversationBannerView emptyConversationBanner;
|
||||
private MessageRequestViewModel messageRequestViewModel;
|
||||
private ConversationViewModel conversationViewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
|
@ -214,6 +208,9 @@ public class ConversationFragment extends Fragment
|
|||
|
||||
setupListLayoutListeners();
|
||||
|
||||
this.conversationViewModel = ViewModelProviders.of(requireActivity(), new ConversationViewModel.Factory()).get(ConversationViewModel.class);
|
||||
conversationViewModel.getConversation().observe(this, this::presentConversation);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -295,16 +292,16 @@ public class ConversationFragment extends Fragment
|
|||
initializeListAdapter();
|
||||
|
||||
if (threadId == -1) {
|
||||
getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
|
||||
conversationViewModel.refreshConversation();
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadList() {
|
||||
getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
|
||||
conversationViewModel.refreshConversation();
|
||||
}
|
||||
|
||||
public void moveToLastSeen() {
|
||||
if (lastSeen <= 0) {
|
||||
if (conversationViewModel.getLastSeen() <= 0) {
|
||||
Log.i(TAG, "No need to move to last seen.");
|
||||
return;
|
||||
}
|
||||
|
@ -314,7 +311,7 @@ public class ConversationFragment extends Fragment
|
|||
return;
|
||||
}
|
||||
|
||||
int position = getListAdapter().findLastSeenPosition(lastSeen);
|
||||
int position = getListAdapter().findLastSeenPosition(conversationViewModel.getLastSeen());
|
||||
scrollToLastSeenPosition(position);
|
||||
}
|
||||
|
||||
|
@ -403,13 +400,17 @@ public class ConversationFragment extends Fragment
|
|||
private void initializeResources() {
|
||||
long oldThreadId = threadId;
|
||||
|
||||
long lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
|
||||
int startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
|
||||
int limit = getArguments() != null ? getArguments().getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT) : PARTIAL_CONVERSATION_LIMIT;
|
||||
|
||||
this.recipient = Recipient.live(getActivity().getIntent().getParcelableExtra(ConversationActivity.RECIPIENT_EXTRA));
|
||||
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
|
||||
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
|
||||
this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);
|
||||
this.firstLoad = true;
|
||||
this.unknownSenderView = new UnknownSenderView(getActivity(), recipient.get(), threadId);
|
||||
|
||||
|
||||
conversationViewModel.onConversationDataAvailable(recipient.get(), threadId, lastSeen, startingPosition, limit);
|
||||
|
||||
OnScrollListener scrollListener = new ConversationScrollListener(getActivity());
|
||||
list.addOnScrollListener(scrollListener);
|
||||
|
||||
|
@ -425,8 +426,7 @@ public class ConversationFragment extends Fragment
|
|||
list.setAdapter(adapter);
|
||||
list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
|
||||
|
||||
setLastSeen(lastSeen);
|
||||
getLoaderManager().restartLoader(0, Bundle.EMPTY, this);
|
||||
setLastSeen(conversationViewModel.getLastSeen());
|
||||
|
||||
emptyConversationBanner.setVisibility(View.GONE);
|
||||
} else if (FeatureFlags.messageRequests() && threadId == -1) {
|
||||
|
@ -436,9 +436,7 @@ public class ConversationFragment extends Fragment
|
|||
|
||||
private void initializeLoadMoreView(ViewSwitcher loadMoreView) {
|
||||
loadMoreView.setOnClickListener(v -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(KEY_LIMIT, 0);
|
||||
getLoaderManager().restartLoader(0, args, ConversationFragment.this);
|
||||
conversationViewModel.onLoadMoreClicked();
|
||||
loadMoreView.showNext();
|
||||
loadMoreView.setOnClickListener(null);
|
||||
});
|
||||
|
@ -565,7 +563,8 @@ public class ConversationFragment extends Fragment
|
|||
}
|
||||
|
||||
public void setLastSeen(long lastSeen) {
|
||||
this.lastSeen = lastSeen;
|
||||
conversationViewModel.onLastSeenChanged(lastSeen);
|
||||
|
||||
if (lastSeenDecoration != null) {
|
||||
list.removeItemDecoration(lastSeenDecoration);
|
||||
}
|
||||
|
@ -827,109 +826,12 @@ public class ConversationFragment extends Fragment
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
Log.i(TAG, "onCreateLoader");
|
||||
|
||||
int limit = args.getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT);
|
||||
int offset = 0;
|
||||
if (limit != 0 && startingPosition >= limit) {
|
||||
offset = Math.max(startingPosition - (limit / 2) + 1, 0);
|
||||
startingPosition -= offset - 1;
|
||||
}
|
||||
|
||||
return new ConversationLoader(getActivity(), threadId, offset, limit, lastSeen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
int count = cursor.getCount();
|
||||
ConversationLoader loader = (ConversationLoader) cursorLoader;
|
||||
|
||||
ConversationAdapter adapter = getListAdapter();
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) {
|
||||
adapter.setFooterView(topLoadMoreView);
|
||||
} else if (FeatureFlags.messageRequests()) {
|
||||
adapter.setFooterView(conversationBanner);
|
||||
} else {
|
||||
adapter.setFooterView(null);
|
||||
}
|
||||
|
||||
if (lastSeen == -1) {
|
||||
setLastSeen(loader.getLastSeen());
|
||||
}
|
||||
|
||||
if (FeatureFlags.messageRequests() && !loader.hasPreMessageRequestMessages()) {
|
||||
clearHeaderIfNotTyping(adapter);
|
||||
} else {
|
||||
if (!loader.hasSent() && !recipient.get().isSystemContact() && !recipient.get().isGroup() && recipient.get().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
adapter.setHeaderView(unknownSenderView);
|
||||
} else {
|
||||
clearHeaderIfNotTyping(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
if (loader.hasOffset()) {
|
||||
adapter.setHeaderView(bottomLoadMoreView);
|
||||
}
|
||||
|
||||
if (firstLoad || loader.hasOffset()) {
|
||||
previousOffset = loader.getOffset();
|
||||
}
|
||||
|
||||
activeOffset = loader.getOffset();
|
||||
adapter.changeCursor(cursor);
|
||||
listener.onCursorChanged();
|
||||
|
||||
int lastSeenPosition = adapter.findLastSeenPosition(lastSeen);
|
||||
|
||||
if (isTypingIndicatorShowing()) {
|
||||
lastSeenPosition = Math.max(lastSeenPosition - 1, 0);
|
||||
}
|
||||
|
||||
if (firstLoad) {
|
||||
if (startingPosition >= 0) {
|
||||
scrollToStartingPosition(startingPosition);
|
||||
} else if (loader.isMessageRequestAccepted()) {
|
||||
scrollToLastSeenPosition(lastSeenPosition);
|
||||
} else if (FeatureFlags.messageRequests()) {
|
||||
list.post(() -> getListLayoutManager().scrollToPosition(adapter.getItemCount() - 1));
|
||||
}
|
||||
firstLoad = false;
|
||||
} else if (previousOffset > 0) {
|
||||
int scrollPosition = previousOffset + getListLayoutManager().findFirstVisibleItemPosition();
|
||||
scrollPosition = Math.min(scrollPosition, count - 1);
|
||||
|
||||
View firstView = list.getLayoutManager().getChildAt(scrollPosition);
|
||||
int pixelOffset = (firstView == null) ? 0 : (firstView.getBottom() - list.getPaddingBottom());
|
||||
|
||||
getListLayoutManager().scrollToPositionWithOffset(scrollPosition, pixelOffset);
|
||||
previousOffset = 0;
|
||||
}
|
||||
|
||||
if (lastSeenPosition <= 0) {
|
||||
setLastSeen(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearHeaderIfNotTyping(ConversationAdapter adapter) {
|
||||
if (adapter.getHeaderView() != typingView) {
|
||||
adapter.setHeaderView(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> arg0) {
|
||||
if (list.getAdapter() != null) {
|
||||
getListAdapter().changeCursor(null);
|
||||
listener.onCursorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public long stageOutgoingMessage(OutgoingMediaMessage message) {
|
||||
MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
|
||||
|
||||
|
@ -960,6 +862,73 @@ public class ConversationFragment extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
private void presentConversation(@NonNull ConversationData conversation) {
|
||||
Cursor cursor = conversation.getCursor();
|
||||
int count = cursor.getCount();
|
||||
|
||||
ConversationAdapter adapter = getListAdapter();
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && conversation.hasLimit()) {
|
||||
adapter.setFooterView(topLoadMoreView);
|
||||
} else if (FeatureFlags.messageRequests()) {
|
||||
adapter.setFooterView(conversationBanner);
|
||||
} else {
|
||||
adapter.setFooterView(null);
|
||||
}
|
||||
|
||||
if (conversationViewModel.getLastSeen() == -1) {
|
||||
setLastSeen(conversation.getLastSeen());
|
||||
}
|
||||
|
||||
if (FeatureFlags.messageRequests() && !conversation.hasPreMessageRequestMessages()) {
|
||||
clearHeaderIfNotTyping(adapter);
|
||||
} else {
|
||||
if (!conversation.hasSent() && !recipient.get().isSystemContact() && !recipient.get().isGroup() && recipient.get().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
adapter.setHeaderView(unknownSenderView);
|
||||
} else {
|
||||
clearHeaderIfNotTyping(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation.hasOffset()) {
|
||||
adapter.setHeaderView(bottomLoadMoreView);
|
||||
}
|
||||
|
||||
adapter.changeCursor(cursor);
|
||||
listener.onCursorChanged();
|
||||
|
||||
int lastSeenPosition = adapter.findLastSeenPosition(conversationViewModel.getLastSeen());
|
||||
|
||||
if (isTypingIndicatorShowing()) {
|
||||
lastSeenPosition = Math.max(lastSeenPosition - 1, 0);
|
||||
}
|
||||
|
||||
if (conversation.isFirstLoad()) {
|
||||
if (conversationViewModel.getStartingPosition() >= 0) {
|
||||
scrollToStartingPosition(conversationViewModel.getStartingPosition());
|
||||
} else if (conversation.isMessageRequestAccepted()) {
|
||||
scrollToLastSeenPosition(lastSeenPosition);
|
||||
} else if (FeatureFlags.messageRequests()) {
|
||||
list.post(() -> getListLayoutManager().scrollToPosition(adapter.getItemCount() - 1));
|
||||
}
|
||||
} else if (conversation.getPreviousOffset() > 0) {
|
||||
int scrollPosition = conversation.getPreviousOffset() + getListLayoutManager().findFirstVisibleItemPosition();
|
||||
scrollPosition = Math.min(scrollPosition, count - 1);
|
||||
|
||||
View firstView = list.getLayoutManager().getChildAt(scrollPosition);
|
||||
int pixelOffset = (firstView == null) ? 0 : (firstView.getBottom() - list.getPaddingBottom());
|
||||
|
||||
getListLayoutManager().scrollToPositionWithOffset(scrollPosition, pixelOffset);
|
||||
}
|
||||
|
||||
if (lastSeenPosition <= 0) {
|
||||
setLastSeen(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollToStartingPosition(final int startingPosition) {
|
||||
list.post(() -> {
|
||||
list.getLayoutManager().scrollToPosition(startingPosition);
|
||||
|
@ -1004,6 +973,8 @@ public class ConversationFragment extends Fragment
|
|||
}
|
||||
|
||||
private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) {
|
||||
int activeOffset = conversationViewModel.getActiveOffset();
|
||||
|
||||
Log.d(TAG, "Moving to message position: " + position + " activeOffset: " + activeOffset + " cursorCount: " + getListAdapter().getCursorCount());
|
||||
|
||||
if (position >= activeOffset && position >= 0 && position < getListAdapter().getCursorCount()) {
|
||||
|
@ -1018,9 +989,7 @@ public class ConversationFragment extends Fragment
|
|||
} else {
|
||||
Log.i(TAG, "Message was outside of the loaded range. Need to restart the loader.");
|
||||
|
||||
firstLoad = true;
|
||||
startingPosition = position;
|
||||
getLoaderManager().restartLoader(0, Bundle.EMPTY, ConversationFragment.this);
|
||||
conversationViewModel.onMoveJumpToMessageOutOfRange(position);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class ConversationRepository {
|
||||
|
||||
private final Context context;
|
||||
private final Executor executor;
|
||||
|
||||
public ConversationRepository() {
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
this.executor = SignalExecutors.BOUNDED;
|
||||
}
|
||||
|
||||
public void getConversationData(long threadId,
|
||||
int offset,
|
||||
int limit,
|
||||
long lastSeen,
|
||||
int previousOffset,
|
||||
boolean firstLoad,
|
||||
@NonNull Callback<ConversationData> callback)
|
||||
{
|
||||
executor.execute(() -> callback.onComplete(getConversationDataInternal(threadId, offset, limit, lastSeen, previousOffset, firstLoad)));
|
||||
}
|
||||
|
||||
private @NonNull ConversationData getConversationDataInternal(long threadId, int offset, int limit, long lastSeen, int previousOffset, boolean firstLoad) {
|
||||
Pair<Long, Boolean> lastSeenAndHasSent = DatabaseFactory.getThreadDatabase(context).getLastSeenAndHasSent(threadId);
|
||||
|
||||
boolean hasSent = lastSeenAndHasSent.second();
|
||||
|
||||
if (lastSeen == -1) {
|
||||
lastSeen = lastSeenAndHasSent.first();
|
||||
}
|
||||
|
||||
boolean isMessageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, threadId);
|
||||
boolean hasPreMessageRequestMessages = RecipientUtil.isPreMessageRequestThread(context, threadId);
|
||||
Cursor cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, offset, limit);
|
||||
|
||||
return new ConversationData(cursor, offset, limit, lastSeen, previousOffset, firstLoad, hasSent, isMessageRequestAccepted, hasPreMessageRequestMessages);
|
||||
}
|
||||
|
||||
|
||||
interface Callback<E> {
|
||||
void onComplete(@NonNull E result);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObservable;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
@ -8,32 +13,139 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mediasend.MediaRepository;
|
||||
import org.thoughtcrime.securesms.pin.PinState;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class ConversationViewModel extends ViewModel {
|
||||
|
||||
private final Context context;
|
||||
private final MediaRepository mediaRepository;
|
||||
private final MutableLiveData<List<Media>> recentMedia;
|
||||
private static final String TAG = Log.tag(ConversationViewModel.class);
|
||||
|
||||
private static final int NO_LIMIT = 0;
|
||||
|
||||
private final Application context;
|
||||
private final MediaRepository mediaRepository;
|
||||
private final ConversationRepository conversationRepository;
|
||||
private final MutableLiveData<List<Media>> recentMedia;
|
||||
private final MutableLiveData<ConversationData> conversation;
|
||||
private final ContentObserver contentObserver;
|
||||
|
||||
private Recipient recipient;
|
||||
private long threadId;
|
||||
private boolean firstLoad;
|
||||
private int requestedLimit;
|
||||
private long lastSeen;
|
||||
private int startingPosition;
|
||||
private int previousOffset;
|
||||
private boolean contentObserverRegistered;
|
||||
|
||||
private ConversationViewModel() {
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
this.mediaRepository = new MediaRepository();
|
||||
this.recentMedia = new MutableLiveData<>();
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
this.mediaRepository = new MediaRepository();
|
||||
this.conversationRepository = new ConversationRepository();
|
||||
this.recentMedia = new MutableLiveData<>();
|
||||
this.conversation = new MutableLiveData<>();
|
||||
this.contentObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
ConversationData data = conversation.getValue();
|
||||
if (data != null) {
|
||||
conversationRepository.getConversationData(threadId, data.getOffset(), data.getLimit(), data.getLastSeen(), data.getPreviousOffset(), data.isFirstLoad(), conversation::postValue);
|
||||
} else {
|
||||
Log.w(TAG, "Got a content change, but have no previous data?");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void onAttachmentKeyboardOpen() {
|
||||
mediaRepository.getMediaInBucket(context, Media.ALL_MEDIA_BUCKET_ID, recentMedia::postValue);
|
||||
}
|
||||
|
||||
void onConversationDataAvailable(Recipient recipient, long threadId, long lastSeen, int startingPosition, int limit) {
|
||||
this.recipient = recipient;
|
||||
this.threadId = threadId;
|
||||
this.lastSeen = lastSeen;
|
||||
this.startingPosition = startingPosition;
|
||||
this.requestedLimit = limit;
|
||||
this.firstLoad = true;
|
||||
|
||||
if (!contentObserverRegistered) {
|
||||
context.getContentResolver().registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadId), true, contentObserver);
|
||||
contentObserverRegistered = true;
|
||||
}
|
||||
|
||||
refreshConversation();
|
||||
}
|
||||
|
||||
void refreshConversation() {
|
||||
int limit = requestedLimit;
|
||||
int offset = 0;
|
||||
|
||||
if (requestedLimit != NO_LIMIT && startingPosition >= requestedLimit) {
|
||||
offset = Math.max(startingPosition - (requestedLimit / 2) + 1, 0);
|
||||
startingPosition -= offset - 1;
|
||||
}
|
||||
|
||||
conversationRepository.getConversationData(threadId, offset, limit, lastSeen, previousOffset, firstLoad, conversation::postValue);
|
||||
|
||||
if (firstLoad) {
|
||||
firstLoad = false;
|
||||
}
|
||||
|
||||
previousOffset = offset;
|
||||
}
|
||||
|
||||
void onLoadMoreClicked() {
|
||||
requestedLimit = 0;
|
||||
refreshConversation();
|
||||
}
|
||||
|
||||
void onMoveJumpToMessageOutOfRange(int startingPosition) {
|
||||
this.firstLoad = true;
|
||||
this.startingPosition = startingPosition;
|
||||
|
||||
refreshConversation();
|
||||
}
|
||||
|
||||
void onLastSeenChanged(long lastSeen) {
|
||||
this.lastSeen = lastSeen;
|
||||
}
|
||||
|
||||
@NonNull LiveData<List<Media>> getRecentMedia() {
|
||||
return recentMedia;
|
||||
}
|
||||
|
||||
@NonNull LiveData<ConversationData> getConversation() {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
int getStartingPosition() {
|
||||
return startingPosition;
|
||||
}
|
||||
|
||||
int getActiveOffset() {
|
||||
ConversationData data = conversation.getValue();
|
||||
return data != null ? data.getOffset() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
context.getContentResolver().unregisterContentObserver(contentObserver);
|
||||
contentObserverRegistered = false;
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
@Override
|
||||
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
package org.thoughtcrime.securesms.database.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
public class ConversationLoader extends AbstractCursorLoader {
|
||||
private final long threadId;
|
||||
private int offset;
|
||||
private int limit;
|
||||
private long lastSeen;
|
||||
private boolean hasSent;
|
||||
private boolean isMessageRequestAccepted;
|
||||
private boolean hasPreMessageRequestMessages;
|
||||
|
||||
public ConversationLoader(Context context, long threadId, int offset, int limit, long lastSeen) {
|
||||
super(context);
|
||||
this.threadId = threadId;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.lastSeen = lastSeen;
|
||||
this.hasSent = true;
|
||||
}
|
||||
|
||||
public boolean hasLimit() {
|
||||
return limit > 0;
|
||||
}
|
||||
|
||||
public boolean hasOffset() {
|
||||
return offset > 0;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
public boolean hasSent() {
|
||||
return hasSent;
|
||||
}
|
||||
|
||||
public boolean isMessageRequestAccepted() {
|
||||
return isMessageRequestAccepted;
|
||||
}
|
||||
|
||||
public boolean hasPreMessageRequestMessages() {
|
||||
return hasPreMessageRequestMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
Pair<Long, Boolean> lastSeenAndHasSent = DatabaseFactory.getThreadDatabase(context).getLastSeenAndHasSent(threadId);
|
||||
|
||||
this.hasSent = lastSeenAndHasSent.second();
|
||||
|
||||
if (lastSeen == -1) {
|
||||
this.lastSeen = lastSeenAndHasSent.first();
|
||||
}
|
||||
|
||||
this.isMessageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, threadId);
|
||||
this.hasPreMessageRequestMessages = RecipientUtil.isPreMessageRequestThread(context, threadId);
|
||||
|
||||
return DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, offset, limit);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue