Update view-once message behavior.

This commit is contained in:
Greyson Parrelli 2019-07-31 19:33:56 -04:00
parent 3439eb4536
commit 57835dc8f1
59 changed files with 575 additions and 569 deletions

View File

@ -336,7 +336,7 @@
android:windowSoftInputMode="stateUnchanged" android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".revealable.RevealableMessageActivity" <activity android:name=".revealable.ViewOnceMessageActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/TextSecure.FullScreenMedia" android:theme="@style/TextSecure.FullScreenMedia"
android:windowSoftInputMode="stateHidden" android:windowSoftInputMode="stateHidden"
@ -584,7 +584,7 @@
<receiver android:name=".service.ExpirationListener" /> <receiver android:name=".service.ExpirationListener" />
<receiver android:name=".revealable.RevealableMessageManager$RevealAlarm" /> <receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<provider android:name=".providers.PartProvider" <provider android:name=".providers.PartProvider"
android:grantUriPermissions="true" android:grantUriPermissions="true"

View File

@ -86,7 +86,7 @@ dependencies {
implementation 'org.conscrypt:conscrypt-android:2.0.0' implementation 'org.conscrypt:conscrypt-android:2.0.0'
implementation 'org.signal:aesgcmprovider:0.0.3' implementation 'org.signal:aesgcmprovider:0.0.3'
implementation 'org.whispersystems:signal-service-android:2.13.6' implementation 'org.whispersystems:signal-service-android:2.13.7'
implementation 'org.whispersystems:webrtc-android:M75' implementation 'org.whispersystems:webrtc-android:M75'
@ -193,7 +193,7 @@ dependencyVerification {
'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0', 'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0',
'org.conscrypt:conscrypt-android:400ca559a49b860a82862b22cee0e3110764bdcf7ee7c79e7479895c25cdfc09', 'org.conscrypt:conscrypt-android:400ca559a49b860a82862b22cee0e3110764bdcf7ee7c79e7479895c25cdfc09',
'org.signal:aesgcmprovider:6eb4422e8a618b3b76cb2096a3619d251f9e27989dc68307a1e5414c3710f2d1', 'org.signal:aesgcmprovider:6eb4422e8a618b3b76cb2096a3619d251f9e27989dc68307a1e5414c3710f2d1',
'org.whispersystems:signal-service-android:fa8c1b82d066bd6902ffd7e3a0c4343c3afa0379c26c78c06323c300d5afca7b', 'org.whispersystems:signal-service-android:5115aa434c52ca671c513995e6ae67d73f3abaaa605f9e6cf64c2e01da961c7e',
'org.whispersystems:webrtc-android:f8231bb57923afb243760213dc58924e85cce42f2f3cc8cb33a6d883672a921a', 'org.whispersystems:webrtc-android:f8231bb57923afb243760213dc58924e85cce42f2f3cc8cb33a6d883672a921a',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774', 'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
@ -265,7 +265,7 @@ dependencyVerification {
'androidx.constraintlayout:constraintlayout-solver:965c177e64fbd81bd1d27b402b66ef9d7bc7b5cb5f718044bf7a453abc542045', 'androidx.constraintlayout:constraintlayout-solver:965c177e64fbd81bd1d27b402b66ef9d7bc7b5cb5f718044bf7a453abc542045',
'com.google.auto.value:auto-value-annotations:0e951fee8c31f60270bc46553a8586001b7b93dbb12aec06373aa99a150392c0', 'com.google.auto.value:auto-value-annotations:0e951fee8c31f60270bc46553a8586001b7b93dbb12aec06373aa99a150392c0',
'org.signal:signal-metadata-android:02323bc29317fa9d3b62fab0b507c94ba2e9bcc4a78d588888ffd313853757b3', 'org.signal:signal-metadata-android:02323bc29317fa9d3b62fab0b507c94ba2e9bcc4a78d588888ffd313853757b3',
'org.whispersystems:signal-service-java:b36c460bb8ed8ff134c35df87781dea2f30bc33fa8a10c8c3d9314dae752a7da', 'org.whispersystems:signal-service-java:34c1efbfdc9cca44946a92f1ba330066bc533056a4db3359a1af96e519893b2e',
'com.github.bumptech.glide:disklrucache:4696a81340eb6beee21ab93f703ed6e7ae49fb4ce3bc2fbc546e5bacd21b96b9', 'com.github.bumptech.glide:disklrucache:4696a81340eb6beee21ab93f703ed6e7ae49fb4ce3bc2fbc546e5bacd21b96b9',
'com.github.bumptech.glide:annotations:702a7521cb3f6d7e55edd66e90bda1a1975baf971d25f75b75638579f86bc69b', 'com.github.bumptech.glide:annotations:702a7521cb3f6d7e55edd66e90bda1a1975baf971d25f75b75638579f86bc69b',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
@ -277,12 +277,12 @@ dependencyVerification {
'org.whispersystems:signal-protocol-java:7f6df67a963acbab7716424b01b12fa7279f18a9623a2a7c8ba7b1c285830168', 'org.whispersystems:signal-protocol-java:7f6df67a963acbab7716424b01b12fa7279f18a9623a2a7c8ba7b1c285830168',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74', 'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
'com.googlecode.libphonenumber:libphonenumber:dbf4bf566d17a60044c19e282a619684e4b4abb0f9f9f24f843c55d19826ab5e', 'com.googlecode.libphonenumber:libphonenumber:dbf4bf566d17a60044c19e282a619684e4b4abb0f9f9f24f843c55d19826ab5e',
'com.fasterxml.jackson.core:jackson-databind:2351c3eba73a545db9079f5d6d768347ad72666537362c8220fe3e950a55a864', 'com.fasterxml.jackson.core:jackson-databind:fb262d42ea2de98044b62d393950a5aa050435fec38bbcadf2325cf7dc41b848',
'com.squareup.okhttp3:okhttp:07c3d82ca7eaf4722f00b2da807dc7860f6169ae60cfedcf5d40218f90880a46', 'com.squareup.okhttp3:okhttp:07c3d82ca7eaf4722f00b2da807dc7860f6169ae60cfedcf5d40218f90880a46',
'org.threeten:threetenbp:f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7', 'org.threeten:threetenbp:f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7',
'org.whispersystems:curve25519-android:b502bcf83efe001f09a7a9efda6f0fa772c43ed5924e97816296ed3503caa092', 'org.whispersystems:curve25519-android:b502bcf83efe001f09a7a9efda6f0fa772c43ed5924e97816296ed3503caa092',
'com.fasterxml.jackson.core:jackson-annotations:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a', 'com.fasterxml.jackson.core:jackson-annotations:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a',
'com.fasterxml.jackson.core:jackson-core:d934dab0bd48994eeea2c1b493cb547158a338a80b58c4fbc8e85fb0905e105f', 'com.fasterxml.jackson.core:jackson-core:3083079be6088db2ed0a0c6ff92204e0aa48fa1de9db5b59c468f35acf882c2c',
'com.squareup.okio:okio:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2', 'com.squareup.okio:okio:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
'org.whispersystems:curve25519-java:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d', 'org.whispersystems:curve25519-java:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
] ]

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.revealable.RevealableMessageView <org.thoughtcrime.securesms.revealable.ViewOnceMessageView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.revealable.RevealableMessageView <org.thoughtcrime.securesms.revealable.ViewOnceMessageView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@ -7,13 +7,13 @@
android:background="@color/core_black"> android:background="@color/core_black">
<ImageView <ImageView
android:id="@+id/reveal_image" android:id="@+id/view_once_image"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scaleType="fitCenter"/> android:scaleType="fitCenter"/>
<ImageView <ImageView
android:id="@+id/reveal_close_button" android:id="@+id/view_once_close_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="19dp" android:layout_marginTop="19dp"

View File

@ -351,7 +351,7 @@
<attr name="labeledEditText_textLayout" format="reference" /> <attr name="labeledEditText_textLayout" format="reference" />
</declare-styleable> </declare-styleable>
<declare-styleable name="RevealableMessageView"> <declare-styleable name="ViewOnceMessageView">
<attr name="revealable_unopenedForegroundColor" format="color" /> <attr name="revealable_unopenedForegroundColor" format="color" />
<attr name="revealable_openedForegroundColor" format="color" /> <attr name="revealable_openedForegroundColor" format="color" />
</declare-styleable> </declare-styleable>

View File

@ -254,6 +254,7 @@
<string name="ConversationFragment_deleting_messages">Deleting messages...</string> <string name="ConversationFragment_deleting_messages">Deleting messages...</string>
<string name="ConversationFragment_quoted_message_not_found">Original message not found</string> <string name="ConversationFragment_quoted_message_not_found">Original message not found</string>
<string name="ConversationFragment_quoted_message_no_longer_available">Original message no longer available</string> <string name="ConversationFragment_quoted_message_no_longer_available">Original message no longer available</string>
<string name="ConversationFragment_failed_to_open_message">Failed to open message</string>
<!-- ConversationListActivity --> <!-- ConversationListActivity -->
<string name="ConversationListActivity_there_is_no_browser_installed_on_your_device">There is no browser installed on your device.</string> <string name="ConversationListActivity_there_is_no_browser_installed_on_your_device">There is no browser installed on your device.</string>

View File

@ -62,7 +62,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.IncomingMessageObserver; import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.LocalBackupListener; import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.revealable.RevealableMessageManager; import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
@ -92,7 +92,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private static final String TAG = ApplicationContext.class.getSimpleName(); private static final String TAG = ApplicationContext.class.getSimpleName();
private ExpiringMessageManager expiringMessageManager; private ExpiringMessageManager expiringMessageManager;
private RevealableMessageManager revealableMessageManager; private ViewOnceMessageManager viewOnceMessageManager;
private TypingStatusRepository typingStatusRepository; private TypingStatusRepository typingStatusRepository;
private TypingStatusSender typingStatusSender; private TypingStatusSender typingStatusSender;
private JobManager jobManager; private JobManager jobManager;
@ -157,8 +157,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
return expiringMessageManager; return expiringMessageManager;
} }
public RevealableMessageManager getRevealableMessageManager() { public ViewOnceMessageManager getViewOnceMessageManager() {
return revealableMessageManager; return viewOnceMessageManager;
} }
public TypingStatusRepository getTypingStatusRepository() { public TypingStatusRepository getTypingStatusRepository() {
@ -252,7 +252,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
} }
private void initializeRevealableMessageManager() { private void initializeRevealableMessageManager() {
this.revealableMessageManager = new RevealableMessageManager(this); this.viewOnceMessageManager = new ViewOnceMessageManager(this);
} }
private void initializeTypingStatusRepository() { private void initializeTypingStatusRepository() {

View File

@ -38,7 +38,7 @@ public interface BindableConversationItem extends Unbindable {
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview); void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms); void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms);
void onStickerClicked(@NonNull StickerLocator stickerLocator); void onStickerClicked(@NonNull StickerLocator stickerLocator);
void onRevealableMessageClicked(@NonNull MmsMessageRecord messageRecord); void onViewOnceMessageClicked(@NonNull MmsMessageRecord messageRecord);
void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView); void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView);
void onAddToContactsClicked(@NonNull Contact contact); void onAddToContactsClicked(@NonNull Contact contact);
void onMessageSharedContactClicked(@NonNull List<Recipient> choices); void onMessageSharedContactClicked(@NonNull List<Recipient> choices);

View File

@ -340,7 +340,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
.forData(inputStream, fileSize == null ? 0 : fileSize) .forData(inputStream, fileSize == null ? 0 : fileSize)
.withMimeType(mimeType) .withMimeType(mimeType)
.withFileName(fileName) .withFileName(fileName)
.createForMultipleSessionsOnDisk(context, e -> Log.w(TAG, "Failed to write to disk.", e)); .createForMultipleSessionsOnDisk(context);
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
return null; return null;

View File

@ -7,6 +7,12 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
/**
* An attachment that represents where an attachment used to be. Useful when you need to know that
* a message had an attachment and some metadata about it (like the contentType), even though the
* underlying media no longer exists. An example usecase would be view-once messages, so that we can
* quote them and know their contentType even though the media has been deleted.
*/
public class TombstoneAttachment extends Attachment { public class TombstoneAttachment extends Attachment {
public TombstoneAttachment(@NonNull String contentType, boolean quote) { public TombstoneAttachment(@NonNull String contentType, boolean quote) {

View File

@ -50,7 +50,7 @@ public class AudioRecorder {
captureUri = BlobProvider.getInstance() captureUri = BlobProvider.getInstance()
.forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0)
.withMimeType(MediaUtil.AUDIO_AAC) .withMimeType(MediaUtil.AUDIO_AAC)
.createForSingleSessionOnDisk(context, e -> Log.w(TAG, "Error during recording", e)); .createForSingleSessionOnDiskAsync(context, () -> Log.i(TAG, "Write successful."), e -> Log.w(TAG, "Error during recording", e));
audioCodec = new AudioCodec(); audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])); audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));

View File

@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase; import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.SessionDatabase; import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase; import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.AvatarHelper;
@ -254,19 +253,19 @@ public class FullBackupExporter extends FullBackupBase {
} }
private static boolean isNonExpiringMessage(@NonNull Cursor cursor) { private static boolean isNonExpiringMessage(@NonNull Cursor cursor) {
return cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 && return cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 &&
cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_DURATION)) <= 0; cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) <= 0;
} }
private static boolean isForNonExpiringMessage(@NonNull SQLiteDatabase db, long mmsId) { private static boolean isForNonExpiringMessage(@NonNull SQLiteDatabase db, long mmsId) {
String[] columns = new String[] { MmsDatabase.EXPIRES_IN, MmsDatabase.REVEAL_DURATION }; String[] columns = new String[] { MmsDatabase.EXPIRES_IN, MmsDatabase.VIEW_ONCE};
String where = MmsDatabase.ID + " = ?"; String where = MmsDatabase.ID + " = ?";
String[] args = new String[] { String.valueOf(mmsId) }; String[] args = new String[] { String.valueOf(mmsId) };
try (Cursor mmsCursor = db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null)) { try (Cursor mmsCursor = db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null)) {
if (mmsCursor != null && mmsCursor.moveToFirst()) { if (mmsCursor != null && mmsCursor.moveToFirst()) {
return mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)) == 0 && return mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)) == 0 &&
mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_DURATION)) == 0; mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) == 0;
} }
} }

View File

@ -542,8 +542,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean initiating = threadId == -1; boolean initiating = threadId == -1;
TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT); TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT);
String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE);
long revealDuration = data.getLongExtra(MediaSendActivity.EXTRA_REVEAL_DURATION, 0); boolean viewOnce = data.getBooleanExtra(MediaSendActivity.EXTRA_VIEW_ONCE, false);
QuoteModel quote = (revealDuration == 0) ? inputPanel.getQuote().orNull() : null; QuoteModel quote = viewOnce ? inputPanel.getQuote().orNull() : null;
SlideDeck slideDeck = new SlideDeck(); SlideDeck slideDeck = new SlideDeck();
if (transport == null) { if (transport == null) {
@ -575,7 +575,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(),
expiresIn, expiresIn,
revealDuration, viewOnce,
subscriptionId, subscriptionId,
initiating, initiating,
true).addListener(new AssertedSuccessListener<Void>() { true).addListener(new AssertedSuccessListener<Void>() {
@ -1813,7 +1813,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long expiresIn = recipient.getExpireMessages() * 1000L; long expiresIn = recipient.getExpireMessages() * 1000L;
boolean initiating = threadId == -1; boolean initiating = threadId == -1;
sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, 0, subscriptionId, initiating, false); sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), expiresIn, false, subscriptionId, initiating, false);
} }
private void selectContactInfo(ContactData contactData) { private void selectContactInfo(ContactData contactData) {
@ -2135,7 +2135,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else if (!forceSms && identityRecords.isUntrusted()) { } else if (!forceSms && identityRecords.isUntrusted()) {
handleUntrustedRecipients(); handleUntrustedRecipients();
} else if (isMediaMessage) { } else if (isMediaMessage) {
sendMediaMessage(forceSms, expiresIn, 0, subscriptionId, initiating); sendMediaMessage(forceSms, expiresIn, false, subscriptionId, initiating);
} else { } else {
sendTextMessage(forceSms, expiresIn, subscriptionId, initiating); sendTextMessage(forceSms, expiresIn, subscriptionId, initiating);
} }
@ -2151,11 +2151,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
private void sendMediaMessage(final boolean forceSms, final long expiresIn, final long revealDuration, final int subscriptionId, boolean initiating) private void sendMediaMessage(final boolean forceSms, final long expiresIn, final boolean viewOnce, final int subscriptionId, boolean initiating)
throws InvalidMessageException throws InvalidMessageException
{ {
Log.i(TAG, "Sending media message..."); Log.i(TAG, "Sending media message...");
sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, revealDuration, subscriptionId, initiating, true); sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), Collections.emptyList(), linkPreviewViewModel.getActiveLinkPreviews(), expiresIn, viewOnce, subscriptionId, initiating, true);
} }
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms, private ListenableFuture<Void> sendMediaMessage(final boolean forceSms,
@ -2165,7 +2165,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
List<Contact> contacts, List<Contact> contacts,
List<LinkPreview> previews, List<LinkPreview> previews,
final long expiresIn, final long expiresIn,
final long revealDuration, final boolean viewOnce,
final int subscriptionId, final int subscriptionId,
final boolean initiating, final boolean initiating,
final boolean clearComposeBox) final boolean clearComposeBox)
@ -2184,7 +2184,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, revealDuration, distributionType, quote, contacts, previews); OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews);
final SettableFuture<Void> future = new SettableFuture<>(); final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext(); final Context context = getApplicationContext();
@ -2385,7 +2385,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
SlideDeck slideDeck = new SlideDeck(); SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide); slideDeck.addSlide(audioSlide);
sendMediaMessage(forceSms, "", slideDeck, inputPanel.getQuote().orNull(), Collections.emptyList(), Collections.emptyList(), expiresIn, 0, subscriptionId, initiating, true).addListener(new AssertedSuccessListener<Void>() { sendMediaMessage(forceSms, "", slideDeck, inputPanel.getQuote().orNull(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, true).addListener(new AssertedSuccessListener<Void>() {
@Override @Override
public void onSuccess(Void nothing) { public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@ -2513,7 +2513,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
slideDeck.addSlide(stickerSlide); slideDeck.addSlide(stickerSlide);
sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), expiresIn, 0, subscriptionId, initiating, clearCompose); sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose);
} }
@ -2696,7 +2696,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else { } else {
SlideDeck slideDeck = messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(); SlideDeck slideDeck = messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck();
if (messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getRevealDuration() > 0 && slideDeck.getSlides().size() > 0) { if (messageRecord.isMms() && ((MmsMessageRecord) messageRecord).isViewOnce() && slideDeck.getSlides().size() > 0) {
Attachment attachment = new TombstoneAttachment(slideDeck.getSlides().get(0).getContentType(), true); Attachment attachment = new TombstoneAttachment(slideDeck.getSlides().get(0).getContentType(), true);
slideDeck = new SlideDeck(); slideDeck = new SlideDeck();
slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, attachment)); slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, attachment));

View File

@ -79,7 +79,7 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceRevealUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity; import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
@ -89,9 +89,10 @@ import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.profiles.UnknownSenderView; import org.thoughtcrime.securesms.profiles.UnknownSenderView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.revealable.RevealableMessageActivity; import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity;
import org.thoughtcrime.securesms.revealable.RevealableUtil; import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.stickers.StickerLocator; import org.thoughtcrime.securesms.stickers.StickerLocator;
@ -102,6 +103,7 @@ import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -963,35 +965,46 @@ public class ConversationFragment extends Fragment
} }
@Override @Override
public void onRevealableMessageClicked(@NonNull MmsMessageRecord messageRecord) { public void onViewOnceMessageClicked(@NonNull MmsMessageRecord messageRecord) {
if (messageRecord.getRevealDuration() == 0) { if (!messageRecord.isViewOnce()) {
throw new AssertionError("Non-revealable message clicked."); throw new AssertionError("Non-revealable message clicked.");
} }
if (messageRecord.getRevealStartTime() == 0) { if (!ViewOnceUtil.isViewable(messageRecord)) {
SimpleTask.run(getLifecycle(), () -> { Log.w(TAG, "View-once photo is not viewable!");
if (!messageRecord.isOutgoing()) { return;
Log.i(TAG, "Marking revealable message as opened.");
DatabaseFactory.getMmsDatabase(requireContext()).markRevealStarted(messageRecord.getId());
ApplicationContext.getInstance(requireContext())
.getRevealableMessageManager()
.scheduleIfNecessary();
ApplicationContext.getInstance(requireContext())
.getJobManager()
.add(new MultiDeviceRevealUpdateJob(new MessagingDatabase.SyncMessageId(messageRecord.getIndividualRecipient().getAddress(), messageRecord.getDateSent())));
} else {
Log.i(TAG, "Opening your own revealable message. It will automatically be marked as opened when it is sent.");
}
return null;
}, (nothing) -> {
startActivity(RevealableMessageActivity.getIntent(requireContext(), messageRecord.getId()));
});
} else if (RevealableUtil.isViewable(messageRecord)) {
startActivity(RevealableMessageActivity.getIntent(requireContext(), messageRecord.getId()));
} }
SimpleTask.run(getLifecycle(), () -> {
Log.i(TAG, "Copying the view-once photo to temp storage and deleting underlying media.");
try {
InputStream inputStream = PartAuthority.getAttachmentStream(requireContext(), messageRecord.getSlideDeck().getThumbnailSlide().getUri());
Uri tempUri = BlobProvider.getInstance().forData(inputStream, 0).createForSingleSessionOnDisk(requireContext());
DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForMessage(messageRecord.getId());
ApplicationContext.getInstance(requireContext())
.getViewOnceMessageManager()
.scheduleIfNecessary();
ApplicationContext.getInstance(requireContext())
.getJobManager()
.add(new MultiDeviceViewOnceOpenJob(new MessagingDatabase.SyncMessageId(messageRecord.getIndividualRecipient().getAddress(), messageRecord.getDateSent())));
return tempUri;
} catch (IOException e) {
return null;
}
}, (uri) -> {
if (uri != null) {
startActivity(ViewOnceMessageActivity.getIntent(requireContext(), messageRecord.getId(), uri));
} else {
Log.w(TAG, "Failed to open view-once photo. Showing a toast and deleting the attachments for the message just in case.");
Toast.makeText(requireContext(), R.string.ConversationFragment_failed_to_open_message, Toast.LENGTH_SHORT).show();
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForMessage(messageRecord.getId()));
}
});
} }
@Override @Override

View File

@ -68,7 +68,7 @@ import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.LinkPreviewView; import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.Outliner; import org.thoughtcrime.securesms.components.Outliner;
import org.thoughtcrime.securesms.components.QuoteView; import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.revealable.RevealableMessageView; import org.thoughtcrime.securesms.revealable.ViewOnceMessageView;
import org.thoughtcrime.securesms.components.SharedContactView; import org.thoughtcrime.securesms.components.SharedContactView;
import org.thoughtcrime.securesms.components.StickerView; import org.thoughtcrime.securesms.components.StickerView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
@ -99,7 +99,7 @@ import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.mms.TextSlide; import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.revealable.RevealableUtil; import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.stickers.StickerUrl; import org.thoughtcrime.securesms.stickers.StickerUrl;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
@ -163,7 +163,7 @@ public class ConversationItem extends LinearLayout
private Stub<SharedContactView> sharedContactStub; private Stub<SharedContactView> sharedContactStub;
private Stub<LinkPreviewView> linkPreviewStub; private Stub<LinkPreviewView> linkPreviewStub;
private Stub<StickerView> stickerStub; private Stub<StickerView> stickerStub;
private Stub<RevealableMessageView> revealableStub; private Stub<ViewOnceMessageView> revealableStub;
private @Nullable EventListener eventListener; private @Nullable EventListener eventListener;
private int defaultBubbleColor; private int defaultBubbleColor;
@ -175,7 +175,7 @@ public class ConversationItem extends LinearLayout
private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener(); private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener();
private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener(); private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener();
private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener(); private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener();
private final RevealableMessageClickListener revealableClickListener = new RevealableMessageClickListener(); private final ViewOnceMessageClickListener revealableClickListener = new ViewOnceMessageClickListener();
private final Context context; private final Context context;
@ -314,7 +314,7 @@ public class ConversationItem extends LinearLayout
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
if (!messageRecord.isOutgoing() && hasRevealableMessage(messageRecord) && RevealableUtil.isRevealExpired((MmsMessageRecord) messageRecord)) { if (!messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord)) {
outliner.setColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color)); outliner.setColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color));
outliner.draw(canvas, bodyBubble.getTop() + getPaddingTop(), bodyBubble.getRight(), bodyBubble.getBottom() + getPaddingTop(), bodyBubble.getLeft()); outliner.draw(canvas, bodyBubble.getTop() + getPaddingTop(), bodyBubble.getRight(), bodyBubble.getBottom() + getPaddingTop(), bodyBubble.getLeft());
} }
@ -324,7 +324,7 @@ public class ConversationItem extends LinearLayout
int availableWidth; int availableWidth;
if (hasAudio(messageRecord)) { if (hasAudio(messageRecord)) {
availableWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get()); availableWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get());
} else if (!hasRevealableMessage(messageRecord) && (hasThumbnail(messageRecord) || hasBigImageLinkPreview(messageRecord))) { } else if (!isViewOnceMessage(messageRecord) && (hasThumbnail(messageRecord) || hasBigImageLinkPreview(messageRecord))) {
availableWidth = mediaThumbnailStub.get().getMeasuredWidth(); availableWidth = mediaThumbnailStub.get().getMeasuredWidth();
} else { } else {
availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight(); availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight();
@ -361,7 +361,7 @@ public class ConversationItem extends LinearLayout
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY); bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color)); footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color));
footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color)); footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color));
} else if (hasRevealableMessage(messageRecord) && RevealableUtil.isRevealExpired((MmsMessageRecord) messageRecord)) { } else if (isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord)) {
bodyBubble.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_reveal_viewed_background_color), PorterDuff.Mode.MULTIPLY); bodyBubble.getBackground().setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_reveal_viewed_background_color), PorterDuff.Mode.MULTIPLY);
footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color)); footer.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_text_secondary_color));
footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color)); footer.setIconColor(ThemeUtil.getThemedColor(context, R.attr.conversation_item_sent_icon_color));
@ -440,7 +440,7 @@ public class ConversationItem extends LinearLayout
!hasDocument(messageRecord) && !hasDocument(messageRecord) &&
!hasSharedContact(messageRecord) && !hasSharedContact(messageRecord) &&
!hasSticker(messageRecord) && !hasSticker(messageRecord) &&
!hasRevealableMessage(messageRecord); !isViewOnceMessage(messageRecord);
} }
private boolean hasDocument(MessageRecord messageRecord) { private boolean hasDocument(MessageRecord messageRecord) {
@ -477,8 +477,8 @@ public class ConversationItem extends LinearLayout
!StickerUrl.isValidShareLink(linkPreview.getUrl()); !StickerUrl.isValidShareLink(linkPreview.getUrl());
} }
private boolean hasRevealableMessage(MessageRecord messageRecord) { private boolean isViewOnceMessage(MessageRecord messageRecord) {
return messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getRevealDuration() > 0; return messageRecord.isMms() && ((MmsMessageRecord) messageRecord).isViewOnce();
} }
private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery) { private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery) {
@ -512,7 +512,7 @@ public class ConversationItem extends LinearLayout
{ {
boolean showControls = !messageRecord.isFailed(); boolean showControls = !messageRecord.isFailed();
if (hasRevealableMessage(messageRecord)) { if (isViewOnceMessage(messageRecord)) {
revealableStub.get().setVisibility(VISIBLE); revealableStub.get().setVisibility(VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
@ -928,7 +928,7 @@ public class ConversationItem extends LinearLayout
} }
private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) { private void setGroupAuthorColor(@NonNull MessageRecord messageRecord) {
if (!messageRecord.isOutgoing() && hasRevealableMessage(messageRecord) && RevealableUtil.isRevealExpired((MmsMessageRecord) messageRecord)) { if (!messageRecord.isOutgoing() && isViewOnceMessage(messageRecord) && ViewOnceUtil.isViewed((MmsMessageRecord) messageRecord)) {
groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); groupSender.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color));
groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color)); groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color));
} else if (hasSticker(messageRecord)) { } else if (hasSticker(messageRecord)) {
@ -1169,13 +1169,13 @@ public class ConversationItem extends LinearLayout
} }
} }
private class RevealableMessageClickListener implements View.OnClickListener { private class ViewOnceMessageClickListener implements View.OnClickListener {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
RevealableMessageView revealView = (RevealableMessageView) view; ViewOnceMessageView revealView = (ViewOnceMessageView) view;
if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && RevealableUtil.isViewable((MmsMessageRecord) messageRecord)) { if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && ViewOnceUtil.isViewable((MmsMessageRecord) messageRecord)) {
eventListener.onRevealableMessageClicked((MmsMessageRecord) messageRecord); eventListener.onViewOnceMessageClicked((MmsMessageRecord) messageRecord);
} else if (batchSelected.isEmpty() && messageRecord.isMms() && revealView.requiresTapToDownload((MmsMessageRecord) messageRecord)) { } else if (batchSelected.isEmpty() && messageRecord.isMms() && revealView.requiresTapToDownload((MmsMessageRecord) messageRecord)) {
singleDownloadClickListener.onClick(view, ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide()); singleDownloadClickListener.onClick(view, ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide());
} else { } else {

View File

@ -47,7 +47,7 @@ public class MediaDatabase extends Database {
+ "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID + "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID
+ " FROM " + MmsDatabase.TABLE_NAME + " FROM " + MmsDatabase.TABLE_NAME
+ " WHERE " + MmsDatabase.THREAD_ID + " = ?) AND (%s) AND " + " WHERE " + MmsDatabase.THREAD_ID + " = ?) AND (%s) AND "
+ MmsDatabase.REVEAL_DURATION + " = 0 AND " + MmsDatabase.VIEW_ONCE + " = 0 AND "
+ AttachmentDatabase.DATA + " IS NOT NULL AND " + AttachmentDatabase.DATA + " IS NOT NULL AND "
+ AttachmentDatabase.QUOTE + " = 0 AND " + AttachmentDatabase.QUOTE + " = 0 AND "
+ AttachmentDatabase.STICKER_PACK_ID + " IS NULL " + AttachmentDatabase.STICKER_PACK_ID + " IS NULL "

View File

@ -63,8 +63,8 @@ import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.revealable.RevealExpirationInfo; import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
import org.thoughtcrime.securesms.revealable.RevealableUtil; import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -109,8 +109,7 @@ public class MmsDatabase extends MessagingDatabase {
static final String SHARED_CONTACTS = "shared_contacts"; static final String SHARED_CONTACTS = "shared_contacts";
static final String LINK_PREVIEWS = "previews"; static final String LINK_PREVIEWS = "previews";
public static final String REVEAL_DURATION = "reveal_duration"; public static final String VIEW_ONCE = "reveal_duration";
public static final String REVEAL_START_TIME = "reveal_start_time";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " + THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " +
@ -131,7 +130,7 @@ public class MmsDatabase extends MessagingDatabase {
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " +
QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " + QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " +
QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " + QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " +
LINK_PREVIEWS + " TEXT, " + REVEAL_DURATION + " INTEGER DEFAULT 0, " + REVEAL_START_TIME + " INTEGER DEFAULT 0);"; LINK_PREVIEWS + " TEXT, " + VIEW_ONCE + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@ -152,7 +151,7 @@ public class MmsDatabase extends MessagingDatabase {
BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID, BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING,
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, REVEAL_DURATION, REVEAL_START_TIME, SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE,
"json_group_array(json_object(" + "json_group_array(json_object(" +
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " + "'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " + "'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
@ -437,60 +436,6 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
public void markRevealStarted(long messageId) {
markRevealStarted(messageId, System.currentTimeMillis());
}
public void markRevealStarted(long messageId, long startTime) {
ContentValues contentValues = new ContentValues();
contentValues.put(REVEAL_START_TIME, startTime);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});
long threadId = getThreadIdForMessage(messageId);
notifyConversationListeners(threadId);
}
public List<RevealExpirationInfo> markRevealStarted(@NonNull SyncMessageId messageId, long proposedStartTime) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
List<RevealExpirationInfo> expirationInfos = new LinkedList<>();
String[] projection = new String[] { ID, ADDRESS, THREAD_ID, DATE_SENT, DATE_RECEIVED, REVEAL_DURATION, REVEAL_START_TIME };
String selection = DATE_SENT + " = ?";
String[] args = new String[] { String.valueOf(messageId.getTimetamp()) };
try (Cursor cursor = db.query(TABLE_NAME, projection, selection, args, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long receiveTime = cursor.getLong(cursor.getColumnIndexOrThrow(DATE_RECEIVED));
long revealDuration = cursor.getLong(cursor.getColumnIndexOrThrow(REVEAL_DURATION));
long revealStartTime = cursor.getLong(cursor.getColumnIndexOrThrow(REVEAL_START_TIME));
revealStartTime = revealStartTime > 0 ? Math.min(proposedStartTime, revealStartTime) : proposedStartTime;
revealStartTime = Math.min(revealStartTime, System.currentTimeMillis());
ContentValues values = new ContentValues();
values.put(REVEAL_START_TIME, revealStartTime);
expirationInfos.add(new RevealExpirationInfo(id, receiveTime, revealStartTime, revealDuration));
db.update(TABLE_NAME, values, ID_WHERE, new String[] { String.valueOf(id) });
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
}
}
}
return expirationInfos;
}
public void markAsNotified(long id) { public void markAsNotified(long id) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
@ -668,7 +613,7 @@ public class MmsDatabase extends MessagingDatabase {
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
long revealDuration = cursor.getLong(cursor.getColumnIndexOrThrow(REVEAL_DURATION)); boolean viewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(VIEW_ONCE)) == 1;
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId); int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
@ -715,12 +660,12 @@ public class MmsDatabase extends MessagingDatabase {
} }
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, 0, quote, contacts, previews); return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, false, quote, contacts, previews);
} else if (Types.isExpirationTimerUpdate(outboxType)) { } else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn); return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
} }
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, revealDuration, distributionType, quote, contacts, previews, networkFailures, mismatches); OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews, networkFailures, mismatches);
if (Types.isSecureType(outboxType)) { if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message); return new OutgoingSecureMediaMessage(message);
@ -824,7 +769,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(READ, 1); contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT)); contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
contentValues.put(EXPIRES_IN, request.getExpiresIn()); contentValues.put(EXPIRES_IN, request.getExpiresIn());
contentValues.put(REVEAL_DURATION, request.getRevealDuration()); contentValues.put(VIEW_ONCE, request.isViewOnce());
List<Attachment> attachments = new LinkedList<>(); List<Attachment> attachments = new LinkedList<>();
@ -892,7 +837,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(PART_COUNT, retrieved.getAttachments().size()); contentValues.put(PART_COUNT, retrieved.getAttachments().size());
contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId()); contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
contentValues.put(EXPIRES_IN, retrieved.getExpiresIn()); contentValues.put(EXPIRES_IN, retrieved.getExpiresIn());
contentValues.put(REVEAL_DURATION, retrieved.getRevealDuration()); contentValues.put(VIEW_ONCE, retrieved.isViewOnce() ? 1 : 0);
contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0); contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0);
contentValues.put(UNIDENTIFIED, retrieved.isUnidentified()); contentValues.put(UNIDENTIFIED, retrieved.isUnidentified());
@ -1048,7 +993,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn()); contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(REVEAL_DURATION, message.getRevealDuration()); contentValues.put(VIEW_ONCE, message.isViewOnce());
contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize()); contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize());
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum());
@ -1307,34 +1252,31 @@ public class MmsDatabase extends MessagingDatabase {
database.delete(TABLE_NAME, null, null); database.delete(TABLE_NAME, null, null);
} }
public @Nullable RevealExpirationInfo getNearestExpiringRevealableMessage() { public @Nullable
ViewOnceExpirationInfo getNearestExpiringViewOnceMessage() {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
RevealExpirationInfo info = null; ViewOnceExpirationInfo info = null;
long nearestExpiration = Long.MAX_VALUE; long nearestExpiration = Long.MAX_VALUE;
String query = "SELECT " + String query = "SELECT " +
TABLE_NAME + "." + ID + ", " + TABLE_NAME + "." + ID + ", " +
REVEAL_DURATION + ", " + VIEW_ONCE + ", " +
REVEAL_START_TIME + ", " +
DATE_RECEIVED + " " + DATE_RECEIVED + " " +
"FROM " + TABLE_NAME + " INNER JOIN " + AttachmentDatabase.TABLE_NAME + " " + "FROM " + TABLE_NAME + " INNER JOIN " + AttachmentDatabase.TABLE_NAME + " " +
"ON " + TABLE_NAME + "." + ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " " + "ON " + TABLE_NAME + "." + ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " " +
"WHERE " + "WHERE " +
REVEAL_DURATION + " > 0 AND " + VIEW_ONCE + " > 0 AND " +
"(" + AttachmentDatabase.DATA + " NOT NULL OR " + AttachmentDatabase.TRANSFER_STATE + " != ?)"; "(" + AttachmentDatabase.DATA + " NOT NULL OR " + AttachmentDatabase.TRANSFER_STATE + " != ?)";
String[] args = new String[] { String.valueOf(AttachmentDatabase.TRANSFER_PROGRESS_DONE) }; String[] args = new String[] { String.valueOf(AttachmentDatabase.TRANSFER_PROGRESS_DONE) };
try (Cursor cursor = db.rawQuery(query, args)) { try (Cursor cursor = db.rawQuery(query, args)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long revealDuration = cursor.getLong(cursor.getColumnIndexOrThrow(REVEAL_DURATION));
long revealStartTime = cursor.getLong(cursor.getColumnIndexOrThrow(REVEAL_START_TIME));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(DATE_RECEIVED)); long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(DATE_RECEIVED));
long expiresAt = revealStartTime > 0 ? revealStartTime + revealDuration long expiresAt = dateReceived + ViewOnceUtil.MAX_LIFESPAN;
: dateReceived + RevealableUtil.MAX_LIFESPAN;
if (info == null || expiresAt < nearestExpiration) { if (info == null || expiresAt < nearestExpiration) {
info = new RevealExpirationInfo(id, dateReceived, revealStartTime, revealDuration); info = new ViewOnceExpirationInfo(id, dateReceived);
nearestExpiration = expiresAt; nearestExpiration = expiresAt;
} }
} }
@ -1442,8 +1384,7 @@ public class MmsDatabase extends MessagingDatabase {
message.getSubscriptionId(), message.getSubscriptionId(),
message.getExpiresIn(), message.getExpiresIn(),
System.currentTimeMillis(), System.currentTimeMillis(),
message.getRevealDuration(), message.isViewOnce(),
0,
0, 0,
message.getOutgoingQuote() != null ? message.getOutgoingQuote() != null ?
new Quote(message.getOutgoingQuote().getId(), new Quote(message.getOutgoingQuote().getId(),
@ -1541,8 +1482,7 @@ public class MmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1; boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1;
long revealDuration = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_DURATION)); boolean isViewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) == 1;
long revealStartTime = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_START_TIME));
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
readReceiptCount = 0; readReceiptCount = 0;
@ -1563,8 +1503,7 @@ public class MmsDatabase extends MessagingDatabase {
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, addressDeviceId, dateSent, dateReceived, deliveryReceiptCount,
threadId, body, slideDeck, partCount, box, mismatches, threadId, body, slideDeck, partCount, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted, networkFailures, subscriptionId, expiresIn, expireStarted,
revealDuration, revealStartTime, isViewOnce, readReceiptCount, quote, contacts, previews, unidentified);
readReceiptCount, quote, contacts, previews, unidentified);
} }
private Recipient getRecipientFor(String serialized) { private Recipient getRecipientFor(String serialized) {

View File

@ -71,8 +71,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT, MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS, MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS, MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION, MmsDatabase.VIEW_ONCE};
MmsDatabase.REVEAL_START_TIME};
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
@ -273,8 +272,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT, MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS, MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS, MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION, MmsDatabase.VIEW_ONCE};
MmsDatabase.REVEAL_START_TIME};
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT, String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
@ -301,8 +299,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT, MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS, MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS, MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION, MmsDatabase.VIEW_ONCE};
MmsDatabase.REVEAL_START_TIME};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
@ -373,8 +370,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsDatabase.QUOTE_ATTACHMENT); mmsColumnsPresent.add(MmsDatabase.QUOTE_ATTACHMENT);
mmsColumnsPresent.add(MmsDatabase.SHARED_CONTACTS); mmsColumnsPresent.add(MmsDatabase.SHARED_CONTACTS);
mmsColumnsPresent.add(MmsDatabase.LINK_PREVIEWS); mmsColumnsPresent.add(MmsDatabase.LINK_PREVIEWS);
mmsColumnsPresent.add(MmsDatabase.REVEAL_DURATION); mmsColumnsPresent.add(MmsDatabase.VIEW_ONCE);
mmsColumnsPresent.add(MmsDatabase.REVEAL_START_TIME);
Set<String> smsColumnsPresent = new HashSet<>(); Set<String> smsColumnsPresent = new HashSet<>();
smsColumnsPresent.add(MmsSmsColumns.ID); smsColumnsPresent.add(MmsSmsColumns.ID);

View File

@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
@ -630,7 +629,7 @@ public class ThreadDatabase extends Database {
SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck(); SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
Slide thumbnail = slideDeck.getThumbnailSlide(); Slide thumbnail = slideDeck.getThumbnailSlide();
if (thumbnail != null && ((MmsMessageRecord) record).getRevealDuration() == 0) { if (thumbnail != null && !((MmsMessageRecord) record).isViewOnce()) {
return thumbnail.getThumbnailUri(); return thumbnail.getThumbnailUri();
} }
@ -650,7 +649,7 @@ public class ThreadDatabase extends Database {
} }
private @Nullable Extra getExtrasFor(MessageRecord record) { private @Nullable Extra getExtrasFor(MessageRecord record) {
if (record.isMms() && ((MmsMessageRecord) record).getRevealDuration() > 0) { if (record.isMms() && ((MmsMessageRecord) record).isViewOnce()) {
return Extra.forRevealableMessage(); return Extra.forRevealableMessage();
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) {
return Extra.forSticker(); return Extra.forSticker();

View File

@ -67,11 +67,13 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int JOBMANAGER_STRIKES_BACK = 20; private static final int JOBMANAGER_STRIKES_BACK = 20;
private static final int STICKERS = 21; private static final int STICKERS = 21;
private static final int REVEALABLE_MESSAGES = 22; private static final int REVEALABLE_MESSAGES = 22;
private static final int VIEW_ONCE_ONLY = 23;
private static final int DATABASE_VERSION = 22; private static final int DATABASE_VERSION = 23;
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
private final DatabaseSecret databaseSecret; private final DatabaseSecret databaseSecret;
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) { public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
@ -471,6 +473,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE thread ADD COLUMN snippet_extras TEXT DEFAULT NULL"); db.execSQL("ALTER TABLE thread ADD COLUMN snippet_extras TEXT DEFAULT NULL");
} }
if (oldVersion < VIEW_ONCE_ONLY) {
db.execSQL("UPDATE mms SET reveal_duration = 1 WHERE reveal_duration > 0");
db.execSQL("UPDATE mms SET reveal_start_time = 0");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@ -55,13 +55,13 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
List<IdentityKeyMismatch> mismatches, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> failures, int subscriptionId, List<NetworkFailure> failures, int subscriptionId,
long expiresIn, long expireStarted, long expiresIn, long expireStarted,
long revealDuration, long revealStartTime, int readReceiptCount, boolean viewOnce, int readReceiptCount,
@Nullable Quote quote, @NonNull List<Contact> contacts, @Nullable Quote quote, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> linkPreviews, boolean unidentified) @NonNull List<LinkPreview> linkPreviews, boolean unidentified)
{ {
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
subscriptionId, expiresIn, expireStarted, revealDuration, revealStartTime, slideDeck, subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck,
readReceiptCount, quote, contacts, linkPreviews, unidentified); readReceiptCount, quote, contacts, linkPreviews, unidentified);
this.partCount = partCount; this.partCount = partCount;
} }

View File

@ -22,25 +22,23 @@ public abstract class MmsMessageRecord extends MessageRecord {
private final @NonNull List<Contact> contacts = new LinkedList<>(); private final @NonNull List<Contact> contacts = new LinkedList<>();
private final @NonNull List<LinkPreview> linkPreviews = new LinkedList<>(); private final @NonNull List<LinkPreview> linkPreviews = new LinkedList<>();
private final long revealDuration; private final boolean viewOnce;
private final long revealStartTime;
MmsMessageRecord(long id, String body, Recipient conversationRecipient, MmsMessageRecord(long id, String body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, long dateSent, Recipient individualRecipient, int recipientDeviceId, long dateSent,
long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount, long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
long type, List<IdentityKeyMismatch> mismatches, long type, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn, List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn,
long expireStarted, long revealDuration, long revealStartTime, long expireStarted, boolean viewOnce,
@NonNull SlideDeck slideDeck, int readReceiptCount, @NonNull SlideDeck slideDeck, int readReceiptCount,
@Nullable Quote quote, @NonNull List<Contact> contacts, @Nullable Quote quote, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> linkPreviews, boolean unidentified) @NonNull List<LinkPreview> linkPreviews, boolean unidentified)
{ {
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified); super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified);
this.slideDeck = slideDeck; this.slideDeck = slideDeck;
this.quote = quote; this.quote = quote;
this.revealDuration = revealDuration; this.viewOnce = viewOnce;
this.revealStartTime = revealStartTime;
this.contacts.addAll(contacts); this.contacts.addAll(contacts);
this.linkPreviews.addAll(linkPreviews); this.linkPreviews.addAll(linkPreviews);
@ -83,11 +81,7 @@ public abstract class MmsMessageRecord extends MessageRecord {
return linkPreviews; return linkPreviews;
} }
public long getRevealDuration() { public boolean isViewOnce() {
return revealDuration; return viewOnce;
}
public long getRevealStartTime() {
return revealStartTime;
} }
} }

View File

@ -57,7 +57,7 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
super(id, "", conversationRecipient, individualRecipient, recipientDeviceId, super(id, "", conversationRecipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId, new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId,
0, 0, 0, 0, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false); 0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false);
this.contentLocation = contentLocation; this.contentLocation = contentLocation;
this.messageSize = messageSize; this.messageSize = messageSize;

View File

@ -121,7 +121,7 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
return BlobProvider.getInstance() return BlobProvider.getInstance()
.forData(data) .forData(data)
.withMimeType(MediaUtil.IMAGE_GIF) .withMimeType(MediaUtil.IMAGE_GIF)
.createForSingleSessionOnDisk(GiphyActivity.this, e -> Log.w(TAG, "Failed to write to disk.", e)); .createForSingleSessionOnDisk(GiphyActivity.this);
} catch (InterruptedException | ExecutionException | IOException e) { } catch (InterruptedException | ExecutionException | IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
return null; return null;

View File

@ -115,7 +115,7 @@ public class GroupManager {
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null); avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null);
} }
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, 0, null, Collections.emptyList(), Collections.emptyList()); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null); long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadId);

View File

@ -212,7 +212,7 @@ public class GroupMessageProcessor {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false)); Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false));
Recipient recipient = Recipient.from(context, addres, false); Recipient recipient = Recipient.from(context, addres, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, 0, null, Collections.emptyList(), Collections.emptyList()); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null); long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
@ -222,7 +222,7 @@ public class GroupMessageProcessor {
} else { } else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray()); String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, 0, content.isNeedsReceipt()); IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@ -41,10 +41,10 @@ public final class JobManagerFactories {
put(MultiDeviceGroupUpdateJob.KEY, new MultiDeviceGroupUpdateJob.Factory()); put(MultiDeviceGroupUpdateJob.KEY, new MultiDeviceGroupUpdateJob.Factory());
put(MultiDeviceProfileKeyUpdateJob.KEY, new MultiDeviceProfileKeyUpdateJob.Factory()); put(MultiDeviceProfileKeyUpdateJob.KEY, new MultiDeviceProfileKeyUpdateJob.Factory());
put(MultiDeviceReadUpdateJob.KEY, new MultiDeviceReadUpdateJob.Factory()); put(MultiDeviceReadUpdateJob.KEY, new MultiDeviceReadUpdateJob.Factory());
put(MultiDeviceRevealUpdateJob.KEY, new MultiDeviceRevealUpdateJob.Factory());
put(MultiDeviceStickerPackOperationJob.KEY, new MultiDeviceStickerPackOperationJob.Factory()); put(MultiDeviceStickerPackOperationJob.KEY, new MultiDeviceStickerPackOperationJob.Factory());
put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory()); put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory());
put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory()); put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory());
put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory());
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory()); put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
put(PushDecryptJob.KEY, new PushDecryptJob.Factory()); put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory()); put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());

View File

@ -247,7 +247,7 @@ public class MmsDownloadJob extends BaseJob {
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true))); group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true)));
} }
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, 0, false); IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false, false);
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId); Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {

View File

@ -16,25 +16,25 @@ import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.MessageTimerReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class MultiDeviceRevealUpdateJob extends BaseJob { public class MultiDeviceViewOnceOpenJob extends BaseJob {
public static final String KEY = "MultiDeviceRevealUpdateJob"; public static final String KEY = "MultiDeviceRevealUpdateJob";
private static final String TAG = MultiDeviceRevealUpdateJob.class.getSimpleName(); private static final String TAG = Log.tag(MultiDeviceViewOnceOpenJob.class);
private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_MESSAGE_ID = "message_id";
private SerializableSyncMessageId messageId; private SerializableSyncMessageId messageId;
public MultiDeviceRevealUpdateJob(SyncMessageId messageId) { public MultiDeviceViewOnceOpenJob(SyncMessageId messageId) {
this(new Parameters.Builder() this(new Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
@ -43,7 +43,7 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
messageId); messageId);
} }
private MultiDeviceRevealUpdateJob(@NonNull Parameters parameters, @NonNull SyncMessageId syncMessageId) { private MultiDeviceViewOnceOpenJob(@NonNull Parameters parameters, @NonNull SyncMessageId syncMessageId) {
super(parameters); super(parameters);
this.messageId = new SerializableSyncMessageId(syncMessageId.getAddress().toPhoneString(), syncMessageId.getTimetamp()); this.messageId = new SerializableSyncMessageId(syncMessageId.getAddress().toPhoneString(), syncMessageId.getTimetamp());
} }
@ -74,9 +74,9 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
} }
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
MessageTimerReadMessage timerMessage = new MessageTimerReadMessage(messageId.sender, messageId.timestamp); ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(messageId.sender, messageId.timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forMessageTimerRead(timerMessage), UnidentifiedAccessUtil.getAccessForSync(context)); messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
} }
@Override @Override
@ -105,9 +105,9 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
} }
} }
public static final class Factory implements Job.Factory<MultiDeviceRevealUpdateJob> { public static final class Factory implements Job.Factory<MultiDeviceViewOnceOpenJob> {
@Override @Override
public @NonNull MultiDeviceRevealUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull MultiDeviceViewOnceOpenJob create(@NonNull Parameters parameters, @NonNull Data data) {
SerializableSyncMessageId messageId; SerializableSyncMessageId messageId;
try { try {
@ -118,7 +118,7 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(messageId.sender), messageId.timestamp); SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(messageId.sender), messageId.timestamp);
return new MultiDeviceRevealUpdateJob(parameters, syncMessageId); return new MultiDeviceViewOnceOpenJob(parameters, syncMessageId);
} }
} }
} }

View File

@ -80,8 +80,6 @@ import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.revealable.RevealExpirationInfo;
import org.thoughtcrime.securesms.revealable.RevealableMessageManager;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage; import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
@ -112,13 +110,13 @@ import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.messages.multidevice.MessageTimerReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
@ -283,7 +281,7 @@ public class PushDecryptJob extends BaseJob {
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get()); if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get()); else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp()); else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
else if (syncMessage.getMessageTimerRead().isPresent()) handleSynchronizeMessageTimerReadMessage(syncMessage.getMessageTimerRead().get(), content.getTimestamp()); else if (syncMessage.getViewOnceOpen().isPresent()) handleSynchronizeViewOnceOpenMessage(syncMessage.getViewOnceOpen().get(), content.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get()); else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
else Log.w(TAG, "Contains no known sync types..."); else Log.w(TAG, "Contains no known sync types...");
@ -431,7 +429,7 @@ public class PushDecryptJob extends BaseJob {
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
content.getSenderDevice(), content.getSenderDevice(),
content.getTimestamp(), content.getTimestamp(),
"", Optional.absent(), 0, 0, "", Optional.absent(), 0,
content.isNeedsReceipt()); content.isNeedsReceipt());
Long threadId; Long threadId;
@ -515,7 +513,7 @@ public class PushDecryptJob extends BaseJob {
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()), IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true, message.getExpiresInSeconds() * 1000L, true,
0, false,
content.isNeedsReceipt(), content.isNeedsReceipt(),
Optional.absent(), Optional.absent(),
message.getGroupInfo(), message.getGroupInfo(),
@ -676,11 +674,14 @@ public class PushDecryptJob extends BaseJob {
MessageNotifier.updateNotification(context); MessageNotifier.updateNotification(context);
} }
private void handleSynchronizeMessageTimerReadMessage(@NonNull MessageTimerReadMessage timerMessage, long envelopeTimestamp) { private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
SyncMessageId messageId = new SyncMessageId(Address.fromExternal(context, timerMessage.getSender()), timerMessage.getTimestamp()); Address author = Address.fromExternal(context, openMessage.getSender());
long timestamp = openMessage.getTimestamp();
MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author);
DatabaseFactory.getMmsDatabase(context).markRevealStarted(messageId, envelopeTimestamp); if (record != null && record.isMms()) {
ApplicationContext.getInstance(context).getRevealableMessageManager().scheduleIfNecessary(); DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(record.getId());
}
MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
MessageNotifier.cancelDelayedNotifications(); MessageNotifier.cancelDelayedNotifications();
@ -707,7 +708,7 @@ public class PushDecryptJob extends BaseJob {
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()), IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false, message.getExpiresInSeconds() * 1000L, false,
message.getMessageTimerInSeconds() * 1000, message.isViewOnce(),
content.isNeedsReceipt(), content.isNeedsReceipt(),
message.getBody(), message.getBody(),
message.getGroupInfo(), message.getGroupInfo(),
@ -747,8 +748,8 @@ public class PushDecryptJob extends BaseJob {
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
if (message.getMessageTimerInSeconds() > 0) { if (message.isViewOnce()) {
ApplicationContext.getInstance(context).getRevealableMessageManager().scheduleIfNecessary(); ApplicationContext.getInstance(context).getViewOnceMessageManager().scheduleIfNecessary();
} }
} }
} }
@ -780,8 +781,8 @@ public class PushDecryptJob extends BaseJob {
Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker()); Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker());
Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts()); Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts());
Optional<List<LinkPreview>> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or("")); Optional<List<LinkPreview>> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or(""));
long messageTimer = message.getMessage().getMessageTimerInSeconds() * 1000; boolean viewOnce = message.getMessage().isViewOnce();
List<Attachment> syncAttachments = messageTimer == 0 ? PointerAttachment.forPointers(message.getMessage().getAttachments()) : Collections.emptyList(); List<Attachment> syncAttachments = viewOnce ? Collections.emptyList() : PointerAttachment.forPointers(message.getMessage().getAttachments());
if (sticker.isPresent()) { if (sticker.isPresent()) {
syncAttachments.add(sticker.get()); syncAttachments.add(sticker.get());
@ -791,7 +792,7 @@ public class PushDecryptJob extends BaseJob {
syncAttachments, syncAttachments,
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getMessage().getExpiresInSeconds() * 1000, message.getMessage().getExpiresInSeconds() * 1000,
messageTimer, viewOnce,
ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(), ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(),
sharedContacts.or(Collections.emptyList()), sharedContacts.or(Collections.emptyList()),
previews.or(Collections.emptyList()), previews.or(Collections.emptyList()),
@ -921,7 +922,6 @@ public class PushDecryptJob extends BaseJob {
message.getTimestamp(), body, message.getTimestamp(), body,
message.getGroupInfo(), message.getGroupInfo(),
message.getExpiresInSeconds() * 1000L, message.getExpiresInSeconds() * 1000L,
message.getMessageTimerInSeconds() * 1000L,
content.isNeedsReceipt()); content.isNeedsReceipt());
textMessage = new IncomingEncryptedMessage(textMessage, body); textMessage = new IncomingEncryptedMessage(textMessage, body);
@ -956,7 +956,7 @@ public class PushDecryptJob extends BaseJob {
long messageId; long messageId;
if (isGroup) { if (isGroup) {
OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getTimestamp(), -1, expiresInMillis, 0, ThreadDatabase.DistributionTypes.DEFAULT, null, Collections.emptyList(), Collections.emptyList()); OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getTimestamp(), -1, expiresInMillis, false, ThreadDatabase.DistributionTypes.DEFAULT, null, Collections.emptyList(), Collections.emptyList());
outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage);
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null); messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
@ -1195,7 +1195,7 @@ public class PushDecryptJob extends BaseJob {
} }
private boolean isInvalidMessage(@NonNull SignalServiceDataMessage message) { private boolean isInvalidMessage(@NonNull SignalServiceDataMessage message) {
if (message.getMessageTimerInSeconds() > 0) { if (message.isViewOnce()) {
return !message.getAttachments().isPresent() || return !message.getAttachments().isPresent() ||
message.getAttachments().get().size() != 1 || message.getAttachments().get().size() != 1 ||
!MediaUtil.isImageType(message.getAttachments().get().get(0).getContentType().toLowerCase()); !MediaUtil.isImageType(message.getAttachments().get().get(0).getContentType().toLowerCase());
@ -1229,7 +1229,7 @@ public class PushDecryptJob extends BaseJob {
if (message.isMms()) { if (message.isMms()) {
MmsMessageRecord mmsMessage = (MmsMessageRecord) message; MmsMessageRecord mmsMessage = (MmsMessageRecord) message;
if (mmsMessage.getRevealDuration() == 0) { if (!mmsMessage.isViewOnce()) {
attachments = mmsMessage.getSlideDeck().asAttachments(); attachments = mmsMessage.getSlideDeck().asAttachments();
if (attachments.isEmpty()) { if (attachments.isEmpty()) {
@ -1335,7 +1335,7 @@ public class PushDecryptJob extends BaseJob {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender), IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender),
senderDevice, timestamp, "", senderDevice, timestamp, "",
group, 0, 0, false); group, 0, false);
textMessage = new IncomingEncryptedMessage(textMessage, ""); textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage); return database.insertMessageInbox(textMessage);

View File

@ -195,11 +195,8 @@ public class PushGroupSendJob extends PushSendJob {
.scheduleDeletion(messageId, true, message.getExpiresIn()); .scheduleDeletion(messageId, true, message.getExpiresIn());
} }
if (message.getRevealDuration() > 0) { if (message.isViewOnce()) {
database.markRevealStarted(messageId); DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(messageId);
ApplicationContext.getInstance(context)
.getRevealableMessageManager()
.scheduleIfNecessary();
} }
} else if (!networkFailures.isEmpty()) { } else if (!networkFailures.isEmpty()) {
throw new RetryLaterException(); throw new RetryLaterException();
@ -269,7 +266,7 @@ public class PushGroupSendJob extends PushSendJob {
.withAttachments(attachmentPointers) .withAttachments(attachmentPointers)
.withBody(message.getBody()) .withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
.withMessageTimer((int)(message.getRevealDuration() / 1000)) .withViewOnce(message.isViewOnce())
.asExpirationUpdate(message.isExpirationUpdate()) .asExpirationUpdate(message.isExpirationUpdate())
.withProfileKey(profileKey.orNull()) .withProfileKey(profileKey.orNull())
.withQuote(quote.orNull()) .withQuote(quote.orNull())

View File

@ -164,11 +164,8 @@ public class PushMediaSendJob extends PushSendJob {
expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn());
} }
if (message.getRevealDuration() > 0) { if (message.isViewOnce()) {
database.markRevealStarted(messageId); DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(messageId);
ApplicationContext.getInstance(context)
.getRevealableMessageManager()
.scheduleIfNecessary();
} }
log(TAG, "Sent message: " + messageId); log(TAG, "Sent message: " + messageId);
@ -230,7 +227,7 @@ public class PushMediaSendJob extends PushSendJob {
.withAttachments(serviceAttachments) .withAttachments(serviceAttachments)
.withTimestamp(message.getSentTimeMillis()) .withTimestamp(message.getSentTimeMillis())
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
.withMessageTimer((int) message.getRevealDuration() / 1000) .withViewOnce(message.isViewOnce())
.withProfileKey(profileKey.orNull()) .withProfileKey(profileKey.orNull())
.withQuote(quote.orNull()) .withQuote(quote.orNull())
.withSticker(sticker.orNull()) .withSticker(sticker.orNull())

View File

@ -50,7 +50,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.imageeditor.model.EditorModel; import org.thoughtcrime.securesms.imageeditor.model.EditorModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
import org.thoughtcrime.securesms.mediasend.MediaSendViewModel.RevealState; import org.thoughtcrime.securesms.mediasend.MediaSendViewModel.ViewOnceState;
import org.thoughtcrime.securesms.mms.GifSlide; import org.thoughtcrime.securesms.mms.GifSlide;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.ImageSlide;
@ -101,10 +101,10 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
{ {
private static final String TAG = MediaSendActivity.class.getSimpleName(); private static final String TAG = MediaSendActivity.class.getSimpleName();
public static final String EXTRA_MEDIA = "media"; public static final String EXTRA_MEDIA = "media";
public static final String EXTRA_MESSAGE = "message"; public static final String EXTRA_MESSAGE = "message";
public static final String EXTRA_TRANSPORT = "transport"; public static final String EXTRA_TRANSPORT = "transport";
public static final String EXTRA_REVEAL_DURATION = "reveal_duration"; public static final String EXTRA_VIEW_ONCE = "view_once";
private static final String KEY_ADDRESS = "address"; private static final String KEY_ADDRESS = "address";
@ -391,7 +391,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Uri uri = BlobProvider.getInstance() Uri uri = BlobProvider.getInstance()
.forData(data) .forData(data)
.withMimeType(MediaUtil.IMAGE_JPEG) .withMimeType(MediaUtil.IMAGE_JPEG)
.createForSingleSessionOnDisk(this, e -> Log.w(TAG, "Failed to write to disk.", e)); .createForSingleSessionOnDisk(this);
return new Media(uri, return new Media(uri,
MediaUtil.IMAGE_JPEG, MediaUtil.IMAGE_JPEG,
System.currentTimeMillis(), System.currentTimeMillis(),
@ -527,14 +527,14 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
if (state == null) return; if (state == null) return;
hud.setVisibility(state.isHudVisible() ? View.VISIBLE : View.GONE); hud.setVisibility(state.isHudVisible() ? View.VISIBLE : View.GONE);
composeContainer.setVisibility(state.isComposeVisible() ? View.VISIBLE : (state.getRevealState() == RevealState.GONE ? View.GONE : View.INVISIBLE)); composeContainer.setVisibility(state.isComposeVisible() ? View.VISIBLE : (state.getViewOnceState() == ViewOnceState.GONE ? View.GONE : View.INVISIBLE));
captionText.setVisibility(state.isCaptionVisible() ? View.VISIBLE : View.GONE); captionText.setVisibility(state.isCaptionVisible() ? View.VISIBLE : View.GONE);
int captionBackground; int captionBackground;
if (state.getRailState() == MediaSendViewModel.RailState.VIEWABLE) { if (state.getRailState() == MediaSendViewModel.RailState.VIEWABLE) {
captionBackground = R.color.core_grey_90; captionBackground = R.color.core_grey_90;
} else if (state.getRevealState() == RevealState.ENABLED) { } else if (state.getViewOnceState() == ViewOnceState.ENABLED) {
captionBackground = 0; captionBackground = 0;
} else { } else {
captionBackground = R.color.transparent_black_70; captionBackground = R.color.transparent_black_70;
@ -572,7 +572,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
break; break;
} }
switch (state.getRevealState()) { switch (state.getViewOnceState()) {
case ENABLED: case ENABLED:
revealButton.setVisibility(View.VISIBLE); revealButton.setVisibility(View.VISIBLE);
revealButton.setImageResource(R.drawable.ic_view_once_32); revealButton.setImageResource(R.drawable.ic_view_once_32);
@ -815,7 +815,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Uri uri = BlobProvider.getInstance() Uri uri = BlobProvider.getInstance()
.forData(outputStream.toByteArray()) .forData(outputStream.toByteArray())
.withMimeType(MediaUtil.IMAGE_JPEG) .withMimeType(MediaUtil.IMAGE_JPEG)
.createForSingleSessionOnDisk(context, e -> Log.w(TAG, "Failed to write to disk.", e)); .createForSingleSessionOnDisk(context);
Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), media.getBucketId(), media.getCaption()); Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), media.getBucketId(), media.getCaption());
@ -859,9 +859,9 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Intent intent = new Intent(); Intent intent = new Intent();
intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList); intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList);
intent.putExtra(EXTRA_MESSAGE, viewModel.getRevealDuration() == 0 ? message : ""); intent.putExtra(EXTRA_MESSAGE, viewModel.isViewOnce() ? "" : message);
intent.putExtra(EXTRA_TRANSPORT, transport); intent.putExtra(EXTRA_TRANSPORT, transport);
intent.putExtra(EXTRA_REVEAL_DURATION, viewModel.getRevealDuration()); intent.putExtra(EXTRA_VIEW_ONCE, viewModel.isViewOnce());
setResult(RESULT_OK, intent); setResult(RESULT_OK, intent);
} else { } else {
@ -885,7 +885,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
System.currentTimeMillis(), System.currentTimeMillis(),
-1, -1,
recipient.getExpireMessages() * 1000, recipient.getExpireMessages() * 1000,
viewModel.getRevealDuration(), viewModel.isViewOnce(),
ThreadDatabase.DistributionTypes.DEFAULT, ThreadDatabase.DistributionTypes.DEFAULT,
null, null,
Collections.emptyList(), Collections.emptyList(),

View File

@ -61,12 +61,12 @@ class MediaSendViewModel extends ViewModel {
private boolean isSms; private boolean isSms;
private Optional<Media> lastCameraCapture; private Optional<Media> lastCameraCapture;
private boolean hudVisible; private boolean hudVisible;
private boolean composeVisible; private boolean composeVisible;
private boolean captionVisible; private boolean captionVisible;
private ButtonState buttonState; private ButtonState buttonState;
private RailState railState; private RailState railState;
private RevealState revealState; private ViewOnceState viewOnceState;
private @Nullable Recipient recipient; private @Nullable Recipient recipient;
@ -86,7 +86,7 @@ class MediaSendViewModel extends ViewModel {
this.body = ""; this.body = "";
this.buttonState = ButtonState.GONE; this.buttonState = ButtonState.GONE;
this.railState = RailState.GONE; this.railState = RailState.GONE;
this.revealState = RevealState.GONE; this.viewOnceState = ViewOnceState.GONE;
this.page = Page.UNKNOWN; this.page = Page.UNKNOWN;
position.setValue(-1); position.setValue(-1);
@ -174,7 +174,7 @@ class MediaSendViewModel extends ViewModel {
captionVisible = false; captionVisible = false;
buttonState = ButtonState.COUNT; buttonState = ButtonState.COUNT;
railState = RailState.VIEWABLE; railState = RailState.VIEWABLE;
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
hudState.setValue(buildHudState()); hudState.setValue(buildHudState());
} }
@ -182,19 +182,19 @@ class MediaSendViewModel extends ViewModel {
void onImageEditorStarted() { void onImageEditorStarted() {
page = Page.EDITOR; page = Page.EDITOR;
hudVisible = true; hudVisible = true;
composeVisible = revealState != RevealState.ENABLED; composeVisible = viewOnceState != ViewOnceState.ENABLED;
captionVisible = getSelectedMediaOrDefault().size() > 1 || (getSelectedMediaOrDefault().size() > 0 && getSelectedMediaOrDefault().get(0).getCaption().isPresent()); captionVisible = getSelectedMediaOrDefault().size() > 1 || (getSelectedMediaOrDefault().size() > 0 && getSelectedMediaOrDefault().get(0).getCaption().isPresent());
buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE; buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE;
if (revealState == RevealState.GONE && revealSupported()) { if (viewOnceState == ViewOnceState.GONE && viewOnceSupported()) {
// TODO[reveal] // TODO[reveal]
// revealState = TextSecurePreferences.isRevealableMessageEnabled(application) ? RevealState.ENABLED : RevealState.DISABLED; // viewOnceState = TextSecurePreferences.isRevealableMessageEnabled(application) ? ViewOnceState.ENABLED : ViewOnceState.DISABLED;
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
} else if (!revealSupported()) { } else if (!viewOnceSupported()) {
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
} }
railState = !isSms && revealState != RevealState.ENABLED ? RailState.INTERACTIVE : RailState.GONE; railState = !isSms && viewOnceState != ViewOnceState.ENABLED ? RailState.INTERACTIVE : RailState.GONE;
hudState.setValue(buildHudState()); hudState.setValue(buildHudState());
} }
@ -203,10 +203,10 @@ class MediaSendViewModel extends ViewModel {
// TODO: Don't need this? // TODO: Don't need this?
Page previous = page; Page previous = page;
page = Page.CAMERA; page = Page.CAMERA;
hudVisible = false; hudVisible = false;
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
buttonState = ButtonState.COUNT; buttonState = ButtonState.COUNT;
List<Media> selected = getSelectedMediaOrDefault(); List<Media> selected = getSelectedMediaOrDefault();
@ -225,7 +225,7 @@ class MediaSendViewModel extends ViewModel {
composeVisible = false; composeVisible = false;
captionVisible = false; captionVisible = false;
buttonState = ButtonState.COUNT; buttonState = ButtonState.COUNT;
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
railState = getSelectedMediaOrDefault().isEmpty() ? RailState.GONE : RailState.VIEWABLE; railState = getSelectedMediaOrDefault().isEmpty() ? RailState.GONE : RailState.VIEWABLE;
lastCameraCapture = Optional.absent(); lastCameraCapture = Optional.absent();
@ -239,7 +239,7 @@ class MediaSendViewModel extends ViewModel {
composeVisible = false; composeVisible = false;
captionVisible = false; captionVisible = false;
buttonState = ButtonState.COUNT; buttonState = ButtonState.COUNT;
revealState = RevealState.GONE; viewOnceState = ViewOnceState.GONE;
railState = getSelectedMediaOrDefault().isEmpty() ? RailState.GONE : RailState.VIEWABLE; railState = getSelectedMediaOrDefault().isEmpty() ? RailState.GONE : RailState.VIEWABLE;
lastCameraCapture = Optional.absent(); lastCameraCapture = Optional.absent();
@ -255,9 +255,9 @@ class MediaSendViewModel extends ViewModel {
void onRevealButtonToggled() { void onRevealButtonToggled() {
hudVisible = true; hudVisible = true;
revealState = revealState == RevealState.ENABLED ? RevealState.DISABLED : RevealState.ENABLED; viewOnceState = viewOnceState == ViewOnceState.ENABLED ? ViewOnceState.DISABLED : ViewOnceState.ENABLED;
composeVisible = revealState != RevealState.ENABLED; composeVisible = viewOnceState != ViewOnceState.ENABLED;
railState = revealState == RevealState.ENABLED || isSms ? RailState.GONE : RailState.INTERACTIVE; railState = viewOnceState == ViewOnceState.ENABLED || isSms ? RailState.GONE : RailState.INTERACTIVE;
captionVisible = false; captionVisible = false;
List<Media> uncaptioned = Stream.of(getSelectedMediaOrDefault()) List<Media> uncaptioned = Stream.of(getSelectedMediaOrDefault())
@ -266,7 +266,7 @@ class MediaSendViewModel extends ViewModel {
selectedMedia.setValue(uncaptioned); selectedMedia.setValue(uncaptioned);
TextSecurePreferences.setIsRevealableMessageEnabled(application, revealState == RevealState.ENABLED); TextSecurePreferences.setIsRevealableMessageEnabled(application, viewOnceState == ViewOnceState.ENABLED);
hudState.setValue(buildHudState()); hudState.setValue(buildHudState());
} }
@ -274,14 +274,14 @@ class MediaSendViewModel extends ViewModel {
void onKeyboardHidden(boolean isSms) { void onKeyboardHidden(boolean isSms) {
if (page != Page.EDITOR) return; if (page != Page.EDITOR) return;
composeVisible = (revealState != RevealState.ENABLED); composeVisible = (viewOnceState != ViewOnceState.ENABLED);
buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE; buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE;
if (isSms) { if (isSms) {
railState = RailState.GONE; railState = RailState.GONE;
captionVisible = false; captionVisible = false;
} else { } else {
railState = revealState != RevealState.ENABLED ? RailState.INTERACTIVE : RailState.GONE; railState = viewOnceState != ViewOnceState.ENABLED ? RailState.INTERACTIVE : RailState.GONE;
if (getSelectedMediaOrDefault().size() > 1 || (getSelectedMediaOrDefault().size() > 0 && getSelectedMediaOrDefault().get(0).getCaption().isPresent())) { if (getSelectedMediaOrDefault().size() > 1 || (getSelectedMediaOrDefault().size() > 0 && getSelectedMediaOrDefault().get(0).getCaption().isPresent())) {
captionVisible = true; captionVisible = true;
@ -296,18 +296,18 @@ class MediaSendViewModel extends ViewModel {
if (isSms) { if (isSms) {
railState = RailState.GONE; railState = RailState.GONE;
composeVisible = (revealState == RevealState.GONE); composeVisible = (viewOnceState == ViewOnceState.GONE);
captionVisible = false; captionVisible = false;
buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE; buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE;
} else { } else {
if (isCaptionFocused) { if (isCaptionFocused) {
railState = revealState != RevealState.ENABLED ? RailState.INTERACTIVE : RailState.GONE; railState = viewOnceState != ViewOnceState.ENABLED ? RailState.INTERACTIVE : RailState.GONE;
composeVisible = false; composeVisible = false;
captionVisible = true; captionVisible = true;
buttonState = ButtonState.GONE; buttonState = ButtonState.GONE;
} else if (isComposeFocused) { } else if (isComposeFocused) {
railState = revealState != RevealState.ENABLED ? RailState.INTERACTIVE : RailState.GONE; railState = viewOnceState != ViewOnceState.ENABLED ? RailState.INTERACTIVE : RailState.GONE;
composeVisible = (revealState != RevealState.ENABLED); composeVisible = (viewOnceState != ViewOnceState.ENABLED);
captionVisible = false; captionVisible = false;
buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE; buttonState = (recipient != null) ? ButtonState.SEND : ButtonState.CONTINUE;
} }
@ -357,7 +357,7 @@ class MediaSendViewModel extends ViewModel {
} }
if (getSelectedMediaOrDefault().size() == 1) { if (getSelectedMediaOrDefault().size() == 1) {
revealState = revealSupported() ? RevealState.DISABLED : RevealState.GONE; viewOnceState = viewOnceSupported() ? ViewOnceState.DISABLED : ViewOnceState.GONE;
} }
hudState.setValue(buildHudState()); hudState.setValue(buildHudState());
@ -448,10 +448,9 @@ class MediaSendViewModel extends ViewModel {
return maxSelection; return maxSelection;
} }
long getRevealDuration() { boolean isViewOnce() {
// TODO[reveal] // TODO[reveal]
// return revealState == RevealState.ENABLED ? RevealableUtil.DURATION : 0; return false;
return 0;
} }
private @NonNull List<Media> getSelectedMediaOrDefault() { private @NonNull List<Media> getSelectedMediaOrDefault() {
@ -473,13 +472,13 @@ class MediaSendViewModel extends ViewModel {
private HudState buildHudState() { private HudState buildHudState() {
// TODO[reveal] // TODO[reveal]
RevealState updatedRevealState = RevealState.GONE; ViewOnceState updatedViewOnceState = ViewOnceState.GONE;
List<Media> selectedMedia = getSelectedMediaOrDefault(); List<Media> selectedMedia = getSelectedMediaOrDefault();
int selectionCount = selectedMedia.size(); int selectionCount = selectedMedia.size();
ButtonState updatedButtonState = buttonState == ButtonState.COUNT && selectionCount == 0 ? ButtonState.GONE : buttonState; ButtonState updatedButtonState = buttonState == ButtonState.COUNT && selectionCount == 0 ? ButtonState.GONE : buttonState;
boolean updatedCaptionVisible = captionVisible && (selectedMedia.size() > 1 || (selectedMedia.size() > 0 && selectedMedia.get(0).getCaption().isPresent())); boolean updatedCaptionVisible = captionVisible && (selectedMedia.size() > 1 || (selectedMedia.size() > 0 && selectedMedia.get(0).getCaption().isPresent()));
return new HudState(hudVisible, composeVisible, updatedCaptionVisible, selectionCount, updatedButtonState, railState, updatedRevealState); return new HudState(hudVisible, composeVisible, updatedCaptionVisible, selectionCount, updatedButtonState, railState, updatedViewOnceState);
} }
private void clearPersistedMedia() { private void clearPersistedMedia() {
@ -489,7 +488,7 @@ class MediaSendViewModel extends ViewModel {
.forEach(uri -> BlobProvider.getInstance().delete(application.getApplicationContext(), uri)); .forEach(uri -> BlobProvider.getInstance().delete(application.getApplicationContext(), uri));
} }
private boolean revealSupported() { private boolean viewOnceSupported() {
return !isSms && (recipient == null || !recipient.isLocalNumber()) && mediaSupportsRevealableMessage(getSelectedMediaOrDefault()); return !isSms && (recipient == null || !recipient.isLocalNumber()) && mediaSupportsRevealableMessage(getSelectedMediaOrDefault());
} }
@ -520,7 +519,7 @@ class MediaSendViewModel extends ViewModel {
INTERACTIVE, VIEWABLE, GONE INTERACTIVE, VIEWABLE, GONE
} }
enum RevealState { enum ViewOnceState {
ENABLED, DISABLED, GONE ENABLED, DISABLED, GONE
} }
@ -532,7 +531,7 @@ class MediaSendViewModel extends ViewModel {
private final int selectionCount; private final int selectionCount;
private final ButtonState buttonState; private final ButtonState buttonState;
private final RailState railState; private final RailState railState;
private final RevealState revealState; private final ViewOnceState viewOnceState;
HudState(boolean hudVisible, HudState(boolean hudVisible,
boolean composeVisible, boolean composeVisible,
@ -540,7 +539,7 @@ class MediaSendViewModel extends ViewModel {
int selectionCount, int selectionCount,
@NonNull ButtonState buttonState, @NonNull ButtonState buttonState,
@NonNull RailState railState, @NonNull RailState railState,
@NonNull RevealState revealState) @NonNull ViewOnceState viewOnceState)
{ {
this.hudVisible = hudVisible; this.hudVisible = hudVisible;
this.composeVisible = composeVisible; this.composeVisible = composeVisible;
@ -548,7 +547,7 @@ class MediaSendViewModel extends ViewModel {
this.selectionCount = selectionCount; this.selectionCount = selectionCount;
this.buttonState = buttonState; this.buttonState = buttonState;
this.railState = railState; this.railState = railState;
this.revealState = revealState; this.viewOnceState = viewOnceState;
} }
public boolean isHudVisible() { public boolean isHudVisible() {
@ -575,9 +574,8 @@ class MediaSendViewModel extends ViewModel {
return hudVisible ? railState : RailState.GONE; return hudVisible ? railState : RailState.GONE;
} }
public @NonNull public @NonNull ViewOnceState getViewOnceState() {
RevealState getRevealState() { return hudVisible ? viewOnceState : ViewOnceState.GONE;
return hudVisible ? revealState : RevealState.GONE;
} }
} }

View File

@ -24,9 +24,9 @@ public class IncomingMediaMessage {
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
private final boolean expirationUpdate; private final boolean expirationUpdate;
private final long revealDuration;
private final QuoteModel quote; private final QuoteModel quote;
private final boolean unidentified; private final boolean unidentified;
private final boolean viewOnce;
private final List<Attachment> attachments = new LinkedList<>(); private final List<Attachment> attachments = new LinkedList<>();
private final List<Contact> sharedContacts = new LinkedList<>(); private final List<Contact> sharedContacts = new LinkedList<>();
@ -40,7 +40,7 @@ public class IncomingMediaMessage {
int subscriptionId, int subscriptionId,
long expiresIn, long expiresIn,
boolean expirationUpdate, boolean expirationUpdate,
long revealDuration, boolean viewOnce,
boolean unidentified) boolean unidentified)
{ {
this.from = from; this.from = from;
@ -51,7 +51,7 @@ public class IncomingMediaMessage {
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate; this.expirationUpdate = expirationUpdate;
this.revealDuration = revealDuration; this.viewOnce = viewOnce;
this.quote = null; this.quote = null;
this.unidentified = unidentified; this.unidentified = unidentified;
@ -63,7 +63,7 @@ public class IncomingMediaMessage {
int subscriptionId, int subscriptionId,
long expiresIn, long expiresIn,
boolean expirationUpdate, boolean expirationUpdate,
long revealDuration, boolean viewOnce,
boolean unidentified, boolean unidentified,
Optional<String> body, Optional<String> body,
Optional<SignalServiceGroup> group, Optional<SignalServiceGroup> group,
@ -80,7 +80,7 @@ public class IncomingMediaMessage {
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate; this.expirationUpdate = expirationUpdate;
this.revealDuration = revealDuration; this.viewOnce = viewOnce;
this.quote = quote.orNull(); this.quote = quote.orNull();
this.unidentified = unidentified; this.unidentified = unidentified;
@ -132,8 +132,8 @@ public class IncomingMediaMessage {
return expiresIn; return expiresIn;
} }
public long getRevealDuration() { public boolean isViewOnce() {
return revealDuration; return viewOnce;
} }
public boolean isGroupMessage() { public boolean isGroupMessage() {

View File

@ -11,7 +11,7 @@ public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage
public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) { public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) {
super(recipient, "", new LinkedList<Attachment>(), sentTimeMillis, super(recipient, "", new LinkedList<Attachment>(), sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, 0, null, Collections.emptyList(), ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, false, null, Collections.emptyList(),
Collections.emptyList()); Collections.emptyList());
} }

View File

@ -24,14 +24,14 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
@NonNull List<Attachment> avatar, @NonNull List<Attachment> avatar,
long sentTimeMillis, long sentTimeMillis,
long expiresIn, long expiresIn,
long revealDuration, boolean viewOnce,
@Nullable QuoteModel quote, @Nullable QuoteModel quote,
@NonNull List<Contact> contacts, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> previews) @NonNull List<LinkPreview> previews)
throws IOException throws IOException
{ {
super(recipient, encodedGroupContext, avatar, sentTimeMillis, super(recipient, encodedGroupContext, avatar, sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, revealDuration, quote, contacts, previews); ThreadDatabase.DistributionTypes.CONVERSATION, expiresIn, viewOnce, quote, contacts, previews);
this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext)); this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext));
} }
@ -41,7 +41,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
@Nullable final Attachment avatar, @Nullable final Attachment avatar,
long sentTimeMillis, long sentTimeMillis,
long expireIn, long expireIn,
long revealDuration, boolean viewOnce,
@Nullable QuoteModel quote, @Nullable QuoteModel quote,
@NonNull List<Contact> contacts, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> previews) @NonNull List<LinkPreview> previews)
@ -49,7 +49,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
super(recipient, Base64.encodeBytes(group.toByteArray()), super(recipient, Base64.encodeBytes(group.toByteArray()),
new LinkedList<Attachment>() {{if (avatar != null) add(avatar);}}, new LinkedList<Attachment>() {{if (avatar != null) add(avatar);}},
System.currentTimeMillis(), System.currentTimeMillis(),
ThreadDatabase.DistributionTypes.CONVERSATION, expireIn, revealDuration, quote, contacts, previews); ThreadDatabase.DistributionTypes.CONVERSATION, expireIn, viewOnce, quote, contacts, previews);
this.group = group; this.group = group;
} }

View File

@ -23,7 +23,7 @@ public class OutgoingMediaMessage {
private final int distributionType; private final int distributionType;
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
private final long revealDuration; private final boolean viewOnce;
private final QuoteModel outgoingQuote; private final QuoteModel outgoingQuote;
private final List<NetworkFailure> networkFailures = new LinkedList<>(); private final List<NetworkFailure> networkFailures = new LinkedList<>();
@ -33,7 +33,7 @@ public class OutgoingMediaMessage {
public OutgoingMediaMessage(Recipient recipient, String message, public OutgoingMediaMessage(Recipient recipient, String message,
List<Attachment> attachments, long sentTimeMillis, List<Attachment> attachments, long sentTimeMillis,
int subscriptionId, long expiresIn, long revealDuration, int subscriptionId, long expiresIn, boolean viewOnce,
int distributionType, int distributionType,
@Nullable QuoteModel outgoingQuote, @Nullable QuoteModel outgoingQuote,
@NonNull List<Contact> contacts, @NonNull List<Contact> contacts,
@ -48,7 +48,7 @@ public class OutgoingMediaMessage {
this.attachments = attachments; this.attachments = attachments;
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
this.revealDuration = revealDuration; this.viewOnce = viewOnce;
this.outgoingQuote = outgoingQuote; this.outgoingQuote = outgoingQuote;
this.contacts.addAll(contacts); this.contacts.addAll(contacts);
@ -59,7 +59,7 @@ public class OutgoingMediaMessage {
public OutgoingMediaMessage(Recipient recipient, SlideDeck slideDeck, String message, public OutgoingMediaMessage(Recipient recipient, SlideDeck slideDeck, String message,
long sentTimeMillis, int subscriptionId, long expiresIn, long sentTimeMillis, int subscriptionId, long expiresIn,
long revealDuration, int distributionType, boolean viewOnce, int distributionType,
@Nullable QuoteModel outgoingQuote, @Nullable QuoteModel outgoingQuote,
@NonNull List<Contact> contacts, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> linkPreviews) @NonNull List<LinkPreview> linkPreviews)
@ -68,7 +68,7 @@ public class OutgoingMediaMessage {
buildMessage(slideDeck, message), buildMessage(slideDeck, message),
slideDeck.asAttachments(), slideDeck.asAttachments(),
sentTimeMillis, subscriptionId, sentTimeMillis, subscriptionId,
expiresIn, revealDuration, distributionType, outgoingQuote, expiresIn, viewOnce, distributionType, outgoingQuote,
contacts, linkPreviews, new LinkedList<>(), new LinkedList<>()); contacts, linkPreviews, new LinkedList<>(), new LinkedList<>());
} }
@ -80,7 +80,7 @@ public class OutgoingMediaMessage {
this.sentTimeMillis = that.sentTimeMillis; this.sentTimeMillis = that.sentTimeMillis;
this.subscriptionId = that.subscriptionId; this.subscriptionId = that.subscriptionId;
this.expiresIn = that.expiresIn; this.expiresIn = that.expiresIn;
this.revealDuration = that.revealDuration; this.viewOnce = that.viewOnce;
this.outgoingQuote = that.outgoingQuote; this.outgoingQuote = that.outgoingQuote;
this.identityKeyMismatches.addAll(that.identityKeyMismatches); this.identityKeyMismatches.addAll(that.identityKeyMismatches);
@ -129,8 +129,8 @@ public class OutgoingMediaMessage {
return expiresIn; return expiresIn;
} }
public long getRevealDuration() { public boolean isViewOnce() {
return revealDuration; return viewOnce;
} }
public @Nullable QuoteModel getOutgoingQuote() { public @Nullable QuoteModel getOutgoingQuote() {

View File

@ -18,12 +18,12 @@ public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
long sentTimeMillis, long sentTimeMillis,
int distributionType, int distributionType,
long expiresIn, long expiresIn,
long revealDuration, boolean viewOnce,
@Nullable QuoteModel quote, @Nullable QuoteModel quote,
@NonNull List<Contact> contacts, @NonNull List<Contact> contacts,
@NonNull List<LinkPreview> previews) @NonNull List<LinkPreview> previews)
{ {
super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, revealDuration, distributionType, quote, contacts, previews, Collections.emptyList(), Collections.emptyList()); super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, viewOnce, distributionType, quote, contacts, previews, Collections.emptyList(), Collections.emptyList());
} }
public OutgoingSecureMediaMessage(OutgoingMediaMessage base) { public OutgoingSecureMediaMessage(OutgoingMediaMessage base) {

View File

@ -76,7 +76,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
if (recipient.isGroupRecipient()) { if (recipient.isGroupRecipient()) {
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, false, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
replyThreadId = MessageSender.send(context, reply, threadId, false, null); replyThreadId = MessageSender.send(context, reply, threadId, false, null);
} else { } else {
Log.w("AndroidAutoReplyReceiver", "Sending regular message "); Log.w("AndroidAutoReplyReceiver", "Sending regular message ");

View File

@ -465,7 +465,7 @@ public class MessageNotifier {
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) { } else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker)); body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker));
slideDeck = ((MmsMessageRecord) record).getSlideDeck(); slideDeck = ((MmsMessageRecord) record).getSlideDeck();
} else if (record.isMms() && ((MmsMessageRecord) record).getRevealDuration() > 0) { } else if (record.isMms() && ((MmsMessageRecord) record).isViewOnce()) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_disappearing_photo)); body = SpanUtil.italic(context.getString(R.string.MessageNotifier_disappearing_photo));
} else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) { } else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message)); body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message));

View File

@ -76,7 +76,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
switch (replyMethod) { switch (replyMethod) {
case GroupMessage: { case GroupMessage: {
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, false, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
threadId = MessageSender.send(context, reply, -1, false, null); threadId = MessageSender.send(context, reply, -1, false, null);
break; break;
} }

View File

@ -25,6 +25,8 @@ import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* Allows for the creation and retrieval of blobs. * Allows for the creation and retrieval of blobs.
@ -173,7 +175,34 @@ public class BlobProvider {
} }
@WorkerThread @WorkerThread
private synchronized @NonNull Uri writeBlobSpecToDisk(@NonNull Context context, @NonNull BlobSpec blobSpec, @Nullable ErrorListener errorListener) throws IOException { private synchronized @NonNull Uri writeBlobSpecToDisk(@NonNull Context context, @NonNull BlobSpec blobSpec)
throws IOException
{
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<IOException> exception = new AtomicReference<>(null);
Uri uri = writeBlobSpecToDiskAsync(context, blobSpec, latch::countDown, exception::set);
try {
latch.await();
} catch (InterruptedException e) {
throw new IOException(e);
}
if (exception.get() != null) {
throw exception.get();
}
return uri;
}
@WorkerThread
private synchronized @NonNull Uri writeBlobSpecToDiskAsync(@NonNull Context context,
@NonNull BlobSpec blobSpec,
@Nullable SuccessListener successListener,
@Nullable ErrorListener errorListener)
throws IOException
{
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
String directory = getDirectory(blobSpec.getStorageType()); String directory = getDirectory(blobSpec.getStorageType());
File outputFile = new File(getOrCreateCacheDirectory(context, directory), buildFileName(blobSpec.id)); File outputFile = new File(getOrCreateCacheDirectory(context, directory), buildFileName(blobSpec.id));
@ -182,6 +211,10 @@ public class BlobProvider {
SignalExecutors.UNBOUNDED.execute(() -> { SignalExecutors.UNBOUNDED.execute(() -> {
try { try {
Util.copy(blobSpec.getData(), outputStream); Util.copy(blobSpec.getData(), outputStream);
if (successListener != null) {
successListener.onSuccess();
}
} catch (IOException e) { } catch (IOException e) {
if (errorListener != null) { if (errorListener != null) {
errorListener.onError(e); errorListener.onError(e);
@ -258,8 +291,23 @@ public class BlobProvider {
* period from one {@link Application#onCreate()} to the next. * period from one {@link Application#onCreate()} to the next.
*/ */
@WorkerThread @WorkerThread
public Uri createForSingleSessionOnDisk(@NonNull Context context, @Nullable ErrorListener errorListener) throws IOException { public Uri createForSingleSessionOnDisk(@NonNull Context context) throws IOException {
return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.SINGLE_SESSION_DISK), errorListener); return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.SINGLE_SESSION_DISK));
}
/**
* Create a blob that will exist for a single app session. An app session is defined as the
* period from one {@link Application#onCreate()} to the next. The file will be created on disk
* synchronously, but the data will copied asynchronously. This is helpful when the copy is
* long-running, such as in the case of recording a voice note.
*/
@WorkerThread
public Uri createForSingleSessionOnDiskAsync(@NonNull Context context,
@Nullable SuccessListener successListener,
@Nullable ErrorListener errorListener)
throws IOException
{
return writeBlobSpecToDiskAsync(context, buildBlobSpec(StorageType.SINGLE_SESSION_DISK), successListener, errorListener);
} }
/** /**
@ -267,8 +315,25 @@ public class BlobProvider {
* eventually call {@link BlobProvider#delete(Context, Uri)} when the blob is no longer in use. * eventually call {@link BlobProvider#delete(Context, Uri)} when the blob is no longer in use.
*/ */
@WorkerThread @WorkerThread
public Uri createForMultipleSessionsOnDisk(@NonNull Context context, @Nullable ErrorListener errorListener) throws IOException { public Uri createForMultipleSessionsOnDisk(@NonNull Context context) throws IOException {
return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.MULTI_SESSION_DISK), errorListener); return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.MULTI_SESSION_DISK));
}
/**
* Create a blob that will exist for multiple app sessions. The file will be created on disk
* synchronously, but the data will copied asynchronously. This is helpful when the copy is
* long-running, such as in the case of recording a voice note.
*
* It is the caller's responsibility to eventually call {@link BlobProvider#delete(Context, Uri)}
* when the blob is no longer in use.
*/
@WorkerThread
public Uri createForMultipleSessionsOnDiskAsync(@NonNull Context context,
@Nullable SuccessListener successListener,
@Nullable ErrorListener errorListener)
throws IOException
{
return writeBlobSpecToDiskAsync(context, buildBlobSpec(StorageType.MULTI_SESSION_DISK), successListener, errorListener);
} }
} }
@ -311,6 +376,11 @@ public class BlobProvider {
} }
} }
public interface SuccessListener {
@WorkerThread
void onSuccess();
}
public interface ErrorListener { public interface ErrorListener {
@WorkerThread @WorkerThread
void onError(IOException e); void onError(IOException e);

View File

@ -1,32 +0,0 @@
package org.thoughtcrime.securesms.revealable;
public class RevealExpirationInfo {
private final long messageId;
private final long receiveTime;
private final long revealStartTime;
private final long revealDuration;
public RevealExpirationInfo(long messageId, long receiveTime, long revealStartTime, long revealDuration) {
this.messageId = messageId;
this.receiveTime = receiveTime;
this.revealStartTime = revealStartTime;
this.revealDuration = revealDuration;
}
public long getMessageId() {
return messageId;
}
public long getReceiveTime() {
return receiveTime;
}
public long getRevealStartTime() {
return revealStartTime;
}
public long getRevealDuration() {
return revealDuration;
}
}

View File

@ -1,71 +0,0 @@
package org.thoughtcrime.securesms.revealable;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.util.Util;
public class RevealableMessageActivity extends PassphraseRequiredActionBarActivity {
private static final String TAG = Log.tag(RevealableMessageActivity.class);
private static final String KEY_MESSAGE_ID = "message_id";
private ImageView image;
private View closeButton;
private RevealableMessageViewModel viewModel;
public static Intent getIntent(@NonNull Context context, long messageId) {
Intent intent = new Intent(context, RevealableMessageActivity.class);
intent.putExtra(KEY_MESSAGE_ID, messageId);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.revealable_message_activity);
this.image = findViewById(R.id.reveal_image);
this.closeButton = findViewById(R.id.reveal_close_button);
image.setOnClickListener(v -> finish());
closeButton.setOnClickListener(v -> finish());
initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1));
}
private void initViewModel(long messageId) {
RevealableMessageRepository repository = new RevealableMessageRepository(this);
viewModel = ViewModelProviders.of(this, new RevealableMessageViewModel.Factory(getApplication(), messageId, repository))
.get(RevealableMessageViewModel.class);
viewModel.getMessage().observe(this, (message) -> {
if (message == null) return;
if (message.isPresent()) {
//noinspection ConstantConditions
GlideApp.with(this)
.load(new DecryptableUri(message.get().getSlideDeck().getThumbnailSlide().getUri()))
.into(image);
} else {
image.setImageDrawable(null);
finish();
}
});
}
}

View File

@ -1,50 +0,0 @@
package org.thoughtcrime.securesms.revealable;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import java.util.concurrent.TimeUnit;
public class RevealableUtil {
public static final long MAX_LIFESPAN = TimeUnit.DAYS.toMillis(30);
public static final long DURATION = TimeUnit.SECONDS.toMillis(5);
public static boolean isViewable(@Nullable MmsMessageRecord message) {
if (message.getRevealDuration() == 0) {
return true;
} else if (message.getSlideDeck().getThumbnailSlide() == null) {
return false;
} else if (message.getSlideDeck().getThumbnailSlide().getUri() == null) {
return false;
} else if (message.isOutgoing() && message.getSlideDeck().getThumbnailSlide().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) {
return true;
} else if (message.getSlideDeck().getThumbnailSlide().getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
return false;
} else if (isRevealExpired(message)) {
return false;
} else {
return true;
}
}
public static boolean isRevealExpired(@Nullable MmsMessageRecord message) {
if (message == null) {
return false;
} else if (message.getRevealDuration() == 0) {
return false;
} else if (message.getDateReceived() + MAX_LIFESPAN < System.currentTimeMillis()) {
return true;
} else if (message.getRevealStartTime() == 0) {
return false;
} else if (message.getRevealStartTime() + message.getRevealDuration() < System.currentTimeMillis()) {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,20 @@
package org.thoughtcrime.securesms.revealable;
public class ViewOnceExpirationInfo {
private final long messageId;
private final long receiveTime;
public ViewOnceExpirationInfo(long messageId, long receiveTime) {
this.messageId = messageId;
this.receiveTime = receiveTime;
}
public long getMessageId() {
return messageId;
}
public long getReceiveTime() {
return receiveTime;
}
}

View File

@ -0,0 +1,81 @@
package org.thoughtcrime.securesms.revealable;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProviders;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.providers.BlobProvider;
public class ViewOnceMessageActivity extends PassphraseRequiredActionBarActivity {
private static final String TAG = Log.tag(ViewOnceMessageActivity.class);
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_URI = "uri";
private ImageView image;
private View closeButton;
private ViewOnceMessageViewModel viewModel;
private Uri uri;
public static Intent getIntent(@NonNull Context context, long messageId, @NonNull Uri uri) {
Intent intent = new Intent(context, ViewOnceMessageActivity.class);
intent.putExtra(KEY_MESSAGE_ID, messageId);
intent.putExtra(KEY_URI, uri);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.view_once_message_activity);
this.image = findViewById(R.id.view_once_image);
this.closeButton = findViewById(R.id.view_once_close_button);
this.uri = getIntent().getParcelableExtra(KEY_URI);
image.setOnClickListener(v -> finish());
closeButton.setOnClickListener(v -> finish());
initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1), uri);
}
@Override
protected void onStop() {
super.onStop();
BlobProvider.getInstance().delete(this, uri);
finish();
}
private void initViewModel(long messageId, @NonNull Uri uri) {
ViewOnceMessageRepository repository = new ViewOnceMessageRepository(this);
viewModel = ViewModelProviders.of(this, new ViewOnceMessageViewModel.Factory(getApplication(), messageId, repository))
.get(ViewOnceMessageViewModel.class);
viewModel.getMessage().observe(this, (message) -> {
if (message == null) return;
if (message.isPresent()) {
GlideApp.with(this)
.load(new DecryptableUri(uri))
.into(image);
} else {
image.setImageDrawable(null);
finish();
}
});
}
}

View File

@ -20,14 +20,14 @@ import org.thoughtcrime.securesms.service.TimedEventManager;
/** /**
* Manages clearing removable message content after they're opened. * Manages clearing removable message content after they're opened.
*/ */
public class RevealableMessageManager extends TimedEventManager<RevealExpirationInfo> { public class ViewOnceMessageManager extends TimedEventManager<ViewOnceExpirationInfo> {
private static final String TAG = Log.tag(RevealableMessageManager.class); private static final String TAG = Log.tag(ViewOnceMessageManager.class);
private final MmsDatabase mmsDatabase; private final MmsDatabase mmsDatabase;
private final AttachmentDatabase attachmentDatabase; private final AttachmentDatabase attachmentDatabase;
public RevealableMessageManager(@NonNull Application application) { public ViewOnceMessageManager(@NonNull Application application) {
super(application, "RevealableMessageManager"); super(application, "RevealableMessageManager");
this.mmsDatabase = DatabaseFactory.getMmsDatabase(application); this.mmsDatabase = DatabaseFactory.getMmsDatabase(application);
@ -38,8 +38,8 @@ public class RevealableMessageManager extends TimedEventManager<RevealExpiration
@WorkerThread @WorkerThread
@Override @Override
protected @Nullable RevealExpirationInfo getNextClosestEvent() { protected @Nullable ViewOnceExpirationInfo getNextClosestEvent() {
RevealExpirationInfo expirationInfo = mmsDatabase.getNearestExpiringRevealableMessage(); ViewOnceExpirationInfo expirationInfo = mmsDatabase.getNearestExpiringViewOnceMessage();
if (expirationInfo != null) { if (expirationInfo != null) {
Log.i(TAG, "Next closest expiration is in " + getDelayForEvent(expirationInfo) + " ms for messsage " + expirationInfo.getMessageId() + "."); Log.i(TAG, "Next closest expiration is in " + getDelayForEvent(expirationInfo) + " ms for messsage " + expirationInfo.getMessageId() + ".");
@ -52,41 +52,34 @@ public class RevealableMessageManager extends TimedEventManager<RevealExpiration
@WorkerThread @WorkerThread
@Override @Override
protected void executeEvent(@NonNull RevealExpirationInfo event) { protected void executeEvent(@NonNull ViewOnceExpirationInfo event) {
Log.i(TAG, "Deleting attachments for message " + event.getMessageId()); Log.i(TAG, "Deleting attachments for message " + event.getMessageId());
attachmentDatabase.deleteAttachmentFilesForMessage(event.getMessageId()); attachmentDatabase.deleteAttachmentFilesForMessage(event.getMessageId());
} }
@WorkerThread @WorkerThread
@Override @Override
protected long getDelayForEvent(@NonNull RevealExpirationInfo event) { protected long getDelayForEvent(@NonNull ViewOnceExpirationInfo event) {
if (event.getRevealStartTime() == 0) { long expiresAt = event.getReceiveTime() + ViewOnceUtil.MAX_LIFESPAN;
long expiresAt = event.getReceiveTime() + RevealableUtil.MAX_LIFESPAN; long timeLeft = expiresAt - System.currentTimeMillis();
long timeLeft = expiresAt - System.currentTimeMillis();
return Math.max(0, timeLeft); return Math.max(0, timeLeft);
} else {
long timeSinceStart = System.currentTimeMillis() - event.getRevealStartTime();
long timeLeft = event.getRevealDuration() - timeSinceStart;
return Math.max(0, timeLeft);
}
} }
@AnyThread @AnyThread
@Override @Override
protected void scheduleAlarm(@NonNull Application application, long delay) { protected void scheduleAlarm(@NonNull Application application, long delay) {
setAlarm(application, delay, RevealAlarm.class); setAlarm(application, delay, ViewOnceAlarm.class);
} }
public static class RevealAlarm extends BroadcastReceiver { public static class ViewOnceAlarm extends BroadcastReceiver {
private static final String TAG = Log.tag(RevealAlarm.class); private static final String TAG = Log.tag(ViewOnceAlarm.class);
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive()"); Log.d(TAG, "onReceive()");
ApplicationContext.getInstance(context).getRevealableMessageManager().scheduleIfNecessary(); ApplicationContext.getInstance(context).getViewOnceMessageManager().scheduleIfNecessary();
} }
} }
} }

View File

@ -4,7 +4,6 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
@ -12,13 +11,13 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
class RevealableMessageRepository { class ViewOnceMessageRepository {
private static final String TAG = Log.tag(RevealableMessageRepository.class); private static final String TAG = Log.tag(ViewOnceMessageRepository.class);
private final MmsDatabase mmsDatabase; private final MmsDatabase mmsDatabase;
RevealableMessageRepository(@NonNull Context context) { ViewOnceMessageRepository(@NonNull Context context) {
this.mmsDatabase = DatabaseFactory.getMmsDatabase(context); this.mmsDatabase = DatabaseFactory.getMmsDatabase(context);
} }

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.revealable;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -26,9 +24,9 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
public class RevealableMessageView extends LinearLayout { public class ViewOnceMessageView extends LinearLayout {
private static final String TAG = Log.tag(RevealableMessageView.class); private static final String TAG = Log.tag(ViewOnceMessageView.class);
private ImageView icon; private ImageView icon;
private ProgressWheel progress; private ProgressWheel progress;
@ -38,12 +36,12 @@ public class RevealableMessageView extends LinearLayout {
private int openedForegroundColor; private int openedForegroundColor;
private int foregroundColor; private int foregroundColor;
public RevealableMessageView(Context context) { public ViewOnceMessageView(Context context) {
super(context); super(context);
init(null); init(null);
} }
public RevealableMessageView(Context context, @Nullable AttributeSet attrs) { public ViewOnceMessageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
init(attrs); init(attrs);
} }
@ -53,10 +51,10 @@ public class RevealableMessageView extends LinearLayout {
setOrientation(LinearLayout.HORIZONTAL); setOrientation(LinearLayout.HORIZONTAL);
if (attrs != null) { if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.RevealableMessageView, 0, 0); TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ViewOnceMessageView, 0, 0);
unopenedForegroundColor = typedArray.getColor(R.styleable.RevealableMessageView_revealable_unopenedForegroundColor, Color.BLACK); unopenedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_unopenedForegroundColor, Color.BLACK);
openedForegroundColor = typedArray.getColor(R.styleable.RevealableMessageView_revealable_openedForegroundColor, Color.BLACK); openedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_openedForegroundColor, Color.BLACK);
typedArray.recycle(); typedArray.recycle();
} }
@ -101,12 +99,17 @@ public class RevealableMessageView extends LinearLayout {
} }
private void presentText(@NonNull MmsMessageRecord messageRecord) { private void presentText(@NonNull MmsMessageRecord messageRecord) {
if (downloadInProgress(messageRecord) && messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
foregroundColor = openedForegroundColor;
text.setText(R.string.RevealableMessageView_photo);
icon.setImageResource(R.drawable.ic_play_outline_24);
progress.setVisibility(GONE);
} else if (ViewOnceUtil.isViewable(messageRecord)) {
foregroundColor = unopenedForegroundColor; foregroundColor = unopenedForegroundColor;
text.setText(R.string.RevealableMessageView_view_photo); text.setText(R.string.RevealableMessageView_view_photo);
icon.setImageResource(0); icon.setImageResource(R.drawable.ic_play_solid_24);
progress.setVisibility(VISIBLE); progress.setVisibility(GONE);
} else if (downloadInProgress(messageRecord)) { } else if (networkInProgress(messageRecord)) {
foregroundColor = unopenedForegroundColor; foregroundColor = unopenedForegroundColor;
text.setText(""); text.setText("");
icon.setImageResource(0); icon.setImageResource(0);
@ -116,16 +119,6 @@ public class RevealableMessageView extends LinearLayout {
text.setText(formatFileSize(messageRecord)); text.setText(formatFileSize(messageRecord));
icon.setImageResource(R.drawable.ic_arrow_down_circle_outline_24); icon.setImageResource(R.drawable.ic_arrow_down_circle_outline_24);
progress.setVisibility(GONE); progress.setVisibility(GONE);
} else if (RevealableUtil.isViewable(messageRecord)) {
foregroundColor = unopenedForegroundColor;
text.setText(R.string.RevealableMessageView_view_photo);
icon.setImageResource(R.drawable.ic_play_solid_24);
progress.setVisibility(GONE);
} else if (messageRecord.isOutgoing()) {
foregroundColor = openedForegroundColor;
text.setText(R.string.RevealableMessageView_photo);
icon.setImageResource(R.drawable.ic_play_outline_24);
progress.setVisibility(GONE);
} else { } else {
foregroundColor = openedForegroundColor; foregroundColor = openedForegroundColor;
text.setText(R.string.RevealableMessageView_viewed); text.setText(R.string.RevealableMessageView_viewed);
@ -139,7 +132,7 @@ public class RevealableMessageView extends LinearLayout {
progress.setRimColor(Color.TRANSPARENT); progress.setRimColor(Color.TRANSPARENT);
} }
private boolean downloadInProgress(@NonNull MmsMessageRecord messageRecord) { private boolean networkInProgress(@NonNull MmsMessageRecord messageRecord) {
if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return false; if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return false;
Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment(); Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment();

View File

@ -17,18 +17,18 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
class RevealableMessageViewModel extends ViewModel { class ViewOnceMessageViewModel extends ViewModel {
private static final String TAG = Log.tag(RevealableMessageViewModel.class); private static final String TAG = Log.tag(ViewOnceMessageViewModel.class);
private final Application application; private final Application application;
private final RevealableMessageRepository repository; private final ViewOnceMessageRepository repository;
private final MutableLiveData<Optional<MmsMessageRecord>> message; private final MutableLiveData<Optional<MmsMessageRecord>> message;
private final ContentObserver observer; private final ContentObserver observer;
private RevealableMessageViewModel(@NonNull Application application, private ViewOnceMessageViewModel(@NonNull Application application,
long messageId, long messageId,
@NonNull RevealableMessageRepository repository) @NonNull ViewOnceMessageRepository repository)
{ {
this.application = application; this.application = application;
this.repository = repository; this.repository = repository;
@ -74,13 +74,13 @@ class RevealableMessageViewModel extends ViewModel {
static class Factory extends ViewModelProvider.NewInstanceFactory { static class Factory extends ViewModelProvider.NewInstanceFactory {
private final Application application; private final Application application;
private final long messageId; private final long messageId;
private final RevealableMessageRepository repository; private final ViewOnceMessageRepository repository;
Factory(@NonNull Application application, Factory(@NonNull Application application,
long messageId, long messageId,
@NonNull RevealableMessageRepository repository) @NonNull ViewOnceMessageRepository repository)
{ {
this.application = application; this.application = application;
this.messageId = messageId; this.messageId = messageId;
@ -90,7 +90,7 @@ class RevealableMessageViewModel extends ViewModel {
@Override @Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) { public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions //noinspection ConstantConditions
return modelClass.cast(new RevealableMessageViewModel(application, messageId, repository)); return modelClass.cast(new ViewOnceMessageViewModel(application, messageId, repository));
} }
} }
} }

View File

@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.revealable;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import java.util.concurrent.TimeUnit;
public class ViewOnceUtil {
public static final long MAX_LIFESPAN = TimeUnit.DAYS.toMillis(30);
public static boolean isViewable(@NonNull MmsMessageRecord message) {
if (!message.isViewOnce()) {
return true;
}
if (message.isOutgoing()) {
return false;
}
if (message.getSlideDeck().getThumbnailSlide() == null) {
return false;
}
if (message.getSlideDeck().getThumbnailSlide().getUri() == null) {
return false;
}
if (message.getSlideDeck().getThumbnailSlide().getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
return false;
}
if (isViewed(message)) {
return false;
}
return true;
}
public static boolean isViewed(@NonNull MmsMessageRecord message) {
if (!message.isViewOnce()) {
return false;
}
if (message.getDateReceived() + MAX_LIFESPAN <= System.currentTimeMillis()) {
return true;
}
if (message.getSlideDeck().getThumbnailSlide() != null && message.getSlideDeck().getThumbnailSlide().getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
return false;
}
if (message.getSlideDeck().getThumbnailSlide() == null) {
return true;
}
if (message.getSlideDeck().getThumbnailSlide().getUri() == null) {
return true;
}
if (message.isOutgoing()) {
return true;
}
return false;
}
}

View File

@ -7,7 +7,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
public class IncomingJoinedMessage extends IncomingTextMessage { public class IncomingJoinedMessage extends IncomingTextMessage {
public IncomingJoinedMessage(Address sender) { public IncomingJoinedMessage(Address sender) {
super(sender, 1, System.currentTimeMillis(), null, Optional.<SignalServiceGroup>absent(), 0, 0, false); super(sender, 1, System.currentTimeMillis(), null, Optional.<SignalServiceGroup>absent(), 0, false);
} }
@Override @Override

View File

@ -42,7 +42,6 @@ public class IncomingTextMessage implements Parcelable {
private final boolean push; private final boolean push;
private final int subscriptionId; private final int subscriptionId;
private final long expiresInMillis; private final long expiresInMillis;
private final long revealDuration;
private final boolean unidentified; private final boolean unidentified;
public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) { public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) {
@ -56,7 +55,6 @@ public class IncomingTextMessage implements Parcelable {
this.sentTimestampMillis = message.getTimestampMillis(); this.sentTimestampMillis = message.getTimestampMillis();
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
this.expiresInMillis = 0; this.expiresInMillis = 0;
this.revealDuration = 0;
this.groupId = null; this.groupId = null;
this.push = false; this.push = false;
this.unidentified = false; this.unidentified = false;
@ -64,7 +62,7 @@ public class IncomingTextMessage implements Parcelable {
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis, public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
String encodedBody, Optional<SignalServiceGroup> group, String encodedBody, Optional<SignalServiceGroup> group,
long expiresInMillis, long revealDuration, boolean unidentified) long expiresInMillis, boolean unidentified)
{ {
this.message = encodedBody; this.message = encodedBody;
this.sender = sender; this.sender = sender;
@ -77,7 +75,6 @@ public class IncomingTextMessage implements Parcelable {
this.push = true; this.push = true;
this.subscriptionId = -1; this.subscriptionId = -1;
this.expiresInMillis = expiresInMillis; this.expiresInMillis = expiresInMillis;
this.revealDuration = revealDuration;
this.unidentified = unidentified; this.unidentified = unidentified;
if (group.isPresent()) { if (group.isPresent()) {
@ -100,7 +97,6 @@ public class IncomingTextMessage implements Parcelable {
this.push = (in.readInt() == 1); this.push = (in.readInt() == 1);
this.subscriptionId = in.readInt(); this.subscriptionId = in.readInt();
this.expiresInMillis = in.readLong(); this.expiresInMillis = in.readLong();
this.revealDuration = in.readLong();
this.unidentified = in.readInt() == 1; this.unidentified = in.readInt() == 1;
} }
@ -117,7 +113,6 @@ public class IncomingTextMessage implements Parcelable {
this.push = base.isPush(); this.push = base.isPush();
this.subscriptionId = base.getSubscriptionId(); this.subscriptionId = base.getSubscriptionId();
this.expiresInMillis = base.getExpiresIn(); this.expiresInMillis = base.getExpiresIn();
this.revealDuration = base.getRevealDuration();
this.unidentified = base.isUnidentified(); this.unidentified = base.isUnidentified();
} }
@ -140,7 +135,6 @@ public class IncomingTextMessage implements Parcelable {
this.push = fragments.get(0).isPush(); this.push = fragments.get(0).isPush();
this.subscriptionId = fragments.get(0).getSubscriptionId(); this.subscriptionId = fragments.get(0).getSubscriptionId();
this.expiresInMillis = fragments.get(0).getExpiresIn(); this.expiresInMillis = fragments.get(0).getExpiresIn();
this.revealDuration = fragments.get(0).getRevealDuration();
this.unidentified = fragments.get(0).isUnidentified(); this.unidentified = fragments.get(0).isUnidentified();
} }
@ -158,7 +152,6 @@ public class IncomingTextMessage implements Parcelable {
this.push = true; this.push = true;
this.subscriptionId = -1; this.subscriptionId = -1;
this.expiresInMillis = 0; this.expiresInMillis = 0;
this.revealDuration = 0;
this.unidentified = false; this.unidentified = false;
} }
@ -170,10 +163,6 @@ public class IncomingTextMessage implements Parcelable {
return expiresInMillis; return expiresInMillis;
} }
public long getRevealDuration() {
return revealDuration;
}
public long getSentTimestampMillis() { public long getSentTimestampMillis() {
return sentTimestampMillis; return sentTimestampMillis;
} }
@ -281,7 +270,6 @@ public class IncomingTextMessage implements Parcelable {
out.writeInt(push ? 1 : 0); out.writeInt(push ? 1 : 0);
out.writeInt(subscriptionId); out.writeInt(subscriptionId);
out.writeLong(expiresInMillis); out.writeLong(expiresInMillis);
out.writeLong(revealDuration);
out.writeInt(unidentified ? 1 : 0); out.writeInt(unidentified ? 1 : 0);
} }
} }

View File

@ -73,7 +73,7 @@ public class GroupUtil {
.setType(GroupContext.Type.QUIT) .setType(GroupContext.Type.QUIT)
.build(); .build();
return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, 0, null, Collections.emptyList(), Collections.emptyList())); return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList()));
} }

View File

@ -78,7 +78,7 @@ public class IdentityUtil {
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId()); SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId());
if (remote) { if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, 0, false); IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming);
@ -98,7 +98,7 @@ public class IdentityUtil {
} }
if (remote) { if (remote) {
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, 0, false); IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
else incoming = new IncomingIdentityDefaultMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming);
@ -128,14 +128,14 @@ public class IdentityUtil {
while ((groupRecord = reader.getNext()) != null) { while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) { if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) {
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId()); SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId());
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, 0, false); IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming); IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
smsDatabase.insertMessageInbox(groupUpdate); smsDatabase.insertMessageInbox(groupUpdate);
} }
} }
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, 0, false); IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming); IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);

View File

@ -183,7 +183,7 @@ public class TextSecurePreferences {
private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode"; private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode";
private static final String REVEALABLE_MESSAGE_DEFAULT = "pref_revealable_message_default"; private static final String VIEW_ONCE_DEFAULT = "pref_revealable_message_default";
private static final String SEEN_CAMERA_FIRST_TOOLTIP = "pref_seen_camera_first_tooltip"; private static final String SEEN_CAMERA_FIRST_TOOLTIP = "pref_seen_camera_first_tooltip";
@ -1103,11 +1103,11 @@ public class TextSecurePreferences {
} }
public static void setIsRevealableMessageEnabled(Context context, boolean value) { public static void setIsRevealableMessageEnabled(Context context, boolean value) {
setBooleanPreference(context, REVEALABLE_MESSAGE_DEFAULT, value); setBooleanPreference(context, VIEW_ONCE_DEFAULT, value);
} }
public static boolean isRevealableMessageEnabled(Context context) { public static boolean isRevealableMessageEnabled(Context context) {
return getBooleanPreference(context, REVEALABLE_MESSAGE_DEFAULT, false); return getBooleanPreference(context, VIEW_ONCE_DEFAULT, false);
} }
public static void setHasSeenCameraFirstTooltip(Context context, boolean value) { public static void setHasSeenCameraFirstTooltip(Context context, boolean value) {