From b2955d66e6a41e418dfdda5328600da39d417e64 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Sat, 17 Oct 2020 08:31:03 +0200 Subject: [PATCH] Disappearing messages for calls --- .../components/ConversationItemFooter.java | 2 +- .../components/ExpirationTimerView.java | 4 ++ .../conversation/ConversationUpdateItem.java | 38 ++++++++++++++ .../securesms/database/MessageDatabase.java | 6 +-- .../securesms/database/MmsDatabase.java | 6 +-- .../securesms/database/SmsDatabase.java | 18 ++++--- .../securesms/recipients/Recipient.java | 4 ++ .../securesms/service/WebRtcCallService.java | 51 ++++++++++++++----- .../res/layout/conversation_item_update.xml | 38 ++++++++++---- 9 files changed, 129 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index d170b0cb5f..fef3fb9ebb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -132,7 +132,7 @@ private void presentTimer(@NonNull final MessageRecord messageRecord) { messageRecord.getExpiresIn()); this.timerView.startAnimation(); - if (messageRecord.getExpireStarted() + messageRecord.getExpiresIn() <= System.currentTimeMillis()) { + if (timerView.isExpired()) { ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule(); } } else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java index 052e772d1f..8b74f0aae0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java @@ -59,6 +59,10 @@ public void setPercentComplete(float percentage) { setImageResource(frames[frame]); } + public boolean isExpired() { + return startedAt + expiresIn <= System.currentTimeMillis(); + } + public void startAnimation() { synchronized (this) { visible = true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 76ae6f78b6..3296c689e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -1,9 +1,11 @@ package org.thoughtcrime.securesms.conversation; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.ColorFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.os.AsyncTask; import android.text.SpannableString; import android.util.AttributeSet; import android.view.View; @@ -19,9 +21,12 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.Transformations; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.BindableConversationItem; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.VerifyIdentityActivity; +import org.thoughtcrime.securesms.components.ExpirationTimerView; +import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.model.LiveUpdateMessage; import org.thoughtcrime.securesms.database.model.MessageRecord; @@ -54,6 +59,7 @@ public final class ConversationUpdateItem extends LinearLayout private TextView title; private TextView body; private TextView date; + private ExpirationTimerView timer; private LiveRecipient sender; private ConversationMessage conversationMessage; private MessageRecord messageRecord; @@ -79,6 +85,7 @@ public void onFinishInflate() { this.title = findViewById(R.id.conversation_update_title); this.body = findViewById(R.id.conversation_update_body); this.date = findViewById(R.id.conversation_update_date); + this.timer = findViewById(R.id.conversation_update_expiration_timer); this.setOnClickListener(new InternalClickListener(null)); } @@ -192,6 +199,7 @@ else if (messageRecord.isIdentityVerified() || else setSelected(false); } + @SuppressLint("StaticFieldLeak") private void setCallRecord(MessageRecord messageRecord) { if (messageRecord.isIncomingCall()) icon.setImageResource(R.drawable.ic_call_received_grey600_24dp); else if (messageRecord.isOutgoingCall()) icon.setImageResource(R.drawable.ic_call_made_grey600_24dp); @@ -201,6 +209,36 @@ private void setCallRecord(MessageRecord messageRecord) { title.setVisibility(GONE); date.setVisibility(View.VISIBLE); + + if (messageRecord.getExpiresIn() > 0 && !messageRecord.isPending()) { + timer.setVisibility(View.VISIBLE); + timer.setPercentComplete(0); + + if (messageRecord.getExpireStarted() > 0) { + timer.setExpirationTime(messageRecord.getExpireStarted(), + messageRecord.getExpiresIn()); + timer.startAnimation(); + + if (timer.isExpired()) { + ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule(); + } + } else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + long id = messageRecord.getId(); + long expiresIn = messageRecord.getExpiresIn(); + + DatabaseFactory.getSmsDatabase(getContext()).markExpireStarted(id); + + ApplicationContext.getInstance(getContext()).getExpiringMessageManager().scheduleDeletion(id, false, expiresIn); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } else { + timer.setVisibility(View.GONE); + } } private void setTimerRecord(final MessageRecord messageRecord) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 6e7ae2e62e..4a3870487e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -124,9 +124,9 @@ public MessageDatabase(Context context, SQLCipherOpenHelper databaseHelper) { public abstract void addFailures(long messageId, List failure); public abstract void removeFailure(long messageId, NetworkFailure failure); - public abstract @NonNull Pair insertReceivedCall(@NonNull RecipientId address); - public abstract @NonNull Pair insertOutgoingCall(@NonNull RecipientId address); - public abstract @NonNull Pair insertMissedCall(@NonNull RecipientId address, long timestamp); + public abstract @NonNull Pair insertReceivedCall(@NonNull RecipientId address, long expiresIn); + public abstract @NonNull Pair insertOutgoingCall(@NonNull RecipientId address, long expiresIn); + public abstract @NonNull Pair insertMissedCall(@NonNull RecipientId address, long expiresIn, long timestamp); public abstract Optional insertMessageInbox(IncomingTextMessage message, long type); public abstract Optional insertMessageInbox(IncomingTextMessage message); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 0323bb9848..aef55beaa2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -379,17 +379,17 @@ public Pair updateBundleMessageBody(long messageId, String body) { } @Override - public @NonNull Pair insertReceivedCall(@NonNull RecipientId address) { + public @NonNull Pair insertReceivedCall(@NonNull RecipientId address, long expiresIn) { throw new UnsupportedOperationException(); } @Override - public @NonNull Pair insertOutgoingCall(@NonNull RecipientId address) { + public @NonNull Pair insertOutgoingCall(@NonNull RecipientId address, long expiresIn) { throw new UnsupportedOperationException(); } @Override - public @NonNull Pair insertMissedCall(@NonNull RecipientId address, long timestamp) { + public @NonNull Pair insertMissedCall(@NonNull RecipientId address, long expiresIn, long timestamp) { throw new UnsupportedOperationException(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 3f6d53862b..0379f0ff12 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -579,7 +579,8 @@ private List setMessagesRead(String where, String[] arguments cursor = database.query(TABLE_NAME, new String[] {ID, RECIPIENT_ID, DATE_SENT, TYPE, EXPIRES_IN, EXPIRE_STARTED, THREAD_ID}, where, arguments, null, null, null); while (cursor != null && cursor.moveToNext()) { - if (Types.isSecureType(cursor.getLong(cursor.getColumnIndex(TYPE)))) { + long type = cursor.getLong(3); + if (Types.isSecureType(type) || Types.isCallLog(type)) { long threadId = cursor.getLong(cursor.getColumnIndex(THREAD_ID)); RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndex(RECIPIENT_ID))); long dateSent = cursor.getLong(cursor.getColumnIndex(DATE_SENT)); @@ -644,21 +645,21 @@ public boolean hasReceivedAnyCallsSince(long threadId, long timestamp) { } @Override - public @NonNull Pair insertReceivedCall(@NonNull RecipientId address) { - return insertCallLog(address, Types.INCOMING_CALL_TYPE, false, System.currentTimeMillis()); + public @NonNull Pair insertReceivedCall(@NonNull RecipientId address, long expiresIn) { + return insertCallLog(address, Types.INCOMING_CALL_TYPE, expiresIn, false, System.currentTimeMillis()); } @Override - public @NonNull Pair insertOutgoingCall(@NonNull RecipientId address) { - return insertCallLog(address, Types.OUTGOING_CALL_TYPE, false, System.currentTimeMillis()); + public @NonNull Pair insertOutgoingCall(@NonNull RecipientId address, long expiresIn) { + return insertCallLog(address, Types.OUTGOING_CALL_TYPE, expiresIn, false, System.currentTimeMillis()); } @Override - public @NonNull Pair insertMissedCall(@NonNull RecipientId address, long timestamp) { - return insertCallLog(address, Types.MISSED_CALL_TYPE, true, timestamp); + public @NonNull Pair insertMissedCall(@NonNull RecipientId address, long expiresIn, long timestamp) { + return insertCallLog(address, Types.MISSED_CALL_TYPE, expiresIn, true, timestamp); } - private @NonNull Pair insertCallLog(@NonNull RecipientId recipientId, long type, boolean unread, long timestamp) { + private @NonNull Pair insertCallLog(@NonNull RecipientId recipientId, long type, long expiresIn, boolean unread, long timestamp) { Recipient recipient = Recipient.resolved(recipientId); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); @@ -670,6 +671,7 @@ public boolean hasReceivedAnyCallsSince(long threadId, long timestamp) { values.put(READ, unread ? 0 : 1); values.put(TYPE, type); values.put(THREAD_ID, threadId); + values.put(EXPIRES_IN, expiresIn); SQLiteDatabase db = databaseHelper.getWritableDatabase(); long messageId = db.insert(TABLE_NAME, null, values); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index d97ad07ddf..e33116979a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -714,6 +714,10 @@ public int getExpireMessages() { return expireMessages; } + public long getExpireMessagesInMillis() { + return getExpireMessages() * 1000L; + } + public boolean hasSeenFirstInviteReminder() { return insightsBannerTier.seen(InsightsBannerTier.TIER_ONE); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java index 94b0f826aa..f730219daa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.CallParticipant; @@ -421,7 +422,7 @@ private void handleReceivedOffer(Intent intent) { Log.i(TAG, "PSTN line is busy."); intent.putExtra(EXTRA_BROADCAST, true); handleSendBusy(intent); - insertMissedCall(remotePeer, true, serverReceivedTimestamp); + insertMissedCall(remotePeer.getRecipient(), true, serverReceivedTimestamp); return; } @@ -430,7 +431,7 @@ private void handleReceivedOffer(Intent intent) { intent.putExtra(EXTRA_BROADCAST, true); intent.putExtra(EXTRA_HANGUP_TYPE, HangupMessage.Type.NEED_PERMISSION.getCode()); handleSendHangup(intent); - insertMissedCall(remotePeer, true, serverReceivedTimestamp); + insertMissedCall(remotePeer.getRecipient(), true, serverReceivedTimestamp); return; } @@ -553,11 +554,37 @@ private void handleIsInCallQuery(Intent intent) { } } - private void insertMissedCall(@NonNull RemotePeer remotePeer, boolean signal, long timestamp) { - Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(remotePeer.getId(), timestamp); + private void insertMissedCall(@NonNull Recipient recipient, boolean signal, long timestamp) { + long expiresIn = recipient.getExpireMessagesInMillis(); + Pair messageAndThreadId = DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getId(), expiresIn, timestamp); ApplicationDependencies.getMessageNotifier().updateNotification(this, messageAndThreadId.second(), signal); } + private void insertDeniedCall(@NonNull Recipient recipient) { + long expiresIn = recipient.getExpireMessagesInMillis(); + DatabaseFactory.getSmsDatabase(this).insertMissedCall(recipient.getId(), expiresIn, System.currentTimeMillis()); + } + + private void insertOutgoingCall(@NonNull Recipient recipient) { + long expiresIn = recipient.getExpireMessagesInMillis(); + MessageDatabase database = DatabaseFactory.getSmsDatabase(this); + Pair messageAndThreadId = database.insertOutgoingCall(recipient.getId(), expiresIn); + if (expiresIn > 0) { + database.markExpireStarted(messageAndThreadId.first()); + ApplicationContext.getInstance(this).getExpiringMessageManager().scheduleDeletion(messageAndThreadId.first(), false, expiresIn); + } + } + + private void insertReceivedCall(@NonNull Recipient recipient) { + long expiresIn = recipient.getExpireMessagesInMillis(); + MessageDatabase database = DatabaseFactory.getSmsDatabase(this); + Pair messageAndThreadId = database.insertReceivedCall(recipient.getId(), expiresIn); + if (expiresIn > 0) { + database.markExpireStarted(messageAndThreadId.first()); + ApplicationContext.getInstance(this).getExpiringMessageManager().scheduleDeletion(messageAndThreadId.first(), false, expiresIn); + } + } + private void handleDenyCall(Intent intent) { if (activePeer == null) { Log.i(TAG, "handleDenyCall(): Ignoring for inactive call."); @@ -573,7 +600,7 @@ private void handleDenyCall(Intent intent) { try { callManager.hangup(); - DatabaseFactory.getSmsDatabase(this).insertMissedCall(activePeer.getId(), System.currentTimeMillis()); + insertDeniedCall(activePeer.getRecipient()); terminate(activePeer); } catch (CallException e) { callFailure("hangup() failed: ", e); @@ -710,7 +737,7 @@ private void handleStartOutgoingCall(Intent intent) { setCallInProgressNotification(TYPE_OUTGOING_RINGING, activePeer); - DatabaseFactory.getSmsDatabase(this).insertOutgoingCall(activePeer.getId()); + insertOutgoingCall(activePeer.getRecipient()); retrieveTurnServers().addListener(new SuccessOnlyListener>(this.activePeer.getState(), this.activePeer.getCallId()) { @Override @@ -802,7 +829,7 @@ private void handleAcceptCall(Intent intent) { Log.i(TAG, "handleAcceptCall(): call_id: " + activePeer.getCallId()); - DatabaseFactory.getSmsDatabase(this).insertReceivedCall(activePeer.getId()); + insertReceivedCall(activePeer.getRecipient()); acceptWithVideo = intent.getBooleanExtra(EXTRA_ANSWER_WITH_VIDEO, false); @@ -1167,7 +1194,7 @@ private void handleReceivedOfferExpired(Intent intent) { Log.i(TAG, "handleReceivedOfferExpired(): call_id: " + remotePeer.getCallId()); - insertMissedCall(remotePeer, true, remotePeer.getCallStartTimestamp()); + insertMissedCall(remotePeer.getRecipient(), true, remotePeer.getCallStartTimestamp()); terminate(remotePeer); } @@ -1196,7 +1223,7 @@ private void handleReceivedOfferWhileActive(Intent intent) { stopForeground(true); } - insertMissedCall(remotePeer, true, remotePeer.getCallStartTimestamp()); + insertMissedCall(remotePeer.getRecipient(), true, remotePeer.getCallStartTimestamp()); terminate(remotePeer); } @@ -1217,7 +1244,7 @@ private void handleEndedRemoteHangup(Intent intent) { boolean incomingBeforeAccept = remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING; if (incomingBeforeAccept) { - insertMissedCall(remotePeer, true, remotePeer.getCallStartTimestamp()); + insertMissedCall(remotePeer.getRecipient(), true, remotePeer.getCallStartTimestamp()); } terminate(remotePeer); @@ -1304,7 +1331,7 @@ private void handleEndedRemoteGlare(Intent intent) { boolean incomingBeforeAccept = remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING; if (incomingBeforeAccept) { - insertMissedCall(remotePeer, true, remotePeer.getCallStartTimestamp()); + insertMissedCall(remotePeer.getRecipient(), true, remotePeer.getCallStartTimestamp()); } terminate(remotePeer); @@ -1320,7 +1347,7 @@ private void handleEndedFailure(Intent intent) { } if (remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING) { - insertMissedCall(remotePeer, true, remotePeer.getCallStartTimestamp()); + insertMissedCall(remotePeer.getRecipient(), true, remotePeer.getCallStartTimestamp()); } terminate(remotePeer); diff --git a/app/src/main/res/layout/conversation_item_update.xml b/app/src/main/res/layout/conversation_item_update.xml index 944ffd4bae..62d832a004 100644 --- a/app/src/main/res/layout/conversation_item_update.xml +++ b/app/src/main/res/layout/conversation_item_update.xml @@ -56,19 +56,35 @@ android:textColor="?attr/conversation_item_update_text_color" tools:text="Gwen Stacy set the disappearing message timer to 1 hour" /> - + android:layout_marginBottom="0dp"> + + + + + +