Convert all database notifiers to use DatabaseObserver.
Lots of red in this diff to celebrate the release of Red (Taylor's Version).
This commit is contained in:
parent
ab55fec6bd
commit
658de3b6e7
30 changed files with 179 additions and 729 deletions
|
@ -770,22 +770,6 @@
|
|||
|
||||
</provider>
|
||||
|
||||
<provider android:name=".database.DatabaseContentProviders$Conversation"
|
||||
android:authorities="${applicationId}.database.conversation"
|
||||
android:exported="false" />
|
||||
|
||||
<provider android:name=".database.DatabaseContentProviders$Attachment"
|
||||
android:authorities="${applicationId}.database.attachment"
|
||||
android:exported="false" />
|
||||
|
||||
<provider android:name=".database.DatabaseContentProviders$Sticker"
|
||||
android:authorities="${applicationId}.database.sticker"
|
||||
android:exported="false" />
|
||||
|
||||
<provider android:name=".database.DatabaseContentProviders$StickerPack"
|
||||
android:authorities="${applicationId}.database.stickerpack"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver android:name=".service.BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
|
|
|
@ -568,26 +568,11 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
|||
if (item == 0) {
|
||||
viewPagerListener.onPageSelected(0);
|
||||
}
|
||||
|
||||
cursor.registerContentObserver(new ContentObserver(new Handler(getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onMediaChange();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mediaNotAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
private void onMediaChange() {
|
||||
MediaItemAdapter adapter = (MediaItemAdapter) mediaPager.getAdapter();
|
||||
|
||||
if (adapter != null) {
|
||||
adapter.checkMedia(mediaPager.getCurrentItem());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Pair<Cursor, Integer>> loader) {
|
||||
|
||||
|
@ -701,11 +686,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
|||
public boolean hasFragmentFor(int position) {
|
||||
return mediaPreviewFragment != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkMedia(int currentItem) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void anchorMarginsToBottomInsets(@NonNull View viewToAnchor) {
|
||||
|
@ -824,14 +804,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
|||
return mediaFragments.containsKey(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkMedia(int position) {
|
||||
MediaPreviewFragment fragment = mediaFragments.get(position);
|
||||
if (fragment != null) {
|
||||
fragment.checkMediaStillAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
private int getCursorPosition(int position) {
|
||||
if (leftIsRecent) return position;
|
||||
else return cursor.getCount() - 1 - position;
|
||||
|
@ -870,6 +842,5 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
|
|||
void pause(int position);
|
||||
@Nullable View getPlaybackControls(int position);
|
||||
boolean hasFragmentFor(int position);
|
||||
void checkMedia(int currentItem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import androidx.lifecycle.MutableLiveData;
|
|||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.search.MessageResult;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
import org.thoughtcrime.securesms.search.SearchRepository;
|
||||
import org.thoughtcrime.securesms.util.Debouncer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ConversationSearchViewModel extends AndroidViewModel {
|
||||
|
@ -39,7 +39,7 @@ public class ConversationSearchViewModel extends AndroidViewModel {
|
|||
|
||||
void onQueryUpdated(@NonNull String query, long threadId, boolean forced) {
|
||||
if (firstSearch && query.length() < 2) {
|
||||
result.postValue(new SearchResult(CursorList.emptyList(), 0));
|
||||
result.postValue(new SearchResult(Collections.emptyList(), 0));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -12,39 +9,34 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
||||
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
|
||||
import org.thoughtcrime.securesms.util.Throttler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class ConversationStickerViewModel extends ViewModel {
|
||||
|
||||
private final Application application;
|
||||
private final StickerSearchRepository repository;
|
||||
private final MutableLiveData<List<StickerRecord>> stickers;
|
||||
private final MutableLiveData<Boolean> stickersAvailable;
|
||||
private final Throttler availabilityThrottler;
|
||||
private final ContentObserver packObserver;
|
||||
private final DatabaseObserver.Observer packObserver;
|
||||
|
||||
private ConversationStickerViewModel(@NonNull Application application, @NonNull StickerSearchRepository repository) {
|
||||
this.application = application;
|
||||
this.repository = repository;
|
||||
this.stickers = new MutableLiveData<>();
|
||||
this.stickersAvailable = new MutableLiveData<>();
|
||||
this.availabilityThrottler = new Throttler(500);
|
||||
this.packObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
availabilityThrottler.publish(() -> repository.getStickerFeatureAvailability(stickersAvailable::postValue));
|
||||
}
|
||||
this.packObserver = () -> {
|
||||
availabilityThrottler.publish(() -> repository.getStickerFeatureAvailability(stickersAvailable::postValue));
|
||||
};
|
||||
|
||||
application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, packObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().registerStickerPackObserver(packObserver);
|
||||
}
|
||||
|
||||
@NonNull LiveData<List<StickerRecord>> getStickerResults() {
|
||||
|
@ -58,7 +50,7 @@ class ConversationStickerViewModel extends ViewModel {
|
|||
|
||||
void onInputTextUpdated(@NonNull String text) {
|
||||
if (TextUtils.isEmpty(text) || text.length() > EmojiSource.getLatest().getMaxEmojiLength()) {
|
||||
stickers.setValue(CursorList.emptyList());
|
||||
stickers.setValue(Collections.emptyList());
|
||||
} else {
|
||||
repository.searchByEmoji(text, stickers::postValue);
|
||||
}
|
||||
|
@ -66,7 +58,7 @@ class ConversationStickerViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(packObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(packObserver);
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
|
|
@ -317,15 +317,6 @@ public class AttachmentDatabase extends Database {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean hasAttachmentFilesForMessage(long mmsId) {
|
||||
String selection = MMS_ID + " = ? AND (" + DATA + " NOT NULL OR " + TRANSFER_STATE + " != ?)";
|
||||
String[] args = new String[] { String.valueOf(mmsId), String.valueOf(TRANSFER_PROGRESS_DONE) };
|
||||
|
||||
try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, "1")) {
|
||||
return cursor != null && cursor.moveToFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull List<DatabaseAttachment> getPendingAttachments() {
|
||||
final SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
final List<DatabaseAttachment> attachments = new LinkedList<>();
|
||||
|
@ -343,8 +334,7 @@ public class AttachmentDatabase extends Database {
|
|||
return attachments;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public void deleteAttachmentsForMessage(long mmsId) {
|
||||
public boolean deleteAttachmentsForMessage(long mmsId) {
|
||||
Log.d(TAG, "[deleteAttachmentsForMessage] mmsId: " + mmsId);
|
||||
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
|
@ -365,8 +355,10 @@ public class AttachmentDatabase extends Database {
|
|||
cursor.close();
|
||||
}
|
||||
|
||||
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {mmsId + ""});
|
||||
int deleteCount = database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {mmsId + ""});
|
||||
notifyAttachmentListeners();
|
||||
|
||||
return deleteCount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -631,6 +623,7 @@ public class AttachmentDatabase extends Database {
|
|||
|
||||
notifyConversationListeners(threadId);
|
||||
notifyConversationListListeners();
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
|
||||
if (transferFile != null) {
|
||||
|
@ -1273,6 +1266,9 @@ public class AttachmentDatabase extends Database {
|
|||
Log.d(TAG, "Inserting attachment for mms id: " + mmsId);
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
|
||||
AttachmentId attachmentId = null;
|
||||
boolean notifyPacks = false;
|
||||
|
||||
database.beginTransaction();
|
||||
try {
|
||||
DataInfo dataInfo = null;
|
||||
|
@ -1347,20 +1343,23 @@ public class AttachmentDatabase extends Database {
|
|||
}
|
||||
}
|
||||
|
||||
boolean notifyPacks = attachment.isSticker() && !hasStickerAttachments();
|
||||
long rowId = database.insert(TABLE_NAME, null, contentValues);
|
||||
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
|
||||
long rowId = database.insert(TABLE_NAME, null, contentValues);
|
||||
|
||||
if (notifyPacks) {
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
attachmentId = new AttachmentId(rowId, uniqueId);
|
||||
notifyPacks = attachment.isSticker() && !hasStickerAttachments();
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
|
||||
return attachmentId;
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
if (notifyPacks) {
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
|
||||
notifyAttachmentListeners();
|
||||
|
||||
return attachmentId;
|
||||
}
|
||||
|
||||
private @Nullable DatabaseAttachment findTemplateAttachment(@NonNull String dataHash) {
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* A list backed by a {@link Cursor} that retrieves models using a provided {@link ModelBuilder}.
|
||||
* Allows you to abstract away the use of a {@link Cursor} while still getting the benefits of a
|
||||
* {@link Cursor} (e.g. windowing).
|
||||
*
|
||||
* The one special consideration that must be made is that because this contains a cursor, you must
|
||||
* call {@link #close()} when you are finished with it.
|
||||
*
|
||||
* Given that this is cursor-backed, it is effectively immutable.
|
||||
*/
|
||||
public class CursorList<T> implements List<T>, ObservableContent {
|
||||
|
||||
private final Cursor cursor;
|
||||
private final ModelBuilder<T> modelBuilder;
|
||||
|
||||
public CursorList(@NonNull Cursor cursor, @NonNull ModelBuilder<T> modelBuilder) {
|
||||
this.cursor = cursor;
|
||||
this.modelBuilder = modelBuilder;
|
||||
|
||||
forceQueryLoad();
|
||||
}
|
||||
|
||||
public static <T> CursorList<T> emptyList() {
|
||||
//noinspection ConstantConditions,unchecked
|
||||
return (CursorList<T>) new CursorList(emptyCursor(), null);
|
||||
}
|
||||
|
||||
private static Cursor emptyCursor() {
|
||||
return new MatrixCursor(new String[] { "a" }, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return cursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return cursor.getCount() > 0 && !cursor.isLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
cursor.moveToPosition(index++);
|
||||
return modelBuilder.build(cursor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Object[] toArray() {
|
||||
Object[] out = new Object[size()];
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
out[i] = modelBuilder.build(cursor);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NonNull Collection collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int i, @NonNull Collection collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int i) {
|
||||
cursor.moveToPosition(i);
|
||||
return modelBuilder.build(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int i, T o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int i, T o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ListIterator<T> listIterator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ListIterator<T> listIterator(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<T> subList(int i, int i1) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NonNull Collection collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NonNull Collection collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NonNull Collection collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull T[] toArray(@Nullable Object[] objects) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContentObserver(@NonNull ContentObserver observer) {
|
||||
cursor.registerContentObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterContentObserver(@NonNull ContentObserver observer) {
|
||||
cursor.unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
private void forceQueryLoad() {
|
||||
cursor.getCount();
|
||||
}
|
||||
|
||||
public interface ModelBuilder<T> {
|
||||
T build(@NonNull Cursor cursor);
|
||||
}
|
||||
}
|
||||
|
|
@ -64,47 +64,16 @@ public abstract class Database {
|
|||
ApplicationDependencies.getDatabaseObserver().notifyConversationListListeners();
|
||||
}
|
||||
|
||||
protected void notifyStickerListeners() {
|
||||
context.getContentResolver().notifyChange(DatabaseContentProviders.Sticker.CONTENT_URI, null);
|
||||
}
|
||||
|
||||
protected void notifyStickerPackListeners() {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyStickerPackObservers();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setNotifyConversationListeners(Cursor cursor, long threadId) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getUriForThread(threadId));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setNotifyConversationListeners(Cursor cursor) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getUriForAllThreads());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setNotifyVerboseConversationListeners(Cursor cursor, long threadId) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setNotifyStickerListeners(Cursor cursor) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Sticker.CONTENT_URI);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setNotifyStickerPackListeners(Cursor cursor) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.StickerPack.CONTENT_URI);
|
||||
}
|
||||
|
||||
protected void registerAttachmentListeners(@NonNull ContentObserver observer) {
|
||||
context.getContentResolver().registerContentObserver(DatabaseContentProviders.Attachment.CONTENT_URI,
|
||||
true,
|
||||
observer);
|
||||
protected void notifyStickerListeners() {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyStickerObservers();
|
||||
}
|
||||
|
||||
protected void notifyAttachmentListeners() {
|
||||
context.getContentResolver().notifyChange(DatabaseContentProviders.Attachment.CONTENT_URI, null);
|
||||
ApplicationDependencies.getDatabaseObserver().notifyAttachmentObservers();
|
||||
}
|
||||
|
||||
public void reset(SQLCipherOpenHelper databaseHelper) {
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
|
||||
/**
|
||||
* Starting in API 26, a {@link ContentProvider} needs to be defined for each authority you wish to
|
||||
* observe changes on. These classes essentially do nothing except exist so Android doesn't complain.
|
||||
*/
|
||||
public class DatabaseContentProviders {
|
||||
|
||||
public static class Conversation extends NoopContentProvider {
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".database.conversation";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY + "/";
|
||||
|
||||
public static Uri getUriForThread(long threadId) {
|
||||
return Uri.parse(CONTENT_URI_STRING + threadId);
|
||||
}
|
||||
|
||||
public static Uri getVerboseUriForThread(long threadId) {
|
||||
return Uri.parse(CONTENT_URI_STRING + "verbose/" + threadId);
|
||||
}
|
||||
|
||||
public static Uri getUriForAllThreads() {
|
||||
return Uri.parse(CONTENT_URI_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Attachment extends NoopContentProvider {
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".database.attachment";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY;
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);
|
||||
}
|
||||
|
||||
public static class Sticker extends NoopContentProvider {
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".database.sticker";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY;
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);
|
||||
}
|
||||
|
||||
public static class StickerPack extends NoopContentProvider {
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".database.stickerpack";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY;
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);
|
||||
}
|
||||
|
||||
private static abstract class NoopContentProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,9 @@ public final class DatabaseObserver {
|
|||
private final Map<UUID, Set<Observer>> paymentObservers;
|
||||
private final Set<Observer> allPaymentsObservers;
|
||||
private final Set<Observer> chatColorsObservers;
|
||||
private final Set<Observer> stickerObservers;
|
||||
private final Set<Observer> stickerPackObservers;
|
||||
private final Set<Observer> attachmentObservers;
|
||||
private final Set<MessageObserver> messageUpdateObservers;
|
||||
private final Map<Long, Set<MessageObserver>> messageInsertObservers;
|
||||
|
||||
|
@ -44,7 +46,9 @@ public final class DatabaseObserver {
|
|||
this.paymentObservers = new HashMap<>();
|
||||
this.allPaymentsObservers = new HashSet<>();
|
||||
this.chatColorsObservers = new HashSet<>();
|
||||
this.stickerObservers = new HashSet<>();
|
||||
this.stickerPackObservers = new HashSet<>();
|
||||
this.attachmentObservers = new HashSet<>();
|
||||
this.messageUpdateObservers = new HashSet<>();
|
||||
this.messageInsertObservers = new HashMap<>();
|
||||
}
|
||||
|
@ -85,12 +89,24 @@ public final class DatabaseObserver {
|
|||
});
|
||||
}
|
||||
|
||||
public void registerStickerObserver(@NonNull Observer listener) {
|
||||
executor.execute(() -> {
|
||||
stickerObservers.add(listener);
|
||||
});
|
||||
}
|
||||
|
||||
public void registerStickerPackObserver(@NonNull Observer listener) {
|
||||
executor.execute(() -> {
|
||||
stickerPackObservers.add(listener);
|
||||
});
|
||||
}
|
||||
|
||||
public void registerAttachmentObserver(@NonNull Observer listener) {
|
||||
executor.execute(() -> {
|
||||
attachmentObservers.add(listener);
|
||||
});
|
||||
}
|
||||
|
||||
public void registerMessageUpdateObserver(@NonNull MessageObserver listener) {
|
||||
executor.execute(() -> {
|
||||
messageUpdateObservers.add(listener);
|
||||
|
@ -110,7 +126,9 @@ public final class DatabaseObserver {
|
|||
unregisterMapped(verboseConversationObservers, listener);
|
||||
unregisterMapped(paymentObservers, listener);
|
||||
chatColorsObservers.remove(listener);
|
||||
stickerObservers.remove(listener);
|
||||
stickerPackObservers.remove(listener);
|
||||
attachmentObservers.remove(listener);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -127,11 +145,6 @@ public final class DatabaseObserver {
|
|||
notifyMapped(conversationObservers, threadId);
|
||||
notifyMapped(verboseConversationObservers, threadId);
|
||||
}
|
||||
|
||||
for (long threadId : threadIds) {
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -139,9 +152,6 @@ public final class DatabaseObserver {
|
|||
executor.execute(() -> {
|
||||
notifyMapped(conversationObservers, threadId);
|
||||
notifyMapped(verboseConversationObservers, threadId);
|
||||
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -150,18 +160,12 @@ public final class DatabaseObserver {
|
|||
for (long threadId : threadIds) {
|
||||
notifyMapped(verboseConversationObservers, threadId);
|
||||
}
|
||||
|
||||
for (long threadId : threadIds) {
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyVerboseConversationListeners(long threadId) {
|
||||
executor.execute(() -> {
|
||||
notifyMapped(verboseConversationObservers, threadId);
|
||||
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -193,11 +197,21 @@ public final class DatabaseObserver {
|
|||
});
|
||||
}
|
||||
|
||||
public void notifyStickerObservers() {
|
||||
executor.execute(() -> {
|
||||
notifySet(stickerObservers);
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyStickerPackObservers() {
|
||||
executor.execute(() -> {
|
||||
notifySet(stickerPackObservers);
|
||||
});
|
||||
}
|
||||
|
||||
application.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
|
||||
public void notifyAttachmentObservers() {
|
||||
executor.execute(() -> {
|
||||
notifySet(attachmentObservers);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
|
||||
|
@ -96,54 +97,38 @@ public class MediaDatabase extends Database {
|
|||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
String query = sorting.applyToQuery(applyEqualityOperator(threadId, GALLERY_MEDIA_QUERY));
|
||||
String[] args = {threadId + ""};
|
||||
Cursor cursor = database.rawQuery(query, args);
|
||||
if (listenToAllThreads) {
|
||||
setNotifyConversationListeners(cursor);
|
||||
} else {
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
}
|
||||
return cursor;
|
||||
|
||||
return database.rawQuery(query, args);
|
||||
}
|
||||
|
||||
public @NonNull Cursor getDocumentMediaForThread(long threadId, @NonNull Sorting sorting) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
String query = sorting.applyToQuery(applyEqualityOperator(threadId, DOCUMENT_MEDIA_QUERY));
|
||||
String[] args = {threadId + ""};
|
||||
Cursor cursor = database.rawQuery(query, args);
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
return cursor;
|
||||
|
||||
return database.rawQuery(query, args);
|
||||
}
|
||||
|
||||
public @NonNull Cursor getAudioMediaForThread(long threadId, @NonNull Sorting sorting) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
String query = sorting.applyToQuery(applyEqualityOperator(threadId, AUDIO_MEDIA_QUERY));
|
||||
String[] args = {threadId + ""};
|
||||
Cursor cursor = database.rawQuery(query, args);
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
return cursor;
|
||||
|
||||
return database.rawQuery(query, args);
|
||||
}
|
||||
|
||||
public @NonNull Cursor getAllMediaForThread(long threadId, @NonNull Sorting sorting) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
String query = sorting.applyToQuery(applyEqualityOperator(threadId, ALL_MEDIA_QUERY));
|
||||
String[] args = {threadId + ""};
|
||||
Cursor cursor = database.rawQuery(query, args);
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
return cursor;
|
||||
|
||||
return database.rawQuery(query, args);
|
||||
}
|
||||
|
||||
private static String applyEqualityOperator(long threadId, String query) {
|
||||
return query.replace("__EQUALITY__", threadId == ALL_THREADS ? "!=" : "=");
|
||||
}
|
||||
|
||||
public void subscribeToMediaChanges(@NonNull ContentObserver observer) {
|
||||
registerAttachmentListeners(observer);
|
||||
}
|
||||
|
||||
public void unsubscribeToMediaChanges(@NonNull ContentObserver observer) {
|
||||
context.getContentResolver().unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
public StorageBreakdown getStorageBreakdown() {
|
||||
StorageBreakdown storageBreakdown = new StorageBreakdown();
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
|
|
|
@ -90,7 +90,6 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||
public abstract OutgoingMediaMessage getOutgoingMessage(long messageId) throws MmsException, NoSuchMessageException;
|
||||
public abstract MessageRecord getMessageRecord(long messageId) throws NoSuchMessageException;
|
||||
public abstract @Nullable MessageRecord getMessageRecordOrNull(long messageId);
|
||||
public abstract Cursor getVerboseMessageCursor(long messageId);
|
||||
public abstract boolean hasReceivedAnyCallsSince(long threadId, long timestamp);
|
||||
public abstract @Nullable ViewOnceExpirationInfo getNearestExpiringViewOnceMessage();
|
||||
public abstract boolean isSent(long messageId);
|
||||
|
|
|
@ -275,16 +275,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
|
||||
@Override
|
||||
public Cursor getMessageCursor(long messageId) {
|
||||
Cursor cursor = internalGetMessage(messageId);
|
||||
setNotifyConversationListeners(cursor, getThreadIdForMessage(messageId));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getVerboseMessageCursor(long messageId) {
|
||||
Cursor cursor = internalGetMessage(messageId);
|
||||
setNotifyVerboseConversationListeners(cursor, getThreadIdForMessage(messageId));
|
||||
return cursor;
|
||||
return internalGetMessage(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -843,6 +834,8 @@ public class MmsDatabase extends MessageDatabase {
|
|||
|
||||
long threadId;
|
||||
|
||||
boolean deletedAttachments = false;
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
ContentValues values = new ContentValues();
|
||||
|
@ -856,7 +849,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
values.putNull(SHARED_CONTACTS);
|
||||
db.update(TABLE_NAME, values, ID_WHERE, new String[] { String.valueOf(messageId) });
|
||||
|
||||
DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentsForMessage(messageId);
|
||||
deletedAttachments = DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentsForMessage(messageId);
|
||||
DatabaseFactory.getMentionDatabase(context).deleteMentionsForMessage(messageId);
|
||||
DatabaseFactory.getMessageLogDatabase(context).deleteAllRelatedToMessage(messageId, true);
|
||||
|
||||
|
@ -866,8 +859,13 @@ public class MmsDatabase extends MessageDatabase {
|
|||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
ApplicationDependencies.getDatabaseObserver().notifyMessageUpdateObservers(new MessageId(messageId, true));
|
||||
ApplicationDependencies.getDatabaseObserver().notifyConversationListListeners();
|
||||
|
||||
if (deletedAttachments) {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyAttachmentObservers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -200,26 +200,13 @@ public class MmsSmsDatabase extends Database {
|
|||
String limitStr = limit > 0 || offset > 0 ? offset + ", " + limit : null;
|
||||
String query = buildQuery(PROJECTION, selection, order, limitStr, false);
|
||||
|
||||
Cursor cursor = db.rawQuery(query, null);
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
|
||||
return cursor;
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public Cursor getConversation(long threadId) {
|
||||
return getConversation(threadId, 0, 0);
|
||||
}
|
||||
|
||||
public Cursor getIdentityConflictMessagesForThread(long threadId) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsSmsColumns.MISMATCHED_IDENTITIES + " IS NOT NULL";
|
||||
|
||||
Cursor cursor = queryTables(PROJECTION, selection, order, null);
|
||||
setNotifyConversationListeners(cursor, threadId);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public @NonNull MessageRecord getConversationSnippet(long threadId) throws NoSuchMessageException {
|
||||
try (Cursor cursor = getConversationSnippetCursor(threadId)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
|
|
|
@ -1320,13 +1320,6 @@ public class SmsDatabase extends MessageDatabase {
|
|||
else return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getVerboseMessageCursor(long messageId) {
|
||||
Cursor cursor = getMessageCursor(messageId);
|
||||
setNotifyVerboseConversationListeners(cursor, getThreadIdForMessage(messageId));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getMessageCursor(long messageId) {
|
||||
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
|
||||
|
|
|
@ -145,20 +145,15 @@ public class StickerDatabase extends Database {
|
|||
public @Nullable Cursor getInstalledStickerPacks() {
|
||||
String selection = COVER + " = ? AND " + INSTALLED + " = ?";
|
||||
String[] args = new String[] { "1", "1" };
|
||||
Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, PACK_ORDER + " ASC");
|
||||
|
||||
setNotifyStickerPackListeners(cursor);
|
||||
return cursor;
|
||||
return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, PACK_ORDER + " ASC");
|
||||
}
|
||||
|
||||
public @Nullable Cursor getStickersByEmoji(@NonNull String emoji) {
|
||||
String selection = EMOJI + " LIKE ? AND " + COVER + " = ?";
|
||||
String[] args = new String[] { "%"+emoji+"%", "0" };
|
||||
|
||||
Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null);
|
||||
setNotifyStickerListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null);
|
||||
}
|
||||
|
||||
public @Nullable Cursor getAllStickerPacks() {
|
||||
|
@ -168,10 +163,8 @@ public class StickerDatabase extends Database {
|
|||
public @Nullable Cursor getAllStickerPacks(@Nullable String limit) {
|
||||
String query = COVER + " = ?";
|
||||
String[] args = new String[] { "1" };
|
||||
Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, query, args, null, null, PACK_ORDER + " ASC", limit);
|
||||
setNotifyStickerPackListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, query, args, null, null, PACK_ORDER + " ASC", limit);
|
||||
}
|
||||
|
||||
public @Nullable Cursor getStickersForPack(@NonNull String packId) {
|
||||
|
@ -179,10 +172,7 @@ public class StickerDatabase extends Database {
|
|||
String selection = PACK_ID + " = ? AND " + COVER + " = ?";
|
||||
String[] args = new String[] { packId, "0" };
|
||||
|
||||
Cursor cursor = db.query(TABLE_NAME, null, selection, args, null, null, null);
|
||||
setNotifyStickerListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
return db.query(TABLE_NAME, null, selection, args, null, null, null);
|
||||
}
|
||||
|
||||
public @Nullable Cursor getRecentlyUsedStickers(int limit) {
|
||||
|
@ -190,10 +180,7 @@ public class StickerDatabase extends Database {
|
|||
String selection = LAST_USED + " > ? AND " + COVER + " = ?";
|
||||
String[] args = new String[] { "0", "0" };
|
||||
|
||||
Cursor cursor = db.query(TABLE_NAME, null, selection, args, null, null, LAST_USED + " DESC", String.valueOf(limit));
|
||||
setNotifyStickerListeners(cursor);
|
||||
|
||||
return cursor;
|
||||
return db.query(TABLE_NAME, null, selection, args, null, null, LAST_USED + " DESC", String.valueOf(limit));
|
||||
}
|
||||
|
||||
public @NonNull Set<String> getAllStickerFiles() {
|
||||
|
|
|
@ -291,7 +291,6 @@ public class ThreadDatabase extends Database {
|
|||
}
|
||||
|
||||
notifyAttachmentListeners();
|
||||
notifyStickerListeners();
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
|
||||
|
@ -326,7 +325,6 @@ public class ThreadDatabase extends Database {
|
|||
}
|
||||
|
||||
notifyAttachmentListeners();
|
||||
notifyStickerListeners();
|
||||
notifyStickerPackListeners();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,13 @@ import android.util.SparseArray;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.loader.content.AsyncTaskLoader;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.util.CalendarDateOnly;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -25,10 +28,10 @@ public final class GroupedThreadMediaLoader extends AsyncTaskLoader<GroupedThrea
|
|||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(GroupedThreadMediaLoader.class);
|
||||
|
||||
private final ContentObserver observer;
|
||||
private final MediaLoader.MediaType mediaType;
|
||||
private final MediaDatabase.Sorting sorting;
|
||||
private final long threadId;
|
||||
private final DatabaseObserver.Observer observer;
|
||||
private final MediaLoader.MediaType mediaType;
|
||||
private final MediaDatabase.Sorting sorting;
|
||||
private final long threadId;
|
||||
|
||||
public GroupedThreadMediaLoader(@NonNull Context context,
|
||||
long threadId,
|
||||
|
@ -39,7 +42,7 @@ public final class GroupedThreadMediaLoader extends AsyncTaskLoader<GroupedThrea
|
|||
this.threadId = threadId;
|
||||
this.mediaType = mediaType;
|
||||
this.sorting = sorting;
|
||||
this.observer = new ForceLoadContentObserver();
|
||||
this.observer = () -> ThreadUtil.runOnMain(this::onContentChanged);
|
||||
|
||||
onContentChanged();
|
||||
}
|
||||
|
@ -58,7 +61,7 @@ public final class GroupedThreadMediaLoader extends AsyncTaskLoader<GroupedThrea
|
|||
|
||||
@Override
|
||||
protected void onAbandon() {
|
||||
DatabaseFactory.getMediaDatabase(getContext()).unsubscribeToMediaChanges(observer);
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +73,8 @@ public final class GroupedThreadMediaLoader extends AsyncTaskLoader<GroupedThrea
|
|||
|
||||
PopulatedGroupedThreadMedia mediaGrouping = new PopulatedGroupedThreadMedia(groupingMethod);
|
||||
|
||||
DatabaseFactory.getMediaDatabase(context).subscribeToMediaChanges(observer);
|
||||
ApplicationDependencies.getDatabaseObserver().registerAttachmentObserver(observer);
|
||||
|
||||
try (Cursor cursor = ThreadMediaLoader.createThreadMediaCursor(context, threadId, mediaType, sorting)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
mediaGrouping.add(MediaDatabase.MediaRecord.from(context, cursor));
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||
|
||||
public class MessageDetailsLoader extends AbstractCursorLoader {
|
||||
private final String type;
|
||||
private final long messageId;
|
||||
|
||||
public MessageDetailsLoader(Context context, String type, long messageId) {
|
||||
super(context);
|
||||
this.type = type;
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
switch (type) {
|
||||
case MmsSmsDatabase.SMS_TRANSPORT:
|
||||
return DatabaseFactory.getSmsDatabase(context).getVerboseMessageCursor(messageId);
|
||||
case MmsSmsDatabase.MMS_TRANSPORT:
|
||||
return DatabaseFactory.getMmsDatabase(context).getVerboseMessageCursor(messageId);
|
||||
default:
|
||||
throw new AssertionError("no valid message type specified");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,12 +8,15 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.Sorting;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader;
|
||||
|
||||
|
@ -22,10 +25,11 @@ public final class PagingMediaLoader extends AsyncLoader<Pair<Cursor, Integer>>
|
|||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(PagingMediaLoader.class);
|
||||
|
||||
private final Uri uri;
|
||||
private final boolean leftIsRecent;
|
||||
private final Sorting sorting;
|
||||
private final long threadId;
|
||||
private final Uri uri;
|
||||
private final boolean leftIsRecent;
|
||||
private final Sorting sorting;
|
||||
private final long threadId;
|
||||
private final DatabaseObserver.Observer observer;
|
||||
|
||||
public PagingMediaLoader(@NonNull Context context, long threadId, @NonNull Uri uri, boolean leftIsRecent, @NonNull Sorting sorting) {
|
||||
super(context);
|
||||
|
@ -33,10 +37,15 @@ public final class PagingMediaLoader extends AsyncLoader<Pair<Cursor, Integer>>
|
|||
this.uri = uri;
|
||||
this.leftIsRecent = leftIsRecent;
|
||||
this.sorting = sorting;
|
||||
this.observer = () -> {
|
||||
ThreadUtil.runOnMain(this::onContentChanged);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<Cursor, Integer> loadInBackground() {
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(threadId, observer);
|
||||
|
||||
Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId, sorting, threadId == MediaDatabase.ALL_THREADS);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
|
@ -50,4 +59,9 @@ public final class PagingMediaLoader extends AsyncLoader<Pair<Cursor, Integer>>
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAbandon() {
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ class StickerKeyboardPageFragment :
|
|||
view.findViewById<View>(R.id.sticker_search).setOnClickListener { StickerSearchDialogFragment.show(requireActivity().supportFragmentManager) }
|
||||
view.findViewById<View>(R.id.sticker_manage).setOnClickListener { findListener<StickerEventListener>()?.onStickerManagementClicked() }
|
||||
|
||||
ApplicationDependencies.getDatabaseObserver().registerStickerObserver(this)
|
||||
ApplicationDependencies.getDatabaseObserver().registerStickerPackObserver(this)
|
||||
|
||||
view.addOnLayoutChangeListener(this)
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package org.thoughtcrime.securesms.longmessage;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
@ -12,31 +8,22 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
class LongMessageViewModel extends ViewModel {
|
||||
|
||||
private final Application application;
|
||||
private final LongMessageRepository repository;
|
||||
private final long messageId;
|
||||
private final boolean isMms;
|
||||
|
||||
private final MutableLiveData<Optional<LongMessage>> message;
|
||||
private final MessageObserver messageObserver;
|
||||
private final DatabaseObserver.Observer threadObserver;
|
||||
|
||||
private LongMessageViewModel(@NonNull Application application, @NonNull LongMessageRepository repository, long messageId, boolean isMms) {
|
||||
this.application = application;
|
||||
this.repository = repository;
|
||||
this.messageId = messageId;
|
||||
this.isMms = isMms;
|
||||
this.message = new MutableLiveData<>();
|
||||
this.messageObserver = new MessageObserver(new Handler(Looper.getMainLooper()));
|
||||
this.message = new MutableLiveData<>();
|
||||
this.threadObserver = () -> repository.getMessage(application, messageId, isMms, message::postValue);
|
||||
|
||||
repository.getMessage(application, messageId, isMms, longMessage -> {
|
||||
if (longMessage.isPresent()) {
|
||||
Uri uri = DatabaseContentProviders.Conversation.getUriForThread(longMessage.get().getMessageRecord().getThreadId());
|
||||
application.getContentResolver().registerContentObserver(uri, true, messageObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(longMessage.get().getMessageRecord().getThreadId(), threadObserver);
|
||||
}
|
||||
|
||||
message.postValue(longMessage);
|
||||
|
@ -49,18 +36,7 @@ class LongMessageViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(messageObserver);
|
||||
}
|
||||
|
||||
private class MessageObserver extends ContentObserver {
|
||||
MessageObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
repository.getMessage(application, messageId, isMms, message::postValue);
|
||||
}
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(threadObserver);
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
@ -79,6 +55,7 @@ class LongMessageViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection ConstantConditions
|
||||
return modelClass.cast(new LongMessageViewModel(context, repository, messageId, isMms));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.messagedetails;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -10,18 +9,20 @@ import androidx.lifecycle.LiveData;
|
|||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
|
||||
final class MessageRecordLiveData extends LiveData<MessageRecord> {
|
||||
|
||||
private final Context context;
|
||||
private final String type;
|
||||
private final Long messageId;
|
||||
private final ContentObserver obs;
|
||||
private final Context context;
|
||||
private final String type;
|
||||
private final Long messageId;
|
||||
private final DatabaseObserver.Observer observer;
|
||||
|
||||
private @Nullable Cursor cursor;
|
||||
|
||||
|
@ -29,13 +30,7 @@ final class MessageRecordLiveData extends LiveData<MessageRecord> {
|
|||
this.context = context;
|
||||
this.type = type;
|
||||
this.messageId = messageId;
|
||||
|
||||
obs = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
SignalExecutors.BOUNDED.execute(() -> resetCursor());
|
||||
}
|
||||
};
|
||||
this.observer = () -> SignalExecutors.BOUNDED.execute(this::resetCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,8 +49,9 @@ final class MessageRecordLiveData extends LiveData<MessageRecord> {
|
|||
|
||||
@WorkerThread
|
||||
private synchronized void destroyCursor() {
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.unregisterContentObserver(obs);
|
||||
cursor.close();
|
||||
cursor = null;
|
||||
}
|
||||
|
@ -87,22 +83,22 @@ final class MessageRecordLiveData extends LiveData<MessageRecord> {
|
|||
@WorkerThread
|
||||
private synchronized void handleSms() {
|
||||
final MessageDatabase db = DatabaseFactory.getSmsDatabase(context);
|
||||
final Cursor cursor = db.getVerboseMessageCursor(messageId);
|
||||
final Cursor cursor = db.getMessageCursor(messageId);
|
||||
final MessageRecord record = SmsDatabase.readerFor(cursor).getNext();
|
||||
|
||||
postValue(record);
|
||||
cursor.registerContentObserver(obs);
|
||||
ApplicationDependencies.getDatabaseObserver().registerVerboseConversationObserver(record.getThreadId(), observer);
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private synchronized void handleMms() {
|
||||
final MessageDatabase db = DatabaseFactory.getMmsDatabase(context);
|
||||
final Cursor cursor = db.getVerboseMessageCursor(messageId);
|
||||
final Cursor cursor = db.getMessageCursor(messageId);
|
||||
final MessageRecord record = MmsDatabase.readerFor(cursor).getNext();
|
||||
|
||||
postValue(record);
|
||||
cursor.registerContentObserver(obs);
|
||||
ApplicationDependencies.getDatabaseObserver().registerVerboseConversationObserver(record.getThreadId(), observer);
|
||||
this.cursor = cursor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public class ViewOnceMessageActivity extends PassphraseRequiredActivity implemen
|
|||
private void initViewModel(long messageId, @NonNull Uri uri) {
|
||||
ViewOnceMessageRepository repository = new ViewOnceMessageRepository(this);
|
||||
|
||||
viewModel = ViewModelProviders.of(this, new ViewOnceMessageViewModel.Factory(getApplication(), messageId, repository))
|
||||
viewModel = ViewModelProviders.of(this, new ViewOnceMessageViewModel.Factory(messageId, repository))
|
||||
.get(ViewOnceMessageViewModel.class);
|
||||
|
||||
viewModel.getMessage().observe(this, (message) -> {
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.signal.core.util.logging.Log;
|
|||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.model.MessageId;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
|
@ -31,8 +32,8 @@ class ViewOnceMessageRepository {
|
|||
|
||||
void getMessage(long messageId, @NonNull Callback<Optional<MmsMessageRecord>> callback) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
try (MmsDatabase.Reader reader = MmsDatabase.readerFor(mmsDatabase.getMessageCursor(messageId))) {
|
||||
MmsMessageRecord record = (MmsMessageRecord) reader.getNext();
|
||||
try {
|
||||
MmsMessageRecord record = (MmsMessageRecord) mmsDatabase.getMessageRecord(messageId);
|
||||
|
||||
MessageDatabase.MarkedMessageInfo info = mmsDatabase.setIncomingMessageViewed(record.getId());
|
||||
if (info != null) {
|
||||
|
@ -44,6 +45,8 @@ class ViewOnceMessageRepository {
|
|||
}
|
||||
|
||||
callback.onComplete(Optional.fromNullable(record));
|
||||
} catch (NoSuchMessageException e) {
|
||||
callback.onComplete(Optional.absent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package org.thoughtcrime.securesms.revealable;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
@ -12,37 +10,25 @@ import androidx.lifecycle.ViewModelProvider;
|
|||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
class ViewOnceMessageViewModel extends ViewModel {
|
||||
|
||||
private static final String TAG = Log.tag(ViewOnceMessageViewModel.class);
|
||||
|
||||
private final Application application;
|
||||
private final ViewOnceMessageRepository repository;
|
||||
private final MutableLiveData<Optional<MmsMessageRecord>> message;
|
||||
private final ContentObserver observer;
|
||||
private final DatabaseObserver.Observer observer;
|
||||
|
||||
private ViewOnceMessageViewModel(@NonNull Application application,
|
||||
long messageId,
|
||||
@NonNull ViewOnceMessageRepository repository)
|
||||
{
|
||||
this.application = application;
|
||||
this.repository = repository;
|
||||
this.message = new MutableLiveData<>();
|
||||
this.observer = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
repository.getMessage(messageId, optionalMessage -> onMessageRetrieved(optionalMessage));
|
||||
}
|
||||
};
|
||||
private ViewOnceMessageViewModel(long messageId, @NonNull ViewOnceMessageRepository repository) {
|
||||
this.message = new MutableLiveData<>();
|
||||
this.observer = () -> repository.getMessage(messageId, this::onMessageRetrieved);
|
||||
|
||||
repository.getMessage(messageId, message -> {
|
||||
if (message.isPresent()) {
|
||||
Uri uri = DatabaseContentProviders.Conversation.getUriForThread(message.get().getThreadId());
|
||||
application.getContentResolver().registerContentObserver(uri, true, observer);
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(message.get().getThreadId(), observer);
|
||||
}
|
||||
|
||||
onMessageRetrieved(message);
|
||||
|
@ -55,7 +41,7 @@ class ViewOnceMessageViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(observer);
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||
}
|
||||
|
||||
private void onMessageRetrieved(@NonNull Optional<MmsMessageRecord> optionalMessage) {
|
||||
|
@ -73,15 +59,10 @@ class ViewOnceMessageViewModel extends ViewModel {
|
|||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final Application application;
|
||||
private final long messageId;
|
||||
private final ViewOnceMessageRepository repository;
|
||||
|
||||
Factory(@NonNull Application application,
|
||||
long messageId,
|
||||
@NonNull ViewOnceMessageRepository repository)
|
||||
{
|
||||
this.application = application;
|
||||
Factory(long messageId, @NonNull ViewOnceMessageRepository repository) {
|
||||
this.messageId = messageId;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
@ -89,7 +70,7 @@ class ViewOnceMessageViewModel extends ViewModel {
|
|||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection ConstantConditions
|
||||
return modelClass.cast(new ViewOnceMessageViewModel(application, messageId, repository));
|
||||
return modelClass.cast(new ViewOnceMessageViewModel(messageId, repository));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.signal.core.util.concurrent.SignalExecutors;
|
|||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MentionDatabase;
|
||||
|
@ -118,7 +117,7 @@ public class SearchRepository {
|
|||
|
||||
public void query(@NonNull String query, long threadId, @NonNull Callback<List<MessageResult>> callback) {
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
callback.onResult(CursorList.emptyList());
|
||||
callback.onResult(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -345,11 +344,11 @@ public class SearchRepository {
|
|||
return body;
|
||||
}
|
||||
|
||||
private @NonNull <T> List<T> readToList(@Nullable Cursor cursor, @NonNull CursorList.ModelBuilder<T> builder) {
|
||||
private @NonNull <T> List<T> readToList(@Nullable Cursor cursor, @NonNull ModelBuilder<T> builder) {
|
||||
return readToList(cursor, builder, -1);
|
||||
}
|
||||
|
||||
private @NonNull <T> List<T> readToList(@Nullable Cursor cursor, @NonNull CursorList.ModelBuilder<T> builder, int limit) {
|
||||
private @NonNull <T> List<T> readToList(@Nullable Cursor cursor, @NonNull ModelBuilder<T> builder, int limit) {
|
||||
if (cursor == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -396,7 +395,7 @@ public class SearchRepository {
|
|||
return combined;
|
||||
}
|
||||
|
||||
private static class RecipientModelBuilder implements CursorList.ModelBuilder<Recipient> {
|
||||
private static class RecipientModelBuilder implements ModelBuilder<Recipient> {
|
||||
|
||||
@Override
|
||||
public Recipient build(@NonNull Cursor cursor) {
|
||||
|
@ -405,7 +404,7 @@ public class SearchRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ThreadModelBuilder implements CursorList.ModelBuilder<ThreadRecord> {
|
||||
private static class ThreadModelBuilder implements ModelBuilder<ThreadRecord> {
|
||||
|
||||
private final ThreadDatabase threadDatabase;
|
||||
|
||||
|
@ -419,7 +418,7 @@ public class SearchRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private static class MessageModelBuilder implements CursorList.ModelBuilder<MessageResult> {
|
||||
private static class MessageModelBuilder implements ModelBuilder<MessageResult> {
|
||||
|
||||
@Override
|
||||
public MessageResult build(@NonNull Cursor cursor) {
|
||||
|
@ -441,4 +440,8 @@ public class SearchRepository {
|
|||
public interface Callback<E> {
|
||||
void onResult(@NonNull E result);
|
||||
}
|
||||
|
||||
public interface ModelBuilder<T> {
|
||||
T build(@NonNull Cursor cursor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.stickers;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
@ -9,8 +8,9 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.stickers.StickerManagementRepository.PackResult;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -20,21 +20,18 @@ final class StickerManagementViewModel extends ViewModel {
|
|||
private final Application application;
|
||||
private final StickerManagementRepository repository;
|
||||
private final MutableLiveData<PackResult> packs;
|
||||
private final ContentObserver observer;
|
||||
private final DatabaseObserver.Observer observer;
|
||||
|
||||
private StickerManagementViewModel(@NonNull Application application, @NonNull StickerManagementRepository repository) {
|
||||
this.application = application;
|
||||
this.repository = repository;
|
||||
this.packs = new MutableLiveData<>();
|
||||
this.observer = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
repository.deleteOrphanedStickerPacks();
|
||||
repository.getStickerPacks(packs::postValue);
|
||||
}
|
||||
this.observer = () -> {
|
||||
repository.deleteOrphanedStickerPacks();
|
||||
repository.getStickerPacks(packs::postValue);
|
||||
};
|
||||
|
||||
application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, observer);
|
||||
ApplicationDependencies.getDatabaseObserver().registerStickerPackObserver(observer);
|
||||
}
|
||||
|
||||
void init() {
|
||||
|
@ -65,7 +62,7 @@ final class StickerManagementViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(observer);
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.stickers;
|
||||
|
||||
import android.app.Application;
|
||||
import android.database.ContentObserver;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -10,7 +9,8 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.stickers.StickerPackPreviewRepository.StickerManifestResult;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
|
@ -20,7 +20,7 @@ final class StickerPackPreviewViewModel extends ViewModel {
|
|||
private final StickerPackPreviewRepository previewRepository;
|
||||
private final StickerManagementRepository managementRepository;
|
||||
private final MutableLiveData<Optional<StickerManifestResult>> stickerManifest;
|
||||
private final ContentObserver packObserver;
|
||||
private final DatabaseObserver.Observer packObserver;
|
||||
|
||||
private String packId;
|
||||
private String packKey;
|
||||
|
@ -33,16 +33,13 @@ final class StickerPackPreviewViewModel extends ViewModel {
|
|||
this.previewRepository = previewRepository;
|
||||
this.managementRepository = managementRepository;
|
||||
this.stickerManifest = new MutableLiveData<>();
|
||||
this.packObserver = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
if (!TextUtils.isEmpty(packId) && !TextUtils.isEmpty(packKey)) {
|
||||
previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue);
|
||||
}
|
||||
this.packObserver = () -> {
|
||||
if (!TextUtils.isEmpty(packId) && !TextUtils.isEmpty(packKey)) {
|
||||
previewRepository.getStickerManifest(packId, packKey, stickerManifest::postValue);
|
||||
}
|
||||
};
|
||||
|
||||
application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, packObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().registerStickerPackObserver(packObserver);
|
||||
}
|
||||
|
||||
LiveData<Optional<StickerManifestResult>> getStickerManifest(@NonNull String packId, @NonNull String packKey) {
|
||||
|
@ -64,7 +61,7 @@ final class StickerPackPreviewViewModel extends ViewModel {
|
|||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
application.getContentResolver().unregisterContentObserver(packObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(packObserver);
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
|
|
@ -8,7 +8,6 @@ import androidx.annotation.NonNull;
|
|||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase.StickerRecordReader;
|
||||
|
@ -59,13 +58,6 @@ public final class StickerSearchRepository {
|
|||
});
|
||||
}
|
||||
|
||||
private static class StickerModelBuilder implements CursorList.ModelBuilder<StickerRecord> {
|
||||
@Override
|
||||
public StickerRecord build(@NonNull Cursor cursor) {
|
||||
return new StickerRecordReader(cursor).getCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback<T> {
|
||||
void onResult(T result);
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.database.ContentObserver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.signal.core.util.StreamUtil;
|
||||
import org.thoughtcrime.securesms.database.ObservableContent;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link androidx.lifecycle.LiveData} that will handle closing the contained
|
||||
* {@link Closeable} when the value changes.
|
||||
*/
|
||||
public class ObservingLiveData<E extends ObservableContent> extends MutableLiveData<E> {
|
||||
|
||||
private ContentObserver observer;
|
||||
|
||||
@Override
|
||||
public void setValue(E value) {
|
||||
E previous = getValue();
|
||||
|
||||
if (previous != null) {
|
||||
previous.unregisterContentObserver(observer);
|
||||
StreamUtil.close(previous);
|
||||
}
|
||||
|
||||
value.registerContentObserver(observer);
|
||||
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
E value = getValue();
|
||||
|
||||
if (value != null) {
|
||||
value.unregisterContentObserver(observer);
|
||||
StreamUtil.close(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerContentObserver(@NonNull ContentObserver observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue