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

View File

@ -86,7 +86,7 @@ dependencies {
implementation 'org.conscrypt:conscrypt-android:2.0.0'
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'
@ -193,7 +193,7 @@ dependencyVerification {
'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0',
'org.conscrypt:conscrypt-android:400ca559a49b860a82862b22cee0e3110764bdcf7ee7c79e7479895c25cdfc09',
'org.signal:aesgcmprovider:6eb4422e8a618b3b76cb2096a3619d251f9e27989dc68307a1e5414c3710f2d1',
'org.whispersystems:signal-service-android:fa8c1b82d066bd6902ffd7e3a0c4343c3afa0379c26c78c06323c300d5afca7b',
'org.whispersystems:signal-service-android:5115aa434c52ca671c513995e6ae67d73f3abaaa605f9e6cf64c2e01da961c7e',
'org.whispersystems:webrtc-android:f8231bb57923afb243760213dc58924e85cce42f2f3cc8cb33a6d883672a921a',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
@ -265,7 +265,7 @@ dependencyVerification {
'androidx.constraintlayout:constraintlayout-solver:965c177e64fbd81bd1d27b402b66ef9d7bc7b5cb5f718044bf7a453abc542045',
'com.google.auto.value:auto-value-annotations:0e951fee8c31f60270bc46553a8586001b7b93dbb12aec06373aa99a150392c0',
'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:annotations:702a7521cb3f6d7e55edd66e90bda1a1975baf971d25f75b75638579f86bc69b',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
@ -277,12 +277,12 @@ dependencyVerification {
'org.whispersystems:signal-protocol-java:7f6df67a963acbab7716424b01b12fa7279f18a9623a2a7c8ba7b1c285830168',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
'com.googlecode.libphonenumber:libphonenumber:dbf4bf566d17a60044c19e282a619684e4b4abb0f9f9f24f843c55d19826ab5e',
'com.fasterxml.jackson.core:jackson-databind:2351c3eba73a545db9079f5d6d768347ad72666537362c8220fe3e950a55a864',
'com.fasterxml.jackson.core:jackson-databind:fb262d42ea2de98044b62d393950a5aa050435fec38bbcadf2325cf7dc41b848',
'com.squareup.okhttp3:okhttp:07c3d82ca7eaf4722f00b2da807dc7860f6169ae60cfedcf5d40218f90880a46',
'org.threeten:threetenbp:f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7',
'org.whispersystems:curve25519-android:b502bcf83efe001f09a7a9efda6f0fa772c43ed5924e97816296ed3503caa092',
'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',
'org.whispersystems:curve25519-java:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
]

View File

@ -1,5 +1,5 @@
<?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

View File

@ -1,5 +1,5 @@
<?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

View File

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

View File

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

View File

@ -254,6 +254,7 @@
<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_no_longer_available">Original message no longer available</string>
<string name="ConversationFragment_failed_to_open_message">Failed to open message</string>
<!-- ConversationListActivity -->
<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.KeyCachingService;
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.RotateSignedPreKeyListener;
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 ExpiringMessageManager expiringMessageManager;
private RevealableMessageManager revealableMessageManager;
private ViewOnceMessageManager viewOnceMessageManager;
private TypingStatusRepository typingStatusRepository;
private TypingStatusSender typingStatusSender;
private JobManager jobManager;
@ -157,8 +157,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
return expiringMessageManager;
}
public RevealableMessageManager getRevealableMessageManager() {
return revealableMessageManager;
public ViewOnceMessageManager getViewOnceMessageManager() {
return viewOnceMessageManager;
}
public TypingStatusRepository getTypingStatusRepository() {
@ -252,7 +252,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
private void initializeRevealableMessageManager() {
this.revealableMessageManager = new RevealableMessageManager(this);
this.viewOnceMessageManager = new ViewOnceMessageManager(this);
}
private void initializeTypingStatusRepository() {

View File

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

View File

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

View File

@ -7,6 +7,12 @@ import androidx.annotation.Nullable;
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 TombstoneAttachment(@NonNull String contentType, boolean quote) {

View File

@ -50,7 +50,7 @@ public class AudioRecorder {
captureUri = BlobProvider.getInstance()
.forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0)
.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.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.SessionDatabase;
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
@ -255,18 +254,18 @@ public class FullBackupExporter extends FullBackupBase {
private static boolean isNonExpiringMessage(@NonNull Cursor cursor) {
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) {
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[] args = new String[] { String.valueOf(mmsId) };
try (Cursor mmsCursor = db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null)) {
if (mmsCursor != null && mmsCursor.moveToFirst()) {
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;
TransportOption transport = data.getParcelableExtra(MediaSendActivity.EXTRA_TRANSPORT);
String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE);
long revealDuration = data.getLongExtra(MediaSendActivity.EXTRA_REVEAL_DURATION, 0);
QuoteModel quote = (revealDuration == 0) ? inputPanel.getQuote().orNull() : null;
boolean viewOnce = data.getBooleanExtra(MediaSendActivity.EXTRA_VIEW_ONCE, false);
QuoteModel quote = viewOnce ? inputPanel.getQuote().orNull() : null;
SlideDeck slideDeck = new SlideDeck();
if (transport == null) {
@ -575,7 +575,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Collections.emptyList(),
Collections.emptyList(),
expiresIn,
revealDuration,
viewOnce,
subscriptionId,
initiating,
true).addListener(new AssertedSuccessListener<Void>() {
@ -1813,7 +1813,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long expiresIn = recipient.getExpireMessages() * 1000L;
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) {
@ -2135,7 +2135,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else if (!forceSms && identityRecords.isUntrusted()) {
handleUntrustedRecipients();
} else if (isMediaMessage) {
sendMediaMessage(forceSms, expiresIn, 0, subscriptionId, initiating);
sendMediaMessage(forceSms, expiresIn, false, subscriptionId, initiating);
} else {
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
{
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,
@ -2165,7 +2165,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
List<Contact> contacts,
List<LinkPreview> previews,
final long expiresIn,
final long revealDuration,
final boolean viewOnce,
final int subscriptionId,
final boolean initiating,
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 Context context = getApplicationContext();
@ -2385,7 +2385,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
SlideDeck slideDeck = new SlideDeck();
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
public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() {
@ -2513,7 +2513,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
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 {
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);
slideDeck = new SlideDeck();
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.MmsMessageRecord;
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.logging.Log;
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.Slide;
import org.thoughtcrime.securesms.profiles.UnknownSenderView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.revealable.RevealableMessageActivity;
import org.thoughtcrime.securesms.revealable.RevealableUtil;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity;
import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
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.Util;
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.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
@ -963,35 +965,46 @@ public class ConversationFragment extends Fragment
}
@Override
public void onRevealableMessageClicked(@NonNull MmsMessageRecord messageRecord) {
if (messageRecord.getRevealDuration() == 0) {
public void onViewOnceMessageClicked(@NonNull MmsMessageRecord messageRecord) {
if (!messageRecord.isViewOnce()) {
throw new AssertionError("Non-revealable message clicked.");
}
if (messageRecord.getRevealStartTime() == 0) {
SimpleTask.run(getLifecycle(), () -> {
if (!messageRecord.isOutgoing()) {
Log.i(TAG, "Marking revealable message as opened.");
if (!ViewOnceUtil.isViewable(messageRecord)) {
Log.w(TAG, "View-once photo is not viewable!");
return;
}
DatabaseFactory.getMmsDatabase(requireContext()).markRevealStarted(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())
.getRevealableMessageManager()
.getViewOnceMessageManager()
.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.");
}
.add(new MultiDeviceViewOnceOpenJob(new MessagingDatabase.SyncMessageId(messageRecord.getIndividualRecipient().getAddress(), messageRecord.getDateSent())));
return tempUri;
} catch (IOException e) {
return null;
}, (nothing) -> {
startActivity(RevealableMessageActivity.getIntent(requireContext(), messageRecord.getId()));
});
} else if (RevealableUtil.isViewable(messageRecord)) {
startActivity(RevealableMessageActivity.getIntent(requireContext(), messageRecord.getId()));
}
}, (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

View File

@ -68,7 +68,7 @@ import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.Outliner;
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.StickerView;
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.recipients.Recipient;
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.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -163,7 +163,7 @@ public class ConversationItem extends LinearLayout
private Stub<SharedContactView> sharedContactStub;
private Stub<LinkPreviewView> linkPreviewStub;
private Stub<StickerView> stickerStub;
private Stub<RevealableMessageView> revealableStub;
private Stub<ViewOnceMessageView> revealableStub;
private @Nullable EventListener eventListener;
private int defaultBubbleColor;
@ -175,7 +175,7 @@ public class ConversationItem extends LinearLayout
private final SharedContactEventListener sharedContactEventListener = new SharedContactEventListener();
private final SharedContactClickListener sharedContactClickListener = new SharedContactClickListener();
private final LinkPreviewClickListener linkPreviewClickListener = new LinkPreviewClickListener();
private final RevealableMessageClickListener revealableClickListener = new RevealableMessageClickListener();
private final ViewOnceMessageClickListener revealableClickListener = new ViewOnceMessageClickListener();
private final Context context;
@ -314,7 +314,7 @@ public class ConversationItem extends LinearLayout
protected void dispatchDraw(Canvas 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.draw(canvas, bodyBubble.getTop() + getPaddingTop(), bodyBubble.getRight(), bodyBubble.getBottom() + getPaddingTop(), bodyBubble.getLeft());
}
@ -324,7 +324,7 @@ public class ConversationItem extends LinearLayout
int availableWidth;
if (hasAudio(messageRecord)) {
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();
} else {
availableWidth = bodyBubble.getMeasuredWidth() - bodyBubble.getPaddingLeft() - bodyBubble.getPaddingRight();
@ -361,7 +361,7 @@ public class ConversationItem extends LinearLayout
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
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));
} 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);
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));
@ -440,7 +440,7 @@ public class ConversationItem extends LinearLayout
!hasDocument(messageRecord) &&
!hasSharedContact(messageRecord) &&
!hasSticker(messageRecord) &&
!hasRevealableMessage(messageRecord);
!isViewOnceMessage(messageRecord);
}
private boolean hasDocument(MessageRecord messageRecord) {
@ -477,8 +477,8 @@ public class ConversationItem extends LinearLayout
!StickerUrl.isValidShareLink(linkPreview.getUrl());
}
private boolean hasRevealableMessage(MessageRecord messageRecord) {
return messageRecord.isMms() && ((MmsMessageRecord) messageRecord).getRevealDuration() > 0;
private boolean isViewOnceMessage(MessageRecord messageRecord) {
return messageRecord.isMms() && ((MmsMessageRecord) messageRecord).isViewOnce();
}
private void setBodyText(MessageRecord messageRecord, @Nullable String searchQuery) {
@ -512,7 +512,7 @@ public class ConversationItem extends LinearLayout
{
boolean showControls = !messageRecord.isFailed();
if (hasRevealableMessage(messageRecord)) {
if (isViewOnceMessage(messageRecord)) {
revealableStub.get().setVisibility(VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.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) {
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));
groupSenderProfileName.setTextColor(ThemeUtil.getThemedColor(context, R.attr.conversation_sticker_author_color));
} 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
public void onClick(View view) {
RevealableMessageView revealView = (RevealableMessageView) view;
ViewOnceMessageView revealView = (ViewOnceMessageView) view;
if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && RevealableUtil.isViewable((MmsMessageRecord) messageRecord)) {
eventListener.onRevealableMessageClicked((MmsMessageRecord) messageRecord);
if (eventListener != null && batchSelected.isEmpty() && messageRecord.isMms() && ViewOnceUtil.isViewable((MmsMessageRecord) messageRecord)) {
eventListener.onViewOnceMessageClicked((MmsMessageRecord) messageRecord);
} else if (batchSelected.isEmpty() && messageRecord.isMms() && revealView.requiresTapToDownload((MmsMessageRecord) messageRecord)) {
singleDownloadClickListener.onClick(view, ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide());
} else {

View File

@ -47,7 +47,7 @@ public class MediaDatabase extends Database {
+ "WHERE " + AttachmentDatabase.MMS_ID + " IN (SELECT " + MmsSmsColumns.ID
+ " FROM " + MmsDatabase.TABLE_NAME
+ " WHERE " + MmsDatabase.THREAD_ID + " = ?) AND (%s) AND "
+ MmsDatabase.REVEAL_DURATION + " = 0 AND "
+ MmsDatabase.VIEW_ONCE + " = 0 AND "
+ AttachmentDatabase.DATA + " IS NOT NULL AND "
+ AttachmentDatabase.QUOTE + " = 0 AND "
+ 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.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.revealable.RevealExpirationInfo;
import org.thoughtcrime.securesms.revealable.RevealableUtil;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
import org.thoughtcrime.securesms.revealable.ViewOnceUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
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 LINK_PREVIEWS = "previews";
public static final String REVEAL_DURATION = "reveal_duration";
public static final String REVEAL_START_TIME = "reveal_start_time";
public static final String VIEW_ONCE = "reveal_duration";
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, " +
@ -131,7 +130,7 @@ public class MmsDatabase extends MessagingDatabase {
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " +
QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " +
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 = {
"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,
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,
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, REVEAL_DURATION, REVEAL_START_TIME,
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE,
"json_group_array(json_object(" +
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
@ -437,60 +436,6 @@ public class MmsDatabase extends MessagingDatabase {
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) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
@ -668,7 +613,7 @@ public class MmsDatabase extends MessagingDatabase {
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
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));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
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))) {
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)) {
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)) {
return new OutgoingSecureMediaMessage(message);
@ -824,7 +769,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
contentValues.put(EXPIRES_IN, request.getExpiresIn());
contentValues.put(REVEAL_DURATION, request.getRevealDuration());
contentValues.put(VIEW_ONCE, request.isViewOnce());
List<Attachment> attachments = new LinkedList<>();
@ -892,7 +837,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(PART_COUNT, retrieved.getAttachments().size());
contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
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(UNIDENTIFIED, retrieved.isUnidentified());
@ -1048,7 +993,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
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(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.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);
}
public @Nullable RevealExpirationInfo getNearestExpiringRevealableMessage() {
public @Nullable
ViewOnceExpirationInfo getNearestExpiringViewOnceMessage() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
RevealExpirationInfo info = null;
ViewOnceExpirationInfo info = null;
long nearestExpiration = Long.MAX_VALUE;
String query = "SELECT " +
TABLE_NAME + "." + ID + ", " +
REVEAL_DURATION + ", " +
REVEAL_START_TIME + ", " +
VIEW_ONCE + ", " +
DATE_RECEIVED + " " +
"FROM " + TABLE_NAME + " INNER JOIN " + AttachmentDatabase.TABLE_NAME + " " +
"ON " + TABLE_NAME + "." + ID + " = " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " " +
"WHERE " +
REVEAL_DURATION + " > 0 AND " +
VIEW_ONCE + " > 0 AND " +
"(" + AttachmentDatabase.DATA + " NOT NULL OR " + AttachmentDatabase.TRANSFER_STATE + " != ?)";
String[] args = new String[] { String.valueOf(AttachmentDatabase.TRANSFER_PROGRESS_DONE) };
try (Cursor cursor = db.rawQuery(query, args)) {
while (cursor != null && cursor.moveToNext()) {
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 expiresAt = revealStartTime > 0 ? revealStartTime + revealDuration
: dateReceived + RevealableUtil.MAX_LIFESPAN;
long expiresAt = dateReceived + ViewOnceUtil.MAX_LIFESPAN;
if (info == null || expiresAt < nearestExpiration) {
info = new RevealExpirationInfo(id, dateReceived, revealStartTime, revealDuration);
info = new ViewOnceExpirationInfo(id, dateReceived);
nearestExpiration = expiresAt;
}
}
@ -1442,8 +1384,7 @@ public class MmsDatabase extends MessagingDatabase {
message.getSubscriptionId(),
message.getExpiresIn(),
System.currentTimeMillis(),
message.getRevealDuration(),
0,
message.isViewOnce(),
0,
message.getOutgoingQuote() != null ?
new Quote(message.getOutgoingQuote().getId(),
@ -1541,8 +1482,7 @@ public class MmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1;
long revealDuration = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_DURATION));
long revealStartTime = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.REVEAL_START_TIME));
boolean isViewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) == 1;
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
readReceiptCount = 0;
@ -1563,8 +1503,7 @@ public class MmsDatabase extends MessagingDatabase {
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount,
threadId, body, slideDeck, partCount, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted,
revealDuration, revealStartTime,
readReceiptCount, quote, contacts, previews, unidentified);
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified);
}
private Recipient getRecipientFor(String serialized) {

View File

@ -71,8 +71,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION,
MmsDatabase.REVEAL_START_TIME};
MmsDatabase.VIEW_ONCE};
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@ -273,8 +272,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION,
MmsDatabase.REVEAL_START_TIME};
MmsDatabase.VIEW_ONCE};
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
@ -301,8 +299,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS,
MmsDatabase.LINK_PREVIEWS,
MmsDatabase.REVEAL_DURATION,
MmsDatabase.REVEAL_START_TIME};
MmsDatabase.VIEW_ONCE};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
@ -373,8 +370,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsDatabase.QUOTE_ATTACHMENT);
mmsColumnsPresent.add(MmsDatabase.SHARED_CONTACTS);
mmsColumnsPresent.add(MmsDatabase.LINK_PREVIEWS);
mmsColumnsPresent.add(MmsDatabase.REVEAL_DURATION);
mmsColumnsPresent.add(MmsDatabase.REVEAL_START_TIME);
mmsColumnsPresent.add(MmsDatabase.VIEW_ONCE);
Set<String> smsColumnsPresent = new HashSet<>();
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.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
@ -630,7 +629,7 @@ public class ThreadDatabase extends Database {
SlideDeck slideDeck = ((MediaMmsMessageRecord)record).getSlideDeck();
Slide thumbnail = slideDeck.getThumbnailSlide();
if (thumbnail != null && ((MmsMessageRecord) record).getRevealDuration() == 0) {
if (thumbnail != null && !((MmsMessageRecord) record).isViewOnce()) {
return thumbnail.getThumbnailUri();
}
@ -650,7 +649,7 @@ public class ThreadDatabase extends Database {
}
private @Nullable Extra getExtrasFor(MessageRecord record) {
if (record.isMms() && ((MmsMessageRecord) record).getRevealDuration() > 0) {
if (record.isMms() && ((MmsMessageRecord) record).isViewOnce()) {
return Extra.forRevealableMessage();
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) {
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 STICKERS = 21;
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 final Context context;
private final 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");
}
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();
} finally {
db.endTransaction();

View File

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

View File

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

View File

@ -57,7 +57,7 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
super(id, "", conversationRecipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
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.messageSize = messageSize;

View File

@ -121,7 +121,7 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
return BlobProvider.getInstance()
.forData(data)
.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) {
Log.w(TAG, e);
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);
}
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);
return new GroupActionResult(groupRecipient, threadId);

View File

@ -212,7 +212,7 @@ public class GroupMessageProcessor {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), 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 messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
@ -222,7 +222,7 @@ public class GroupMessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
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);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@ -41,10 +41,10 @@ public final class JobManagerFactories {
put(MultiDeviceGroupUpdateJob.KEY, new MultiDeviceGroupUpdateJob.Factory());
put(MultiDeviceProfileKeyUpdateJob.KEY, new MultiDeviceProfileKeyUpdateJob.Factory());
put(MultiDeviceReadUpdateJob.KEY, new MultiDeviceReadUpdateJob.Factory());
put(MultiDeviceRevealUpdateJob.KEY, new MultiDeviceRevealUpdateJob.Factory());
put(MultiDeviceStickerPackOperationJob.KEY, new MultiDeviceStickerPackOperationJob.Factory());
put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory());
put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory());
put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory());
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
put(PushDecryptJob.KEY, new PushDecryptJob.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)));
}
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);
if (insertResult.isPresent()) {

View File

@ -16,25 +16,25 @@ import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
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.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
public class MultiDeviceRevealUpdateJob extends BaseJob {
public class MultiDeviceViewOnceOpenJob extends BaseJob {
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 SerializableSyncMessageId messageId;
public MultiDeviceRevealUpdateJob(SyncMessageId messageId) {
public MultiDeviceViewOnceOpenJob(SyncMessageId messageId) {
this(new Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
@ -43,7 +43,7 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
messageId);
}
private MultiDeviceRevealUpdateJob(@NonNull Parameters parameters, @NonNull SyncMessageId syncMessageId) {
private MultiDeviceViewOnceOpenJob(@NonNull Parameters parameters, @NonNull SyncMessageId syncMessageId) {
super(parameters);
this.messageId = new SerializableSyncMessageId(syncMessageId.getAddress().toPhoneString(), syncMessageId.getTimetamp());
}
@ -74,9 +74,9 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
}
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
@ -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
public @NonNull MultiDeviceRevealUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
public @NonNull MultiDeviceViewOnceOpenJob create(@NonNull Parameters parameters, @NonNull Data data) {
SerializableSyncMessageId messageId;
try {
@ -118,7 +118,7 @@ public class MultiDeviceRevealUpdateJob extends BaseJob {
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.NotificationChannels;
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.sms.IncomingEncryptedMessage;
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.OfferMessage;
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.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
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.push.SignalServiceAddress;
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());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
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.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
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()),
content.getSenderDevice(),
content.getTimestamp(),
"", Optional.absent(), 0, 0,
"", Optional.absent(), 0,
content.isNeedsReceipt());
Long threadId;
@ -515,7 +513,7 @@ public class PushDecryptJob extends BaseJob {
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
0,
false,
content.isNeedsReceipt(),
Optional.absent(),
message.getGroupInfo(),
@ -676,11 +674,14 @@ public class PushDecryptJob extends BaseJob {
MessageNotifier.updateNotification(context);
}
private void handleSynchronizeMessageTimerReadMessage(@NonNull MessageTimerReadMessage timerMessage, long envelopeTimestamp) {
SyncMessageId messageId = new SyncMessageId(Address.fromExternal(context, timerMessage.getSender()), timerMessage.getTimestamp());
private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
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);
ApplicationContext.getInstance(context).getRevealableMessageManager().scheduleIfNecessary();
if (record != null && record.isMms()) {
DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(record.getId());
}
MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
MessageNotifier.cancelDelayedNotifications();
@ -707,7 +708,7 @@ public class PushDecryptJob extends BaseJob {
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false,
message.getMessageTimerInSeconds() * 1000,
message.isViewOnce(),
content.isNeedsReceipt(),
message.getBody(),
message.getGroupInfo(),
@ -747,8 +748,8 @@ public class PushDecryptJob extends BaseJob {
if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
if (message.getMessageTimerInSeconds() > 0) {
ApplicationContext.getInstance(context).getRevealableMessageManager().scheduleIfNecessary();
if (message.isViewOnce()) {
ApplicationContext.getInstance(context).getViewOnceMessageManager().scheduleIfNecessary();
}
}
}
@ -780,8 +781,8 @@ public class PushDecryptJob extends BaseJob {
Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker());
Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts());
Optional<List<LinkPreview>> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or(""));
long messageTimer = message.getMessage().getMessageTimerInSeconds() * 1000;
List<Attachment> syncAttachments = messageTimer == 0 ? PointerAttachment.forPointers(message.getMessage().getAttachments()) : Collections.emptyList();
boolean viewOnce = message.getMessage().isViewOnce();
List<Attachment> syncAttachments = viewOnce ? Collections.emptyList() : PointerAttachment.forPointers(message.getMessage().getAttachments());
if (sticker.isPresent()) {
syncAttachments.add(sticker.get());
@ -791,7 +792,7 @@ public class PushDecryptJob extends BaseJob {
syncAttachments,
message.getTimestamp(), -1,
message.getMessage().getExpiresInSeconds() * 1000,
messageTimer,
viewOnce,
ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(),
sharedContacts.or(Collections.emptyList()),
previews.or(Collections.emptyList()),
@ -921,7 +922,6 @@ public class PushDecryptJob extends BaseJob {
message.getTimestamp(), body,
message.getGroupInfo(),
message.getExpiresInSeconds() * 1000L,
message.getMessageTimerInSeconds() * 1000L,
content.isNeedsReceipt());
textMessage = new IncomingEncryptedMessage(textMessage, body);
@ -956,7 +956,7 @@ public class PushDecryptJob extends BaseJob {
long messageId;
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);
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) {
if (message.getMessageTimerInSeconds() > 0) {
if (message.isViewOnce()) {
return !message.getAttachments().isPresent() ||
message.getAttachments().get().size() != 1 ||
!MediaUtil.isImageType(message.getAttachments().get().get(0).getContentType().toLowerCase());
@ -1229,7 +1229,7 @@ public class PushDecryptJob extends BaseJob {
if (message.isMms()) {
MmsMessageRecord mmsMessage = (MmsMessageRecord) message;
if (mmsMessage.getRevealDuration() == 0) {
if (!mmsMessage.isViewOnce()) {
attachments = mmsMessage.getSlideDeck().asAttachments();
if (attachments.isEmpty()) {
@ -1335,7 +1335,7 @@ public class PushDecryptJob extends BaseJob {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender),
senderDevice, timestamp, "",
group, 0, 0, false);
group, 0, false);
textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage);

View File

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

View File

@ -164,11 +164,8 @@ public class PushMediaSendJob extends PushSendJob {
expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn());
}
if (message.getRevealDuration() > 0) {
database.markRevealStarted(messageId);
ApplicationContext.getInstance(context)
.getRevealableMessageManager()
.scheduleIfNecessary();
if (message.isViewOnce()) {
DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForMessage(messageId);
}
log(TAG, "Sent message: " + messageId);
@ -230,7 +227,7 @@ public class PushMediaSendJob extends PushSendJob {
.withAttachments(serviceAttachments)
.withTimestamp(message.getSentTimeMillis())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withMessageTimer((int) message.getRevealDuration() / 1000)
.withViewOnce(message.isViewOnce())
.withProfileKey(profileKey.orNull())
.withQuote(quote.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.logging.Log;
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.GlideApp;
import org.thoughtcrime.securesms.mms.ImageSlide;
@ -104,7 +104,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
public static final String EXTRA_MEDIA = "media";
public static final String EXTRA_MESSAGE = "message";
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";
@ -391,7 +391,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Uri uri = BlobProvider.getInstance()
.forData(data)
.withMimeType(MediaUtil.IMAGE_JPEG)
.createForSingleSessionOnDisk(this, e -> Log.w(TAG, "Failed to write to disk.", e));
.createForSingleSessionOnDisk(this);
return new Media(uri,
MediaUtil.IMAGE_JPEG,
System.currentTimeMillis(),
@ -527,14 +527,14 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
if (state == null) return;
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);
int captionBackground;
if (state.getRailState() == MediaSendViewModel.RailState.VIEWABLE) {
captionBackground = R.color.core_grey_90;
} else if (state.getRevealState() == RevealState.ENABLED) {
} else if (state.getViewOnceState() == ViewOnceState.ENABLED) {
captionBackground = 0;
} else {
captionBackground = R.color.transparent_black_70;
@ -572,7 +572,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
break;
}
switch (state.getRevealState()) {
switch (state.getViewOnceState()) {
case ENABLED:
revealButton.setVisibility(View.VISIBLE);
revealButton.setImageResource(R.drawable.ic_view_once_32);
@ -815,7 +815,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Uri uri = BlobProvider.getInstance()
.forData(outputStream.toByteArray())
.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());
@ -859,9 +859,9 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
Intent intent = new Intent();
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_REVEAL_DURATION, viewModel.getRevealDuration());
intent.putExtra(EXTRA_VIEW_ONCE, viewModel.isViewOnce());
setResult(RESULT_OK, intent);
} else {
@ -885,7 +885,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
System.currentTimeMillis(),
-1,
recipient.getExpireMessages() * 1000,
viewModel.getRevealDuration(),
viewModel.isViewOnce(),
ThreadDatabase.DistributionTypes.DEFAULT,
null,
Collections.emptyList(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,12 +18,12 @@ public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
long sentTimeMillis,
int distributionType,
long expiresIn,
long revealDuration,
boolean viewOnce,
@Nullable QuoteModel quote,
@NonNull List<Contact> contacts,
@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) {

View File

@ -76,7 +76,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
if (recipient.isGroupRecipient()) {
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);
} else {
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");

View File

@ -465,7 +465,7 @@ public class MessageNotifier {
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_sticker));
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));
} else if (record.isMms() && TextUtils.isEmpty(body) && !((MmsMessageRecord) record).getSlideDeck().getSlides().isEmpty()) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message));

View File

@ -76,7 +76,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
switch (replyMethod) {
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);
break;
}

View File

@ -25,6 +25,8 @@ import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/**
* Allows for the creation and retrieval of blobs.
@ -173,7 +175,34 @@ public class BlobProvider {
}
@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();
String directory = getDirectory(blobSpec.getStorageType());
File outputFile = new File(getOrCreateCacheDirectory(context, directory), buildFileName(blobSpec.id));
@ -182,6 +211,10 @@ public class BlobProvider {
SignalExecutors.UNBOUNDED.execute(() -> {
try {
Util.copy(blobSpec.getData(), outputStream);
if (successListener != null) {
successListener.onSuccess();
}
} catch (IOException e) {
if (errorListener != null) {
errorListener.onError(e);
@ -258,8 +291,23 @@ public class BlobProvider {
* period from one {@link Application#onCreate()} to the next.
*/
@WorkerThread
public Uri createForSingleSessionOnDisk(@NonNull Context context, @Nullable ErrorListener errorListener) throws IOException {
return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.SINGLE_SESSION_DISK), errorListener);
public Uri createForSingleSessionOnDisk(@NonNull Context context) throws IOException {
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.
*/
@WorkerThread
public Uri createForMultipleSessionsOnDisk(@NonNull Context context, @Nullable ErrorListener errorListener) throws IOException {
return writeBlobSpecToDisk(context, buildBlobSpec(StorageType.MULTI_SESSION_DISK), errorListener);
public Uri createForMultipleSessionsOnDisk(@NonNull Context context) throws IOException {
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 {
@WorkerThread
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.
*/
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 AttachmentDatabase attachmentDatabase;
public RevealableMessageManager(@NonNull Application application) {
public ViewOnceMessageManager(@NonNull Application application) {
super(application, "RevealableMessageManager");
this.mmsDatabase = DatabaseFactory.getMmsDatabase(application);
@ -38,8 +38,8 @@ public class RevealableMessageManager extends TimedEventManager<RevealExpiration
@WorkerThread
@Override
protected @Nullable RevealExpirationInfo getNextClosestEvent() {
RevealExpirationInfo expirationInfo = mmsDatabase.getNearestExpiringRevealableMessage();
protected @Nullable ViewOnceExpirationInfo getNextClosestEvent() {
ViewOnceExpirationInfo expirationInfo = mmsDatabase.getNearestExpiringViewOnceMessage();
if (expirationInfo != null) {
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
@Override
protected void executeEvent(@NonNull RevealExpirationInfo event) {
protected void executeEvent(@NonNull ViewOnceExpirationInfo event) {
Log.i(TAG, "Deleting attachments for message " + event.getMessageId());
attachmentDatabase.deleteAttachmentFilesForMessage(event.getMessageId());
}
@WorkerThread
@Override
protected long getDelayForEvent(@NonNull RevealExpirationInfo event) {
if (event.getRevealStartTime() == 0) {
long expiresAt = event.getReceiveTime() + RevealableUtil.MAX_LIFESPAN;
protected long getDelayForEvent(@NonNull ViewOnceExpirationInfo event) {
long expiresAt = event.getReceiveTime() + ViewOnceUtil.MAX_LIFESPAN;
long timeLeft = expiresAt - System.currentTimeMillis();
return Math.max(0, timeLeft);
} else {
long timeSinceStart = System.currentTimeMillis() - event.getRevealStartTime();
long timeLeft = event.getRevealDuration() - timeSinceStart;
return Math.max(0, timeLeft);
}
}
@AnyThread
@Override
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
public void onReceive(Context context, Intent intent) {
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 org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
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.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;
RevealableMessageRepository(@NonNull Context context) {
ViewOnceMessageRepository(@NonNull Context context) {
this.mmsDatabase = DatabaseFactory.getMmsDatabase(context);
}

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.revealable;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
@ -26,9 +24,9 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.logging.Log;
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 ProgressWheel progress;
@ -38,12 +36,12 @@ public class RevealableMessageView extends LinearLayout {
private int openedForegroundColor;
private int foregroundColor;
public RevealableMessageView(Context context) {
public ViewOnceMessageView(Context context) {
super(context);
init(null);
}
public RevealableMessageView(Context context, @Nullable AttributeSet attrs) {
public ViewOnceMessageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
@ -53,10 +51,10 @@ public class RevealableMessageView extends LinearLayout {
setOrientation(LinearLayout.HORIZONTAL);
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);
openedForegroundColor = typedArray.getColor(R.styleable.RevealableMessageView_revealable_openedForegroundColor, Color.BLACK);
unopenedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_unopenedForegroundColor, Color.BLACK);
openedForegroundColor = typedArray.getColor(R.styleable.ViewOnceMessageView_revealable_openedForegroundColor, Color.BLACK);
typedArray.recycle();
}
@ -101,12 +99,17 @@ public class RevealableMessageView extends LinearLayout {
}
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;
text.setText(R.string.RevealableMessageView_view_photo);
icon.setImageResource(0);
progress.setVisibility(VISIBLE);
} else if (downloadInProgress(messageRecord)) {
icon.setImageResource(R.drawable.ic_play_solid_24);
progress.setVisibility(GONE);
} else if (networkInProgress(messageRecord)) {
foregroundColor = unopenedForegroundColor;
text.setText("");
icon.setImageResource(0);
@ -116,16 +119,6 @@ public class RevealableMessageView extends LinearLayout {
text.setText(formatFileSize(messageRecord));
icon.setImageResource(R.drawable.ic_arrow_down_circle_outline_24);
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 {
foregroundColor = openedForegroundColor;
text.setText(R.string.RevealableMessageView_viewed);
@ -139,7 +132,7 @@ public class RevealableMessageView extends LinearLayout {
progress.setRimColor(Color.TRANSPARENT);
}
private boolean downloadInProgress(@NonNull MmsMessageRecord messageRecord) {
private boolean networkInProgress(@NonNull MmsMessageRecord messageRecord) {
if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return false;
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.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 RevealableMessageRepository repository;
private final ViewOnceMessageRepository repository;
private final MutableLiveData<Optional<MmsMessageRecord>> message;
private final ContentObserver observer;
private RevealableMessageViewModel(@NonNull Application application,
private ViewOnceMessageViewModel(@NonNull Application application,
long messageId,
@NonNull RevealableMessageRepository repository)
@NonNull ViewOnceMessageRepository repository)
{
this.application = application;
this.repository = repository;
@ -76,11 +76,11 @@ class RevealableMessageViewModel extends ViewModel {
private final Application application;
private final long messageId;
private final RevealableMessageRepository repository;
private final ViewOnceMessageRepository repository;
Factory(@NonNull Application application,
long messageId,
@NonNull RevealableMessageRepository repository)
@NonNull ViewOnceMessageRepository repository)
{
this.application = application;
this.messageId = messageId;
@ -90,7 +90,7 @@ class RevealableMessageViewModel extends ViewModel {
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//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 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

View File

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

View File

@ -73,7 +73,7 @@ public class GroupUtil {
.setType(GroupContext.Type.QUIT)
.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());
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);
else incoming = new IncomingIdentityDefaultMessage(incoming);
@ -98,7 +98,7 @@ public class IdentityUtil {
}
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);
else incoming = new IncomingIdentityDefaultMessage(incoming);
@ -128,14 +128,14 @@ public class IdentityUtil {
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) {
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);
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);
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 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";
@ -1103,11 +1103,11 @@ public class TextSecurePreferences {
}
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) {
return getBooleanPreference(context, REVEALABLE_MESSAGE_DEFAULT, false);
return getBooleanPreference(context, VIEW_ONCE_DEFAULT, false);
}
public static void setHasSeenCameraFirstTooltip(Context context, boolean value) {