2017-10-16 13:11:42 -07:00
/ *
2012-07-19 14:22:03 -07:00
* Copyright ( C ) 2011 Whisper Systems
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
2019-01-31 19:28:40 -08:00
package org.thoughtcrime.securesms.conversation ;
2012-07-19 14:22:03 -07:00
2017-11-24 22:00:30 -08:00
import android.Manifest ;
2017-11-14 10:49:54 -08:00
import android.annotation.SuppressLint ;
2016-11-20 15:56:47 -08:00
import android.annotation.TargetApi ;
2019-01-15 00:41:05 -08:00
import android.arch.lifecycle.ViewModelProviders ;
2013-06-18 14:43:27 -07:00
import android.content.ActivityNotFoundException ;
2013-03-05 19:32:15 -08:00
import android.content.BroadcastReceiver ;
import android.content.Context ;
import android.content.Intent ;
import android.content.IntentFilter ;
2019-02-25 15:21:37 -08:00
import android.content.pm.PackageManager ;
2015-07-07 14:25:41 -07:00
import android.content.res.Configuration ;
2015-06-30 17:45:39 -07:00
import android.content.res.TypedArray ;
2018-08-25 10:33:14 -07:00
import android.graphics.Bitmap ;
2018-08-06 14:42:22 -04:00
import android.graphics.BitmapFactory ;
2015-06-30 17:45:39 -07:00
import android.graphics.Color ;
2020-01-15 17:13:46 +11:00
import android.graphics.PorterDuff ;
2015-06-23 15:10:50 -07:00
import android.graphics.drawable.ColorDrawable ;
2019-02-25 15:21:37 -08:00
import android.hardware.Camera ;
2013-03-05 19:32:15 -08:00
import android.net.Uri ;
import android.os.AsyncTask ;
2015-11-24 11:47:50 -08:00
import android.os.Build ;
2013-03-05 19:32:15 -08:00
import android.os.Bundle ;
2020-01-16 14:35:51 +11:00
import android.os.Handler ;
2015-11-18 14:52:26 -08:00
import android.os.Vibrator ;
2016-03-29 17:52:54 -05:00
import android.provider.Browser ;
2016-11-20 15:56:47 -08:00
import android.provider.Telephony ;
2014-12-15 12:25:55 -08:00
import android.support.annotation.NonNull ;
2016-12-26 15:14:23 -08:00
import android.support.annotation.Nullable ;
2020-01-16 14:35:51 +11:00
import android.support.v4.content.LocalBroadcastManager ;
2018-08-06 14:42:22 -04:00
import android.support.v4.content.pm.ShortcutInfoCompat ;
import android.support.v4.content.pm.ShortcutManagerCompat ;
import android.support.v4.graphics.drawable.IconCompat ;
2016-09-25 20:04:13 -07:00
import android.support.v4.view.MenuItemCompat ;
2017-12-05 11:52:03 -08:00
import android.support.v7.app.ActionBar ;
2015-05-20 23:36:30 +02:00
import android.support.v7.app.AlertDialog ;
2019-02-01 09:06:59 -08:00
import android.support.v7.widget.SearchView ;
2019-05-24 14:31:48 -03:00
import android.support.v7.widget.Toolbar ;
2013-03-05 19:32:15 -08:00
import android.text.Editable ;
2016-12-26 15:14:23 -08:00
import android.text.TextUtils ;
2013-03-05 19:32:15 -08:00
import android.text.TextWatcher ;
2015-09-29 14:26:37 -07:00
import android.util.Pair ;
2020-01-22 10:46:04 +11:00
import android.util.TypedValue ;
2013-03-05 19:32:15 -08:00
import android.view.KeyEvent ;
2014-06-28 20:40:57 -07:00
import android.view.Menu ;
import android.view.MenuInflater ;
import android.view.MenuItem ;
2013-03-05 19:32:15 -08:00
import android.view.View ;
import android.view.View.OnClickListener ;
2014-05-28 20:53:34 -07:00
import android.view.View.OnFocusChangeListener ;
2013-03-05 19:32:15 -08:00
import android.view.View.OnKeyListener ;
2017-02-16 21:28:06 +01:00
import android.view.WindowManager ;
2013-03-05 19:32:15 -08:00
import android.view.inputmethod.EditorInfo ;
2019-07-19 13:15:17 +10:00
import android.view.inputmethod.InputMethodManager ;
2015-06-09 07:37:20 -07:00
import android.widget.Button ;
2015-05-18 10:26:32 -07:00
import android.widget.ImageButton ;
2020-01-17 10:37:06 +11:00
import android.widget.ImageView ;
2020-01-16 14:35:51 +11:00
import android.widget.ProgressBar ;
2013-03-05 19:32:15 -08:00
import android.widget.TextView ;
import android.widget.Toast ;
2012-07-19 14:22:03 -07:00
2019-02-25 13:58:10 -08:00
import com.annimon.stream.Stream ;
2015-12-18 14:37:11 -08:00
import com.google.android.gms.location.places.ui.PlacePicker ;
2013-11-25 17:00:20 -08:00
2017-02-17 20:43:24 -08:00
import org.greenrobot.eventbus.EventBus ;
import org.greenrobot.eventbus.Subscribe ;
import org.greenrobot.eventbus.ThreadMode ;
2019-06-28 12:32:23 +10:00
import org.jetbrains.annotations.NotNull ;
2019-01-31 19:28:40 -08:00
import org.thoughtcrime.securesms.ApplicationContext ;
import org.thoughtcrime.securesms.ExpirationDialog ;
import org.thoughtcrime.securesms.GroupCreateActivity ;
import org.thoughtcrime.securesms.GroupMembersDialog ;
import org.thoughtcrime.securesms.MediaOverviewActivity ;
import org.thoughtcrime.securesms.MuteDialog ;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity ;
import org.thoughtcrime.securesms.PromptMmsActivity ;
import org.thoughtcrime.securesms.RegistrationActivity ;
import org.thoughtcrime.securesms.ShortcutLauncherActivity ;
import org.thoughtcrime.securesms.TransportOption ;
import org.thoughtcrime.securesms.VerifyIdentityActivity ;
2015-11-18 14:52:26 -08:00
import org.thoughtcrime.securesms.audio.AudioRecorder ;
2015-10-23 17:00:51 -07:00
import org.thoughtcrime.securesms.audio.AudioSlidePlayer ;
2015-06-30 09:16:05 -07:00
import org.thoughtcrime.securesms.color.MaterialColor ;
2015-05-18 10:26:32 -07:00
import org.thoughtcrime.securesms.components.AnimatingToggle ;
2015-10-28 09:47:09 -07:00
import org.thoughtcrime.securesms.components.AttachmentTypeSelector ;
2015-03-11 14:23:45 -07:00
import org.thoughtcrime.securesms.components.ComposeText ;
2019-02-01 09:06:59 -08:00
import org.thoughtcrime.securesms.components.ConversationSearchBottomBar ;
2015-11-18 14:52:26 -08:00
import org.thoughtcrime.securesms.components.HidingLinearLayout ;
2015-09-21 17:41:27 -07:00
import org.thoughtcrime.securesms.components.InputAwareLayout ;
2015-11-18 14:52:26 -08:00
import org.thoughtcrime.securesms.components.InputPanel ;
2015-07-02 16:47:03 -07:00
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener ;
2015-06-09 07:37:20 -07:00
import org.thoughtcrime.securesms.components.SendButton ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.components.TooltipPopup ;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider ;
2018-04-26 17:03:54 -07:00
import org.thoughtcrime.securesms.components.emoji.EmojiStrings ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard ;
2017-06-06 18:03:09 -07:00
import org.thoughtcrime.securesms.components.identity.UntrustedSendDialog ;
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView ;
import org.thoughtcrime.securesms.components.identity.UnverifiedSendDialog ;
2016-01-04 13:02:22 -08:00
import org.thoughtcrime.securesms.components.location.SignalPlace ;
2017-11-15 16:29:00 -08:00
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder ;
2015-11-09 14:51:53 -08:00
import org.thoughtcrime.securesms.components.reminder.InviteReminder ;
import org.thoughtcrime.securesms.components.reminder.ReminderView ;
2018-06-11 09:37:01 -07:00
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder ;
2017-11-15 16:29:00 -08:00
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder ;
2013-10-17 02:28:36 +02:00
import org.thoughtcrime.securesms.contacts.ContactAccessor ;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData ;
2018-04-26 17:03:54 -07:00
import org.thoughtcrime.securesms.contactshare.Contact ;
import org.thoughtcrime.securesms.contactshare.ContactShareEditActivity ;
import org.thoughtcrime.securesms.contactshare.ContactUtil ;
2018-10-29 15:14:31 -07:00
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher ;
2017-06-06 18:03:09 -07:00
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable ;
2014-11-03 15:16:04 -08:00
import org.thoughtcrime.securesms.crypto.SecurityEvent ;
2017-07-26 09:59:15 -07:00
import org.thoughtcrime.securesms.database.Address ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.database.DatabaseFactory ;
import org.thoughtcrime.securesms.database.DraftDatabase ;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft ;
2014-12-11 17:13:01 -08:00
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts ;
2018-05-22 02:13:10 -07:00
import org.thoughtcrime.securesms.database.GroupDatabase ;
2017-06-06 18:03:09 -07:00
import org.thoughtcrime.securesms.database.IdentityDatabase ;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord ;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus ;
2016-10-10 11:13:37 -07:00
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo ;
2014-12-11 17:13:01 -08:00
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types ;
2019-04-12 16:22:38 -03:00
import org.thoughtcrime.securesms.database.RecipientDatabase ;
2017-08-22 10:44:04 -07:00
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState ;
2019-12-06 13:00:08 +11:00
import org.thoughtcrime.securesms.database.SmsDatabase ;
2013-04-25 18:59:49 -07:00
import org.thoughtcrime.securesms.database.ThreadDatabase ;
2017-06-06 18:03:09 -07:00
import org.thoughtcrime.securesms.database.identity.IdentityRecordList ;
2018-02-07 14:01:37 -08:00
import org.thoughtcrime.securesms.database.model.MessageRecord ;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.database.model.StickerRecord ;
2017-11-15 16:29:00 -08:00
import org.thoughtcrime.securesms.events.ReminderUpdateEvent ;
2018-03-20 11:27:11 -07:00
import org.thoughtcrime.securesms.giph.ui.GiphyActivity ;
2016-08-26 16:53:23 -07:00
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob ;
2017-05-19 18:01:40 -07:00
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob ;
2018-06-11 09:37:01 -07:00
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob ;
2019-06-24 14:10:09 +10:00
import org.thoughtcrime.securesms.linkpreview.LinkPreview ;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository ;
2019-09-17 11:46:47 +10:00
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil ;
2019-06-24 14:10:09 +10:00
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel ;
2018-05-22 02:13:10 -07:00
import org.thoughtcrime.securesms.logging.Log ;
2019-10-08 13:23:03 +11:00
import org.thoughtcrime.securesms.loki.LokiMessageDatabase ;
2019-12-19 12:04:38 +01:00
import org.thoughtcrime.securesms.loki.LokiThreadDatabase ;
2019-07-22 09:38:12 +10:00
import org.thoughtcrime.securesms.loki.LokiThreadDatabaseDelegate ;
2019-10-30 09:59:11 +11:00
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities ;
2019-12-19 12:04:38 +01:00
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity ;
2020-02-01 15:12:06 +11:00
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities ;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase ;
2020-01-10 11:35:16 +11:00
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate ;
2020-01-17 10:37:06 +11:00
import org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateSelectionView ;
2020-02-01 15:12:06 +11:00
import org.thoughtcrime.securesms.loki.redesign.views.SessionRestoreBannerView ;
2019-06-24 14:10:09 +10:00
import org.thoughtcrime.securesms.mediasend.Media ;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.mms.AttachmentManager ;
2015-09-04 17:33:22 -07:00
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType ;
2015-11-18 14:52:26 -08:00
import org.thoughtcrime.securesms.mms.AudioSlide ;
2018-11-20 09:59:23 -08:00
import org.thoughtcrime.securesms.mms.GifSlide ;
2017-10-16 13:11:42 -07:00
import org.thoughtcrime.securesms.mms.GlideApp ;
import org.thoughtcrime.securesms.mms.GlideRequests ;
2018-09-20 13:27:18 -07:00
import org.thoughtcrime.securesms.mms.ImageSlide ;
2016-01-04 13:02:22 -08:00
import org.thoughtcrime.securesms.mms.LocationSlide ;
2015-07-28 13:17:01 -07:00
import org.thoughtcrime.securesms.mms.MediaConstraints ;
2016-08-15 20:23:56 -07:00
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage ;
2014-02-22 10:54:43 -08:00
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage ;
2014-02-24 00:19:54 -08:00
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage ;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage ;
2018-04-24 11:09:54 -07:00
import org.thoughtcrime.securesms.mms.QuoteId ;
import org.thoughtcrime.securesms.mms.QuoteModel ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.mms.Slide ;
2015-11-18 14:52:26 -08:00
import org.thoughtcrime.securesms.mms.SlideDeck ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.mms.StickerSlide ;
2019-02-26 19:29:52 -08:00
import org.thoughtcrime.securesms.mms.TextSlide ;
2018-11-20 09:59:23 -08:00
import org.thoughtcrime.securesms.mms.VideoSlide ;
2016-10-10 11:13:37 -07:00
import org.thoughtcrime.securesms.notifications.MarkReadReceiver ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.notifications.MessageNotifier ;
2018-08-16 09:47:43 -07:00
import org.thoughtcrime.securesms.notifications.NotificationChannels ;
2017-11-24 22:00:30 -08:00
import org.thoughtcrime.securesms.permissions.Permissions ;
2017-08-22 11:51:01 -07:00
import org.thoughtcrime.securesms.profiles.GroupShareProfileView ;
2019-02-25 17:47:30 -08:00
import org.thoughtcrime.securesms.providers.BlobProvider ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.recipients.Recipient ;
2019-06-24 14:10:09 +10:00
import org.thoughtcrime.securesms.recipients.RecipientExporter ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.recipients.RecipientFormattingException ;
2017-08-01 08:56:00 -07:00
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener ;
2019-02-01 09:06:59 -08:00
import org.thoughtcrime.securesms.search.model.MessageResult ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.service.KeyCachingService ;
import org.thoughtcrime.securesms.sms.MessageSender ;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage ;
2014-02-19 13:46:49 -08:00
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import org.thoughtcrime.securesms.sms.OutgoingTextMessage ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider ;
import org.thoughtcrime.securesms.stickers.StickerLocator ;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity ;
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent ;
import org.thoughtcrime.securesms.stickers.StickerSearchRepository ;
2018-08-25 10:33:14 -07:00
import org.thoughtcrime.securesms.util.BitmapUtil ;
2018-04-26 17:03:54 -07:00
import org.thoughtcrime.securesms.util.CommunicationActions ;
2020-01-17 10:37:06 +11:00
import org.thoughtcrime.securesms.util.DateUtils ;
2014-03-01 01:32:00 +01:00
import org.thoughtcrime.securesms.util.Dialogs ;
2014-03-26 14:49:40 -07:00
import org.thoughtcrime.securesms.util.DirectoryHelper ;
2013-06-29 18:03:55 -07:00
import org.thoughtcrime.securesms.util.DynamicLanguage ;
2019-05-24 14:31:48 -03:00
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme ;
2016-09-25 20:04:13 -07:00
import org.thoughtcrime.securesms.util.ExpirationUtil ;
2014-02-22 10:54:43 -08:00
import org.thoughtcrime.securesms.util.GroupUtil ;
2017-06-06 18:03:09 -07:00
import org.thoughtcrime.securesms.util.IdentityUtil ;
2015-09-04 17:33:22 -07:00
import org.thoughtcrime.securesms.util.MediaUtil ;
2017-12-05 11:52:03 -08:00
import org.thoughtcrime.securesms.util.ServiceUtil ;
2013-07-09 18:26:18 -07:00
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode ;
2014-11-12 11:15:05 -08:00
import org.thoughtcrime.securesms.util.Util ;
2015-10-13 21:44:01 -07:00
import org.thoughtcrime.securesms.util.ViewUtil ;
2015-11-09 14:51:53 -08:00
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener ;
2015-06-22 08:46:43 -07:00
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture ;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture ;
2017-01-18 18:46:40 -08:00
import org.thoughtcrime.securesms.util.views.Stub ;
2016-03-23 10:34:41 -07:00
import org.whispersystems.libsignal.InvalidMessageException ;
import org.whispersystems.libsignal.util.guava.Optional ;
2019-10-10 13:53:02 +11:00
import org.whispersystems.signalservice.loki.api.LokiAPI ;
2020-01-22 10:46:04 +11:00
import org.whispersystems.signalservice.loki.api.LokiPublicChat ;
2020-02-11 09:38:05 +11:00
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI ;
2020-01-17 10:37:06 +11:00
import org.whispersystems.signalservice.loki.api.PairingAuthorisation ;
2019-08-07 12:00:12 +10:00
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus ;
2019-06-24 14:10:09 +10:00
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus ;
2019-10-11 16:37:28 +11:00
import org.whispersystems.signalservice.loki.messaging.Mention ;
2020-01-22 15:28:39 +11:00
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import java.io.IOException ;
2019-02-26 19:29:52 -08:00
import java.text.SimpleDateFormat ;
2019-10-11 11:13:34 +11:00
import java.util.ArrayList ;
2018-04-26 17:03:54 -07:00
import java.util.Collections ;
2019-02-26 19:29:52 -08:00
import java.util.Date ;
2020-01-17 10:37:06 +11:00
import java.util.HashSet ;
2017-08-01 08:56:00 -07:00
import java.util.LinkedList ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
import java.util.List ;
2019-02-26 19:29:52 -08:00
import java.util.Locale ;
2019-12-06 13:00:08 +11:00
import java.util.Set ;
2015-11-18 14:52:26 -08:00
import java.util.concurrent.ExecutionException ;
2018-07-25 11:30:48 -04:00
import java.util.concurrent.atomic.AtomicBoolean ;
import java.util.concurrent.atomic.AtomicInteger ;
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 12:22:04 -07:00
2019-10-10 15:30:34 +11:00
import kotlin.Unit ;
2019-07-26 12:20:55 +10:00
import network.loki.messenger.R ;
2015-03-11 14:23:45 -07:00
import static org.thoughtcrime.securesms.TransportOption.Type ;
2014-02-22 10:54:43 -08:00
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord ;
2017-06-06 18:03:09 -07:00
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK ;
2014-02-22 10:54:43 -08:00
2012-07-19 14:22:03 -07:00
/ * *
* Activity for displaying a message thread , as well as
* composing / sending a new message into that thread .
*
* @author Moxie Marlinspike
*
* /
2017-11-14 10:49:54 -08:00
@SuppressLint ( " StaticFieldLeak " )
2014-06-28 20:40:57 -07:00
public class ConversationActivity extends PassphraseRequiredActionBarActivity
2014-04-15 12:43:14 +02:00
implements ConversationFragment . ConversationFragmentListener ,
2015-02-10 03:15:50 -08:00
AttachmentManager . AttachmentListener ,
2017-11-24 22:00:30 -08:00
RecipientModifiedListener ,
2015-06-08 11:07:46 -07:00
OnKeyboardShownListener ,
2016-12-26 15:14:23 -08:00
InputPanel . Listener ,
2019-02-01 09:06:59 -08:00
InputPanel . MediaListener ,
2019-02-25 15:21:37 -08:00
ComposeText . CursorPositionChangedListener ,
2019-06-28 12:32:23 +10:00
ConversationSearchBottomBar . EventListener ,
2019-08-07 16:48:54 +10:00
StickerKeyboardProvider . StickerEventListener ,
LokiThreadDatabaseDelegate ,
2019-12-06 13:00:08 +11:00
FriendRequestViewDelegate
2013-04-25 18:59:49 -07:00
{
2014-02-18 16:28:54 -08:00
private static final String TAG = ConversationActivity . class . getSimpleName ( ) ;
2012-07-19 14:22:03 -07:00
2017-08-01 08:56:00 -07:00
public static final String ADDRESS_EXTRA = " address " ;
2013-04-25 18:59:49 -07:00
public static final String THREAD_ID_EXTRA = " thread_id " ;
2015-12-01 01:38:37 +01:00
public static final String IS_ARCHIVED_EXTRA = " is_archived " ;
2015-11-18 12:54:40 -08:00
public static final String TEXT_EXTRA = " draft_text " ;
2018-11-20 09:59:23 -08:00
public static final String MEDIA_EXTRA = " media_list " ;
2019-04-17 10:21:30 -04:00
public static final String STICKER_EXTRA = " media_list " ;
2013-04-25 18:59:49 -07:00
public static final String DISTRIBUTION_TYPE_EXTRA = " distribution_type " ;
2017-01-19 11:37:19 -08:00
public static final String TIMING_EXTRA = " timing " ;
2017-02-13 22:35:47 -08:00
public static final String LAST_SEEN_EXTRA = " last_seen " ;
2018-04-06 18:15:24 -07:00
public static final String STARTING_POSITION_EXTRA = " starting_position " ;
2013-02-02 20:37:40 -08:00
2018-04-26 17:03:54 -07:00
private static final int PICK_GALLERY = 1 ;
private static final int PICK_DOCUMENT = 2 ;
private static final int PICK_AUDIO = 3 ;
private static final int PICK_CONTACT = 4 ;
private static final int GET_CONTACT_DETAILS = 5 ;
private static final int GROUP_EDIT = 6 ;
private static final int TAKE_PHOTO = 7 ;
private static final int ADD_CONTACT = 8 ;
private static final int PICK_LOCATION = 9 ;
private static final int PICK_GIF = 10 ;
private static final int SMS_DEFAULT = 11 ;
2019-03-13 16:05:25 -07:00
private static final int MEDIA_SENDER = 12 ;
2012-07-19 14:22:03 -07:00
2019-10-10 14:44:08 +11:00
private GlideRequests glideRequests ;
protected ComposeText composeText ;
private AnimatingToggle buttonToggle ;
private SendButton sendButton ;
private ImageButton attachButton ;
2020-01-10 11:35:16 +11:00
private TextView titleTextView ;
2019-10-10 14:44:08 +11:00
private TextView charactersLeft ;
private ConversationFragment fragment ;
private Button unblockButton ;
private Button makeDefaultSmsButton ;
private Button registerButton ;
private InputAwareLayout container ;
private View composePanel ;
protected Stub < ReminderView > reminderView ;
private Stub < UnverifiedBannerView > unverifiedBannerView ;
2017-08-22 11:51:01 -07:00
private Stub < GroupShareProfileView > groupShareProfileView ;
2018-10-29 15:14:31 -07:00
private TypingStatusTextWatcher typingTextWatcher ;
2019-10-10 14:44:08 +11:00
private MentionTextWatcher mentionTextWatcher ;
2019-02-01 09:06:59 -08:00
private ConversationSearchBottomBar searchNav ;
private MenuItem searchViewItem ;
2020-01-16 14:35:51 +11:00
private ProgressBar messageStatusProgressBar ;
2020-01-17 10:37:06 +11:00
private ImageView muteIndicatorImageView ;
2020-01-16 14:35:51 +11:00
private TextView actionBarSubtitleTextView ;
2012-07-19 14:22:03 -07:00
2015-10-28 09:47:09 -07:00
private AttachmentTypeSelector attachmentTypeSelector ;
private AttachmentManager attachmentManager ;
2015-11-18 14:52:26 -08:00
private AudioRecorder audioRecorder ;
2015-10-28 09:47:09 -07:00
private BroadcastReceiver securityUpdateReceiver ;
2019-04-17 10:21:30 -04:00
private Stub < MediaKeyboard > emojiDrawerStub ;
2015-11-18 14:52:26 -08:00
protected HidingLinearLayout quickAttachmentToggle ;
2018-08-10 12:18:02 -04:00
protected HidingLinearLayout inlineAttachmentToggle ;
2015-11-18 14:52:26 -08:00
private InputPanel inputPanel ;
2019-02-01 09:06:59 -08:00
2019-04-17 10:21:30 -04:00
private LinkPreviewViewModel linkPreviewViewModel ;
private ConversationSearchViewModel searchViewModel ;
private ConversationStickerViewModel stickerViewModel ;
2012-07-19 14:22:03 -07:00
2017-08-01 08:56:00 -07:00
private Recipient recipient ;
2014-02-17 20:25:40 -08:00
private long threadId ;
private int distributionType ;
2015-12-01 01:38:37 +01:00
private boolean archived ;
2015-09-29 14:26:37 -07:00
private boolean isSecureText ;
2020-01-13 10:11:20 +11:00
private boolean isDefaultSms = true ;
private boolean isMmsEnabled = true ;
private boolean isSecurityInitialized = false ;
private int expandedKeyboardHeight = 0 ;
private int collapsedKeyboardHeight = Integer . MAX_VALUE ;
private int keyboardHeight = 0 ;
2012-07-19 14:22:03 -07:00
2019-05-24 14:31:48 -03:00
private final IdentityRecordList identityRecords = new IdentityRecordList ( ) ;
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme ( ) ;
private final DynamicLanguage dynamicLanguage = new DynamicLanguage ( ) ;
2012-07-19 14:22:03 -07:00
2020-01-16 14:35:51 +11:00
private ArrayList < BroadcastReceiver > broadcastReceivers = new ArrayList < > ( ) ;
private String messageStatus = null ;
2019-10-11 11:13:34 +11:00
// Mentions
2020-01-16 15:15:08 +11:00
private View mentionCandidateSelectionViewContainer ;
2019-10-11 16:37:28 +11:00
private MentionCandidateSelectionView mentionCandidateSelectionView ;
2019-10-11 11:13:34 +11:00
private int currentMentionStartIndex = - 1 ;
private ArrayList < Mention > mentions = new ArrayList < > ( ) ;
private String oldText = " " ;
2019-11-13 16:05:37 +11:00
// Multi Device
private boolean isFriendsWithAnyDevice = false ;
2019-07-01 16:49:42 +10:00
2019-12-06 13:00:08 +11:00
// Restoration
2019-12-06 16:33:17 +11:00
protected SessionRestoreBannerView sessionRestoreBannerView ;
2019-12-06 13:00:08 +11:00
2012-07-19 14:22:03 -07:00
@Override
2014-12-15 12:25:55 -08:00
protected void onPreCreate ( ) {
2013-06-21 11:56:59 -07:00
dynamicTheme . onCreate ( this ) ;
2013-06-29 18:03:55 -07:00
dynamicLanguage . onCreate ( this ) ;
2014-12-15 12:25:55 -08:00
}
@Override
2018-02-01 19:22:48 -08:00
protected void onCreate ( Bundle state , boolean ready ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onCreate() " ) ;
2012-07-19 14:22:03 -07:00
setContentView ( R . layout . conversation_activity ) ;
2015-06-09 07:37:20 -07:00
2017-11-13 18:01:05 -08:00
TypedArray typedArray = obtainStyledAttributes ( new int [ ] { R . attr . conversation_background } ) ;
int color = typedArray . getColor ( 0 , Color . WHITE ) ;
typedArray . recycle ( ) ;
getWindow ( ) . getDecorView ( ) . setBackgroundColor ( color ) ;
2018-02-01 19:22:48 -08:00
fragment = initFragment ( R . id . fragment_content , new ConversationFragment ( ) , dynamicLanguage . getCurrentLocale ( ) ) ;
2019-06-28 12:32:23 +10:00
fragment . friendRequestViewDelegate = this ;
2012-07-19 14:22:03 -07:00
2020-01-16 14:35:51 +11:00
registerMessageStatusObserver ( " calculatingPoW " ) ;
registerMessageStatusObserver ( " contactingNetwork " ) ;
registerMessageStatusObserver ( " sendingMessage " ) ;
registerMessageStatusObserver ( " messageSent " ) ;
registerMessageStatusObserver ( " messageFailed " ) ;
2012-07-19 14:22:03 -07:00
initializeReceivers ( ) ;
2015-06-09 07:37:20 -07:00
initializeActionBar ( ) ;
2014-12-24 18:32:51 -08:00
initializeViews ( ) ;
2012-07-19 14:22:03 -07:00
initializeResources ( ) ;
2019-01-15 00:41:05 -08:00
initializeLinkPreviewObserver ( ) ;
2019-02-01 09:06:59 -08:00
initializeSearchObserver ( ) ;
2019-04-17 10:21:30 -04:00
initializeStickerObserver ( ) ;
2017-03-14 13:24:24 -07:00
initializeSecurity ( false , isDefaultSms ) . addListener ( new AssertedSuccessListener < Boolean > ( ) {
2015-10-01 17:46:47 -07:00
@Override
public void onSuccess ( Boolean result ) {
2017-06-14 09:53:22 -07:00
initializeProfiles ( ) ;
2018-07-25 11:30:48 -04:00
initializeDraft ( ) . addListener ( new AssertedSuccessListener < Boolean > ( ) {
@Override
2018-10-29 15:14:31 -07:00
public void onSuccess ( Boolean loadedDraft ) {
if ( loadedDraft ! = null & & loadedDraft ) {
2018-08-02 11:57:10 -04:00
Log . i ( TAG , " Finished loading draft " ) ;
2018-07-25 11:30:48 -04:00
Util . runOnMain ( ( ) - > {
if ( fragment ! = null & & fragment . isResumed ( ) ) {
fragment . moveToLastSeen ( ) ;
2018-08-02 11:57:10 -04:00
} else {
Log . w ( TAG , " Wanted to move to the last seen position, but the fragment was in an invalid state " ) ;
2018-07-25 11:30:48 -04:00
}
} ) ;
}
2018-10-29 15:14:31 -07:00
if ( TextSecurePreferences . isTypingIndicatorsEnabled ( ConversationActivity . this ) ) {
composeText . addTextChangedListener ( typingTextWatcher ) ;
}
2019-02-14 13:55:48 -08:00
composeText . setSelection ( composeText . length ( ) , composeText . length ( ) ) ;
2019-10-10 14:44:08 +11:00
composeText . addTextChangedListener ( mentionTextWatcher ) ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionView . setGlide ( glideRequests ) ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . setOnMentionCandidateSelected ( mentionCandidate - > {
mentions . add ( mentionCandidate ) ;
2019-10-10 15:30:34 +11:00
String oldText = composeText . getText ( ) . toString ( ) ;
2019-10-11 16:37:28 +11:00
String newText = oldText . substring ( 0 , currentMentionStartIndex ) + " @ " + mentionCandidate . getDisplayName ( ) ;
2019-10-10 15:30:34 +11:00
composeText . setText ( newText ) ;
composeText . setSelection ( newText . length ( ) ) ;
2019-10-11 14:59:13 +11:00
currentMentionStartIndex = - 1 ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . hide ( ) ;
ConversationActivity . this . oldText = newText ;
2019-10-10 15:30:34 +11:00
return Unit . INSTANCE ;
} ) ;
2018-07-25 11:30:48 -04:00
}
} ) ;
2015-10-01 17:46:47 -07:00
}
} ) ;
2019-09-05 09:38:36 +10:00
2019-12-06 16:33:17 +11:00
sessionRestoreBannerView . setOnRestore ( ( ) - > {
this . restoreSession ( ) ;
return Unit . INSTANCE ;
} ) ;
sessionRestoreBannerView . setOnDismiss ( ( ) - > {
// TODO: Maybe silence for x minutes?
DatabaseFactory . getLokiThreadDatabase ( ConversationActivity . this ) . removeAllSessionRestoreDevices ( threadId ) ;
updateSessionRestoreBanner ( ) ;
return Unit . INSTANCE ;
} ) ;
2019-10-11 16:37:28 +11:00
LokiAPIUtilities . INSTANCE . populateUserHexEncodedPublicKeyCacheIfNeeded ( threadId , this ) ;
2019-10-10 10:39:56 +11:00
2020-01-22 10:46:04 +11:00
LokiPublicChat publicChat = DatabaseFactory . getLokiThreadDatabase ( this ) . getPublicChat ( threadId ) ;
if ( publicChat ! = null ) {
ApplicationContext . getInstance ( this ) . getLokiPublicChatAPI ( ) . getUserCount ( publicChat . getChannel ( ) , publicChat . getServer ( ) ) . success ( integer - > {
updateSubtitleTextView ( ) ;
return Unit . INSTANCE ;
} ) ;
2019-09-05 09:38:36 +10:00
}
2020-01-22 10:46:04 +11:00
2020-01-13 10:11:20 +11:00
View rootView = findViewById ( R . id . rootView ) ;
rootView . getViewTreeObserver ( ) . addOnGlobalLayoutListener ( ( ) - > {
int height = rootView . getRootView ( ) . getHeight ( ) - rootView . getHeight ( ) ;
2020-01-13 16:47:50 +11:00
int thresholdInDP = 120 ;
float scale = getResources ( ) . getDisplayMetrics ( ) . density ;
int thresholdInPX = ( int ) ( thresholdInDP * scale ) ;
if ( expandedKeyboardHeight = = 0 | | height > thresholdInPX ) {
expandedKeyboardHeight = height ;
}
2020-01-13 10:11:20 +11:00
collapsedKeyboardHeight = Math . min ( collapsedKeyboardHeight , height ) ;
2020-01-14 10:26:18 +11:00
keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight ;
2020-01-13 10:11:20 +11:00
} ) ;
2012-07-19 14:22:03 -07:00
}
2020-01-16 14:35:51 +11:00
private void registerMessageStatusObserver ( String status ) {
BroadcastReceiver broadcastReceiver = new BroadcastReceiver ( ) {
@Override
public void onReceive ( Context context , Intent intent ) {
long timestamp = intent . getLongExtra ( " long " , 0 ) ;
handleMessageStatusChanged ( status , timestamp ) ;
}
} ;
broadcastReceivers . add ( broadcastReceiver ) ;
LocalBroadcastManager . getInstance ( this ) . registerReceiver ( broadcastReceiver , new IntentFilter ( status ) ) ;
2012-07-19 14:22:03 -07:00
}
2013-02-03 18:41:34 -08:00
@Override
2014-12-24 18:32:51 -08:00
protected void onNewIntent ( Intent intent ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onNewIntent() " ) ;
2016-01-30 15:33:47 -08:00
if ( isFinishing ( ) ) {
Log . w ( TAG , " Activity is finishing... " ) ;
return ;
}
2014-12-15 12:25:55 -08:00
2014-12-24 18:32:51 -08:00
if ( ! Util . isEmpty ( composeText ) | | attachmentManager . isAttachmentPresent ( ) ) {
saveDraft ( ) ;
2017-10-16 13:11:42 -07:00
attachmentManager . clear ( glideRequests , false ) ;
2018-10-29 15:14:31 -07:00
silentlySetComposeText ( " " ) ;
2014-12-24 18:32:51 -08:00
}
setIntent ( intent ) ;
initializeResources ( ) ;
2017-03-14 13:24:24 -07:00
initializeSecurity ( false , isDefaultSms ) . addListener ( new AssertedSuccessListener < Boolean > ( ) {
2015-10-01 17:46:47 -07:00
@Override
public void onSuccess ( Boolean result ) {
initializeDraft ( ) ;
}
} ) ;
2014-10-08 21:11:02 +03:00
2014-12-24 18:32:51 -08:00
if ( fragment ! = null ) {
fragment . onNewIntent ( ) ;
}
2019-02-28 16:51:10 -08:00
searchNav . setVisibility ( View . GONE ) ;
2013-02-03 18:41:34 -08:00
}
2012-07-19 14:22:03 -07:00
@Override
protected void onResume ( ) {
super . onResume ( ) ;
2013-06-21 11:56:59 -07:00
dynamicTheme . onResume ( this ) ;
2013-06-29 18:03:55 -07:00
dynamicLanguage . onResume ( this ) ;
2013-06-21 11:56:59 -07:00
2019-04-17 10:21:30 -04:00
EventBus . getDefault ( ) . register ( this ) ;
2014-02-22 10:54:43 -08:00
initializeEnabledCheck ( ) ;
2013-03-04 17:43:04 -08:00
initializeMmsEnabledCheck ( ) ;
2017-06-06 18:03:09 -07:00
initializeIdentityRecords ( ) ;
2015-07-20 19:25:54 -07:00
composeText . setTransport ( sendButton . getSelectedTransport ( ) ) ;
2015-06-09 07:37:20 -07:00
2020-01-10 11:35:16 +11:00
updateTitleTextView ( glideRequests , recipient ) ;
2020-01-17 10:37:06 +11:00
updateSubtitleTextView ( ) ;
2017-08-01 08:56:00 -07:00
setActionBarColor ( recipient . getColor ( ) ) ;
setBlockedUserState ( recipient , isSecureText , isDefaultSms ) ;
2017-08-22 11:51:01 -07:00
setGroupShareProfileReminder ( recipient ) ;
2012-07-19 14:22:03 -07:00
calculateCharactersRemaining ( ) ;
2013-02-03 18:41:34 -08:00
MessageNotifier . setVisibleThread ( threadId ) ;
markThreadAsRead ( ) ;
2017-01-19 11:37:19 -08:00
2019-07-22 09:38:12 +10:00
DatabaseFactory . getLokiThreadDatabase ( this ) . setDelegate ( this ) ;
2019-06-25 11:48:39 +10:00
updateInputPanel ( ) ;
2019-12-06 16:33:17 +11:00
updateSessionRestoreBanner ( ) ;
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onResume() Finished: " + ( System . currentTimeMillis ( ) - getIntent ( ) . getLongExtra ( TIMING_EXTRA , 0 ) ) ) ;
2012-07-19 14:22:03 -07:00
}
@Override
2013-03-04 17:43:04 -08:00
protected void onPause ( ) {
super . onPause ( ) ;
MessageNotifier . setVisibleThread ( - 1L ) ;
2014-04-08 10:45:55 -07:00
if ( isFinishing ( ) ) overridePendingTransition ( R . anim . fade_scale_in , R . anim . slide_to_right ) ;
2015-11-18 14:52:26 -08:00
inputPanel . onPause ( ) ;
2017-02-13 22:35:47 -08:00
fragment . setLastSeen ( System . currentTimeMillis ( ) ) ;
markLastSeen ( ) ;
2015-10-23 17:00:51 -07:00
AudioSlidePlayer . stopAll ( ) ;
2017-02-17 20:43:24 -08:00
EventBus . getDefault ( ) . unregister ( this ) ;
2019-07-19 13:15:17 +10:00
2019-07-22 09:38:12 +10:00
DatabaseFactory . getLokiThreadDatabase ( this ) . setDelegate ( null ) ;
2017-02-17 20:43:24 -08:00
}
@Override
protected void onStop ( ) {
super . onStop ( ) ;
EventBus . getDefault ( ) . unregister ( this ) ;
2012-07-19 14:22:03 -07:00
}
2015-09-23 15:47:48 -07:00
@Override
public void onConfigurationChanged ( Configuration newConfig ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onConfigurationChanged( " + newConfig . orientation + " ) " ) ;
2015-06-08 11:07:46 -07:00
super . onConfigurationChanged ( newConfig ) ;
2015-07-20 19:25:54 -07:00
composeText . setTransport ( sendButton . getSelectedTransport ( ) ) ;
2017-01-19 11:31:41 -08:00
if ( emojiDrawerStub . resolved ( ) & & container . getCurrentInput ( ) = = emojiDrawerStub . get ( ) ) {
container . hideAttachedInput ( true ) ;
}
2015-06-08 11:07:46 -07:00
}
2012-07-19 14:22:03 -07:00
@Override
protected void onDestroy ( ) {
2014-12-24 18:32:51 -08:00
saveDraft ( ) ;
2017-08-01 08:56:00 -07:00
if ( recipient ! = null ) recipient . removeListener ( this ) ;
2016-08-15 20:23:56 -07:00
if ( securityUpdateReceiver ! = null ) unregisterReceiver ( securityUpdateReceiver ) ;
2020-01-16 14:35:51 +11:00
for ( BroadcastReceiver broadcastReceiver : broadcastReceivers ) {
LocalBroadcastManager . getInstance ( this ) . unregisterReceiver ( broadcastReceiver ) ;
}
2012-07-19 14:22:03 -07:00
super . onDestroy ( ) ;
}
@Override
2015-12-18 14:37:11 -08:00
public void onActivityResult ( final int reqCode , int resultCode , Intent data ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onActivityResult called: " + reqCode + " , " + resultCode + " , " + data ) ;
2012-07-19 14:22:03 -07:00
super . onActivityResult ( reqCode , resultCode , data ) ;
2017-03-15 02:05:48 +01:00
if ( ( data = = null & & reqCode ! = TAKE_PHOTO & & reqCode ! = SMS_DEFAULT ) | |
( resultCode ! = RESULT_OK & & reqCode ! = SMS_DEFAULT ) )
{
2019-01-15 00:41:05 -08:00
updateLinkPreviewState ( ) ;
2017-03-15 02:05:48 +01:00
return ;
}
2015-05-18 10:26:32 -07:00
2012-07-19 14:22:03 -07:00
switch ( reqCode ) {
2017-04-18 16:33:03 -07:00
case PICK_DOCUMENT :
setMedia ( data . getData ( ) , MediaType . DOCUMENT ) ;
2012-07-19 14:22:03 -07:00
break ;
case PICK_AUDIO :
2015-10-15 14:40:45 -07:00
setMedia ( data . getData ( ) , MediaType . AUDIO ) ;
2012-07-19 14:22:03 -07:00
break ;
2018-04-26 17:03:54 -07:00
case PICK_CONTACT :
2018-06-29 11:08:13 -07:00
if ( isSecureText & & ! isSmsForced ( ) ) {
openContactShareEditor ( data . getData ( ) ) ;
} else {
addAttachmentContactInfo ( data . getData ( ) ) ;
}
2018-04-26 17:03:54 -07:00
break ;
case GET_CONTACT_DETAILS :
sendSharedContact ( data . getParcelableArrayListExtra ( ContactShareEditActivity . KEY_CONTACTS ) ) ;
2013-10-17 02:28:36 +02:00
break ;
2014-02-20 15:41:52 -08:00
case GROUP_EDIT :
2017-10-16 13:11:42 -07:00
recipient = Recipient . from ( this , data . getParcelableExtra ( GroupCreateActivity . GROUP_ADDRESS_EXTRA ) , true ) ;
2017-08-01 08:56:00 -07:00
recipient . addListener ( this ) ;
2020-01-10 11:35:16 +11:00
updateTitleTextView ( glideRequests , recipient ) ;
2020-01-17 10:37:06 +11:00
updateSubtitleTextView ( ) ;
2018-08-16 09:47:43 -07:00
NotificationChannels . updateContactChannelName ( this , recipient ) ;
2017-08-01 08:56:00 -07:00
setBlockedUserState ( recipient , isSecureText , isDefaultSms ) ;
2015-06-09 07:37:20 -07:00
supportInvalidateOptionsMenu ( ) ;
2014-02-20 15:41:52 -08:00
break ;
2015-07-10 18:45:55 -07:00
case TAKE_PHOTO :
if ( attachmentManager . getCaptureUri ( ) ! = null ) {
2015-10-15 14:40:45 -07:00
setMedia ( attachmentManager . getCaptureUri ( ) , MediaType . IMAGE ) ;
2015-07-10 18:45:55 -07:00
}
break ;
2015-10-16 15:39:27 +02:00
case ADD_CONTACT :
2017-08-21 18:32:38 -07:00
recipient = Recipient . from ( this , recipient . getAddress ( ) , true ) ;
2017-08-01 08:56:00 -07:00
recipient . addListener ( this ) ;
2015-10-16 15:39:27 +02:00
fragment . reloadList ( ) ;
break ;
2015-12-18 14:37:11 -08:00
case PICK_LOCATION :
2016-01-04 13:02:22 -08:00
SignalPlace place = new SignalPlace ( PlacePicker . getPlace ( data , this ) ) ;
2018-01-24 19:17:44 -08:00
attachmentManager . setLocation ( place , getCurrentMediaConstraints ( ) ) ;
2015-12-18 14:37:11 -08:00
break ;
2016-10-16 19:05:07 -07:00
case PICK_GIF :
2018-03-20 11:27:11 -07:00
setMedia ( data . getData ( ) ,
2020-02-01 15:12:06 +11:00
MediaType . GIF ,
data . getIntExtra ( GiphyActivity . EXTRA_WIDTH , 0 ) ,
data . getIntExtra ( GiphyActivity . EXTRA_HEIGHT , 0 ) ) ;
2016-10-16 19:05:07 -07:00
break ;
2017-01-18 11:01:13 -08:00
case SMS_DEFAULT :
2017-03-14 13:24:24 -07:00
initializeSecurity ( isSecureText , isDefaultSms ) ;
2017-01-18 11:01:13 -08:00
break ;
2019-03-13 16:05:25 -07:00
case MEDIA_SENDER :
2018-09-20 13:27:18 -07:00
long expiresIn = recipient . getExpireMessages ( ) * 1000L ;
int subscriptionId = sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ;
boolean initiating = threadId = = - 1 ;
2019-03-13 16:05:25 -07:00
TransportOption transport = data . getParcelableExtra ( MediaSendActivity . EXTRA_TRANSPORT ) ;
String message = data . getStringExtra ( MediaSendActivity . EXTRA_MESSAGE ) ;
SlideDeck slideDeck = new SlideDeck ( ) ;
2018-11-20 09:59:23 -08:00
if ( transport = = null ) {
throw new IllegalStateException ( " Received a null transport from the MediaSendActivity. " ) ;
}
sendButton . setTransport ( transport ) ;
List < Media > mediaList = data . getParcelableArrayListExtra ( MediaSendActivity . EXTRA_MEDIA ) ;
for ( Media mediaItem : mediaList ) {
if ( MediaUtil . isVideoType ( mediaItem . getMimeType ( ) ) ) {
slideDeck . addSlide ( new VideoSlide ( this , mediaItem . getUri ( ) , 0 , mediaItem . getCaption ( ) . orNull ( ) ) ) ;
} else if ( MediaUtil . isGif ( mediaItem . getMimeType ( ) ) ) {
slideDeck . addSlide ( new GifSlide ( this , mediaItem . getUri ( ) , 0 , mediaItem . getWidth ( ) , mediaItem . getHeight ( ) , mediaItem . getCaption ( ) . orNull ( ) ) ) ;
} else if ( MediaUtil . isImageType ( mediaItem . getMimeType ( ) ) ) {
slideDeck . addSlide ( new ImageSlide ( this , mediaItem . getUri ( ) , 0 , mediaItem . getWidth ( ) , mediaItem . getHeight ( ) , mediaItem . getCaption ( ) . orNull ( ) ) ) ;
} else {
Log . w ( TAG , " Asked to send an unexpected mimeType: ' " + mediaItem . getMimeType ( ) + " '. Skipping. " ) ;
}
}
2019-02-25 13:58:10 -08:00
final Context context = ConversationActivity . this . getApplicationContext ( ) ;
2019-04-17 10:21:30 -04:00
sendMediaMessage ( transport . isSms ( ) ,
message ,
slideDeck ,
inputPanel . getQuote ( ) . orNull ( ) ,
Collections . emptyList ( ) ,
Collections . emptyList ( ) ,
expiresIn ,
subscriptionId ,
initiating ,
true ) . addListener ( new AssertedSuccessListener < Void > ( ) {
2019-02-25 13:58:10 -08:00
@Override
public void onSuccess ( Void result ) {
AsyncTask . THREAD_POOL_EXECUTOR . execute ( ( ) - > {
Stream . of ( slideDeck . getSlides ( ) )
. map ( Slide : : getUri )
. withoutNulls ( )
2019-03-13 16:05:25 -07:00
. filter ( BlobProvider : : isAuthority )
2019-02-25 17:47:30 -08:00
. forEach ( uri - > BlobProvider . getInstance ( ) . delete ( context , uri ) ) ;
2019-02-25 13:58:10 -08:00
} ) ;
}
} ) ;
2018-11-20 09:59:23 -08:00
break ;
2012-07-19 14:22:03 -07:00
}
}
2016-03-29 17:52:54 -05:00
@Override
public void startActivity ( Intent intent ) {
if ( intent . getStringExtra ( Browser . EXTRA_APPLICATION_ID ) ! = null ) {
intent . removeExtra ( Browser . EXTRA_APPLICATION_ID ) ;
}
2016-08-14 12:23:51 +02:00
try {
super . startActivity ( intent ) ;
} catch ( ActivityNotFoundException e ) {
Log . w ( TAG , e ) ;
Toast . makeText ( this , R . string . ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device , Toast . LENGTH_LONG ) . show ( ) ;
}
2016-03-29 17:52:54 -05:00
}
2012-07-19 14:22:03 -07:00
@Override
public boolean onPrepareOptionsMenu ( Menu menu ) {
2014-06-28 20:40:57 -07:00
MenuInflater inflater = this . getMenuInflater ( ) ;
2012-07-19 14:22:03 -07:00
menu . clear ( ) ;
2019-12-12 10:07:17 +11:00
boolean isLokiGroupChat = recipient . getAddress ( ) . isPublicChat ( ) | | recipient . getAddress ( ) . isRSSFeed ( ) ;
2019-08-06 15:43:47 +10:00
2019-12-12 10:07:17 +11:00
if ( isSecureText & & ! isLokiGroupChat ) {
2017-08-01 08:56:00 -07:00
if ( recipient . getExpireMessages ( ) > 0 ) {
2016-09-25 20:04:13 -07:00
inflater . inflate ( R . menu . conversation_expiring_on , menu ) ;
final MenuItem item = menu . findItem ( R . id . menu_expiring_messages ) ;
final View actionView = MenuItemCompat . getActionView ( item ) ;
2017-12-05 11:52:03 -08:00
final TextView badgeView = actionView . findViewById ( R . id . expiration_badge ) ;
2016-09-25 20:04:13 -07:00
2017-08-01 08:56:00 -07:00
badgeView . setText ( ExpirationUtil . getExpirationAbbreviatedDisplayValue ( this , recipient . getExpireMessages ( ) ) ) ;
2017-12-05 11:52:03 -08:00
actionView . setOnClickListener ( v - > onOptionsItemSelected ( item ) ) ;
2016-09-25 20:04:13 -07:00
} else {
inflater . inflate ( R . menu . conversation_expiring_off , menu ) ;
}
}
2016-08-15 20:23:56 -07:00
2012-08-07 19:03:28 -07:00
if ( isSingleConversation ( ) ) {
2019-07-19 15:59:51 +10:00
/ *
2016-11-09 09:37:40 -08:00
if ( isSecureText ) inflater . inflate ( R . menu . conversation_callable_secure , menu ) ;
else inflater . inflate ( R . menu . conversation_callable_insecure , menu ) ;
2019-07-19 15:59:51 +10:00
* /
2019-12-12 10:07:17 +11:00
} else if ( isGroupConversation ( ) & & ! isLokiGroupChat ) {
2012-10-21 14:34:09 -07:00
inflater . inflate ( R . menu . conversation_group_options , menu ) ;
2013-04-25 18:59:49 -07:00
2014-02-17 20:25:40 -08:00
if ( ! isPushGroupConversation ( ) ) {
inflater . inflate ( R . menu . conversation_mms_group_options , menu ) ;
if ( distributionType = = ThreadDatabase . DistributionTypes . BROADCAST ) {
menu . findItem ( R . id . menu_distribution_broadcast ) . setChecked ( true ) ;
} else {
menu . findItem ( R . id . menu_distribution_conversation ) . setChecked ( true ) ;
}
2014-02-22 10:54:43 -08:00
} else if ( isActiveGroup ( ) ) {
2014-02-20 15:41:52 -08:00
inflater . inflate ( R . menu . conversation_push_group_options , menu ) ;
2013-04-25 18:59:49 -07:00
}
2012-08-07 19:03:28 -07:00
}
2012-07-19 14:22:03 -07:00
inflater . inflate ( R . menu . conversation , menu ) ;
2014-06-04 01:24:44 +02:00
2015-09-29 14:26:37 -07:00
if ( isSingleConversation ( ) & & isSecureText ) {
inflater . inflate ( R . menu . conversation_secure , menu ) ;
} else if ( isSingleConversation ( ) ) {
inflater . inflate ( R . menu . conversation_insecure , menu ) ;
}
2017-08-01 08:56:00 -07:00
if ( recipient ! = null & & recipient . isMuted ( ) ) inflater . inflate ( R . menu . conversation_muted , menu ) ;
else inflater . inflate ( R . menu . conversation_unmuted , menu ) ;
2015-06-09 07:37:20 -07:00
2019-07-19 15:59:51 +10:00
/ *
2017-08-01 08:56:00 -07:00
if ( isSingleConversation ( ) & & getRecipient ( ) . getContactUri ( ) = = null ) {
2014-06-04 01:24:44 +02:00
inflater . inflate ( R . menu . conversation_add_to_contacts , menu ) ;
}
2019-10-24 14:26:54 +11:00
2014-06-04 01:24:44 +02:00
2019-01-13 23:30:54 -08:00
if ( recipient ! = null & & recipient . isLocalNumber ( ) ) {
if ( isSecureText ) menu . findItem ( R . id . menu_call_secure ) . setVisible ( false ) ;
else menu . findItem ( R . id . menu_call_insecure ) . setVisible ( false ) ;
2019-02-16 11:45:06 -08:00
MenuItem muteItem = menu . findItem ( R . id . menu_mute_notifications ) ;
if ( muteItem ! = null ) {
muteItem . setVisible ( false ) ;
}
2019-01-13 23:30:54 -08:00
}
2019-10-24 14:26:54 +11:00
* /
2019-01-13 23:30:54 -08:00
2019-02-01 09:06:59 -08:00
searchViewItem = menu . findItem ( R . id . menu_search ) ;
2020-01-16 11:37:52 +11:00
SearchView searchView = ( SearchView ) searchViewItem . getActionView ( ) ;
2019-02-01 09:06:59 -08:00
SearchView . OnQueryTextListener queryListener = new SearchView . OnQueryTextListener ( ) {
@Override
public boolean onQueryTextSubmit ( String query ) {
searchViewModel . onQueryUpdated ( query , threadId ) ;
searchNav . showLoading ( ) ;
fragment . onSearchQueryUpdated ( query ) ;
return true ;
}
@Override
public boolean onQueryTextChange ( String query ) {
searchViewModel . onQueryUpdated ( query , threadId ) ;
searchNav . showLoading ( ) ;
fragment . onSearchQueryUpdated ( query ) ;
return true ;
}
} ;
searchViewItem . setOnActionExpandListener ( new MenuItem . OnActionExpandListener ( ) {
@Override
public boolean onMenuItemActionExpand ( MenuItem item ) {
searchView . setOnQueryTextListener ( queryListener ) ;
searchViewModel . onSearchOpened ( ) ;
searchNav . setVisibility ( View . VISIBLE ) ;
searchNav . setData ( 0 , 0 ) ;
inputPanel . setVisibility ( View . GONE ) ;
for ( int i = 0 ; i < menu . size ( ) ; i + + ) {
if ( ! menu . getItem ( i ) . equals ( searchViewItem ) ) {
menu . getItem ( i ) . setVisible ( false ) ;
}
}
return true ;
}
@Override
public boolean onMenuItemActionCollapse ( MenuItem item ) {
searchView . setOnQueryTextListener ( null ) ;
searchViewModel . onSearchClosed ( ) ;
searchNav . setVisibility ( View . GONE ) ;
inputPanel . setVisibility ( View . VISIBLE ) ;
fragment . onSearchQueryUpdated ( null ) ;
invalidateOptionsMenu ( ) ;
return true ;
}
} ) ;
2012-07-19 14:22:03 -07:00
super . onPrepareOptionsMenu ( menu ) ;
return true ;
}
@Override
public boolean onOptionsItemSelected ( MenuItem item ) {
super . onOptionsItemSelected ( item ) ;
switch ( item . getItemId ( ) ) {
2018-09-27 11:45:56 -07:00
case R . id . menu_call_secure : handleDial ( getRecipient ( ) , true ) ; return true ;
case R . id . menu_call_insecure : handleDial ( getRecipient ( ) , false ) ; return true ;
2019-10-18 12:40:41 +11:00
case R . id . menu_view_media : handleViewMedia ( ) ; return true ;
2018-04-08 12:55:30 +02:00
case R . id . menu_add_shortcut : handleAddShortcut ( ) ; return true ;
2019-02-01 09:06:59 -08:00
case R . id . menu_search : handleSearch ( ) ; return true ;
2014-06-04 01:24:44 +02:00
case R . id . menu_add_to_contacts : handleAddToContacts ( ) ; return true ;
2015-10-04 12:13:34 +09:00
case R . id . menu_reset_secure_session : handleResetSecureSession ( ) ; return true ;
2013-04-25 18:59:49 -07:00
case R . id . menu_group_recipients : handleDisplayGroupRecipients ( ) ; return true ;
case R . id . menu_distribution_broadcast : handleDistributionBroadcastEnabled ( item ) ; return true ;
case R . id . menu_distribution_conversation : handleDistributionConversationEnabled ( item ) ; return true ;
2020-02-05 10:36:26 +11:00
case R . id . menu_edit_group : handleEditPushGroup ( ) ; return true ;
2014-02-20 15:41:52 -08:00
case R . id . menu_leave : handleLeavePushGroup ( ) ; return true ;
2014-12-12 18:31:20 -08:00
case R . id . menu_invite : handleInviteLink ( ) ; return true ;
2015-06-09 07:37:20 -07:00
case R . id . menu_mute_notifications : handleMuteNotifications ( ) ; return true ;
case R . id . menu_unmute_notifications : handleUnmuteNotifications ( ) ; return true ;
2019-07-19 15:59:51 +10:00
// case R.id.menu_conversation_settings: handleConversationSettings(); return true;
2016-08-15 20:23:56 -07:00
case R . id . menu_expiring_messages_off :
case R . id . menu_expiring_messages : handleSelectMessageExpiration ( ) ; return true ;
2013-04-25 18:59:49 -07:00
case android . R . id . home : handleReturnToConversationList ( ) ; return true ;
2012-07-19 14:22:03 -07:00
}
return false ;
}
2013-06-27 20:57:27 -07:00
@Override
public void onBackPressed ( ) {
2018-08-02 09:25:33 -04:00
Log . d ( TAG , " onBackPressed() " ) ;
2015-07-24 13:22:28 -07:00
if ( container . isInputOpen ( ) ) container . hideCurrentInput ( composeText ) ;
else super . onBackPressed ( ) ;
2013-06-27 20:57:27 -07:00
}
2015-07-02 16:47:03 -07:00
@Override
public void onKeyboardShown ( ) {
2015-11-22 10:44:53 -08:00
inputPanel . onKeyboardShown ( ) ;
2015-07-02 16:47:03 -07:00
}
2017-11-15 16:29:00 -08:00
@Subscribe ( threadMode = ThreadMode . MAIN )
public void onEvent ( ReminderUpdateEvent event ) {
updateReminders ( recipient . hasSeenInviteReminder ( ) ) ;
}
2017-11-24 22:00:30 -08:00
@Override
public void onRequestPermissionsResult ( int requestCode , @NonNull String [ ] permissions , @NonNull int [ ] grantResults ) {
Permissions . onRequestPermissionsResult ( this , requestCode , permissions , grantResults ) ;
}
2012-07-19 14:22:03 -07:00
//////// Event Handlers
2013-02-17 11:42:30 -08:00
private void handleReturnToConversationList ( ) {
2019-12-19 12:04:38 +01:00
Intent intent = new Intent ( this , HomeActivity . class ) ;
2013-02-17 11:42:30 -08:00
intent . setFlags ( Intent . FLAG_ACTIVITY_CLEAR_TOP ) ;
startActivity ( intent ) ;
finish ( ) ;
}
2016-08-15 20:23:56 -07:00
private void handleSelectMessageExpiration ( ) {
2017-01-03 13:36:34 -08:00
if ( isPushGroupConversation ( ) & & ! isActiveGroup ( ) ) {
return ;
}
2017-12-05 11:52:03 -08:00
//noinspection CodeBlock2Expr
ExpirationDialog . show ( this , recipient . getExpireMessages ( ) , expirationTime - > {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
DatabaseFactory . getRecipientDatabase ( ConversationActivity . this ) . setExpireMessages ( recipient , expirationTime ) ;
2018-01-12 11:39:10 +00:00
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage ( getRecipient ( ) , System . currentTimeMillis ( ) , expirationTime * 1000L ) ;
2018-01-24 19:17:44 -08:00
MessageSender . send ( ConversationActivity . this , outgoingMessage , threadId , false , null ) ;
2016-08-15 20:23:56 -07:00
2017-12-05 11:52:03 -08:00
return null ;
}
2017-02-13 22:35:47 -08:00
2017-12-05 11:52:03 -08:00
@Override
protected void onPostExecute ( Void result ) {
invalidateOptionsMenu ( ) ;
if ( fragment ! = null ) fragment . setLastSeen ( 0 ) ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2016-08-15 20:23:56 -07:00
} ) ;
}
2015-06-09 07:37:20 -07:00
private void handleMuteNotifications ( ) {
2017-12-05 11:52:03 -08:00
MuteDialog . show ( this , until - > {
recipient . setMuted ( until ) ;
2015-06-09 07:37:20 -07:00
2017-12-05 11:52:03 -08:00
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
DatabaseFactory . getRecipientDatabase ( ConversationActivity . this )
. setMuted ( recipient , until ) ;
2015-06-09 07:37:20 -07:00
2017-12-05 11:52:03 -08:00
return null ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2015-06-09 07:37:20 -07:00
} ) ;
}
2015-07-08 12:22:51 -07:00
private void handleConversationSettings ( ) {
2019-07-19 15:59:51 +10:00
/ *
2017-10-03 16:27:12 -07:00
Intent intent = new Intent ( ConversationActivity . this , RecipientPreferenceActivity . class ) ;
intent . putExtra ( RecipientPreferenceActivity . ADDRESS_EXTRA , recipient . getAddress ( ) ) ;
intent . putExtra ( RecipientPreferenceActivity . CAN_HAVE_SAFETY_NUMBER_EXTRA ,
isSecureText & & ! isSelfConversation ( ) ) ;
startActivitySceneTransition ( intent , titleView . findViewById ( R . id . contact_photo_image ) , " avatar " ) ;
2019-07-19 15:59:51 +10:00
* /
2015-07-08 12:22:51 -07:00
}
2015-06-09 07:37:20 -07:00
private void handleUnmuteNotifications ( ) {
2017-08-01 08:56:00 -07:00
recipient . setMuted ( 0 ) ;
2015-06-09 07:37:20 -07:00
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
2017-08-21 18:47:37 -07:00
DatabaseFactory . getRecipientDatabase ( ConversationActivity . this )
2017-08-01 08:56:00 -07:00
. setMuted ( recipient , 0 ) ;
2015-06-09 07:37:20 -07:00
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2015-06-09 07:37:20 -07:00
}
private void handleUnblock ( ) {
2018-09-10 08:40:00 -07:00
int titleRes = R . string . ConversationActivity_unblock_this_contact_question ;
int bodyRes = R . string . ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact ;
if ( recipient . isGroupRecipient ( ) ) {
titleRes = R . string . ConversationActivity_unblock_this_group_question ;
bodyRes = R . string . ConversationActivity_unblock_this_group_description ;
}
2017-12-05 11:52:03 -08:00
//noinspection CodeBlock2Expr
2015-05-20 23:36:30 +02:00
new AlertDialog . Builder ( this )
2018-09-10 08:40:00 -07:00
. setTitle ( titleRes )
. setMessage ( bodyRes )
. setNegativeButton ( android . R . string . cancel , null )
. setPositiveButton ( R . string . ConversationActivity_unblock , ( dialog , which ) - > {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
DatabaseFactory . getRecipientDatabase ( ConversationActivity . this )
2017-12-05 11:52:03 -08:00
. setBlocked ( recipient , false ) ;
2016-08-26 16:53:23 -07:00
2018-09-10 08:40:00 -07:00
ApplicationContext . getInstance ( ConversationActivity . this )
. getJobManager ( )
2019-03-28 08:56:35 -07:00
. add ( new MultiDeviceBlockedUpdateJob ( ) ) ;
2016-08-26 16:53:23 -07:00
2018-09-10 08:40:00 -07:00
return null ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
} ) . show ( ) ;
2015-06-09 07:37:20 -07:00
}
2016-11-20 15:56:47 -08:00
@TargetApi ( Build . VERSION_CODES . KITKAT )
private void handleMakeDefaultSms ( ) {
Intent intent = new Intent ( Telephony . Sms . Intents . ACTION_CHANGE_DEFAULT ) ;
intent . putExtra ( Telephony . Sms . Intents . EXTRA_PACKAGE_NAME , getPackageName ( ) ) ;
2017-01-18 11:01:13 -08:00
startActivityForResult ( intent , SMS_DEFAULT ) ;
2016-11-20 15:56:47 -08:00
}
2017-12-25 15:57:33 -08:00
private void handleRegisterForSignal ( ) {
Intent intent = new Intent ( this , RegistrationActivity . class ) ;
2018-03-11 17:12:42 -07:00
intent . putExtra ( RegistrationActivity . RE_REGISTRATION_EXTRA , true ) ;
2017-12-25 15:57:33 -08:00
startActivity ( intent ) ;
}
2014-12-12 18:31:20 -08:00
private void handleInviteLink ( ) {
2019-05-23 08:02:15 -03:00
String inviteText = getString ( R . string . ConversationActivity_lets_switch_to_signal , getString ( R . string . install_url ) ) ;
2019-03-15 15:31:52 -07:00
if ( isDefaultSms ) {
composeText . appendInvite ( inviteText ) ;
} else {
Intent intent = new Intent ( Intent . ACTION_SENDTO ) ;
intent . setData ( Uri . parse ( " smsto: " + recipient . getAddress ( ) . serialize ( ) ) ) ;
intent . putExtra ( " sms_body " , inviteText ) ;
intent . putExtra ( Intent . EXTRA_TEXT , inviteText ) ;
startActivity ( intent ) ;
2014-12-12 18:31:20 -08:00
}
}
2015-10-04 12:13:34 +09:00
private void handleResetSecureSession ( ) {
2015-05-20 23:36:30 +02:00
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
2015-11-20 01:05:31 +09:00
builder . setTitle ( R . string . ConversationActivity_reset_secure_session_question ) ;
2015-03-24 13:44:22 +01:00
builder . setIconAttribute ( R . attr . dialog_alert_icon ) ;
2012-07-19 14:22:03 -07:00
builder . setCancelable ( true ) ;
2015-11-20 01:05:31 +09:00
builder . setMessage ( R . string . ConversationActivity_this_may_help_if_youre_having_encryption_problems ) ;
2017-12-05 11:52:03 -08:00
builder . setPositiveButton ( R . string . ConversationActivity_reset , ( dialog , which ) - > {
if ( isSingleConversation ( ) ) {
final Context context = getApplicationContext ( ) ;
2014-04-22 14:33:29 -07:00
2017-12-05 11:52:03 -08:00
OutgoingEndSessionMessage endSessionMessage =
new OutgoingEndSessionMessage ( new OutgoingTextMessage ( getRecipient ( ) , " TERMINATE " , 0 , - 1 ) ) ;
2014-04-22 14:33:29 -07:00
2017-12-05 11:52:03 -08:00
new AsyncTask < OutgoingEndSessionMessage , Void , Long > ( ) {
@Override
protected Long doInBackground ( OutgoingEndSessionMessage . . . messages ) {
2018-01-24 19:17:44 -08:00
return MessageSender . send ( context , messages [ 0 ] , threadId , false , null ) ;
2017-12-05 11:52:03 -08:00
}
2014-11-08 11:35:58 -08:00
2017-12-05 11:52:03 -08:00
@Override
protected void onPostExecute ( Long result ) {
sendComplete ( result ) ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , endSessionMessage ) ;
2012-07-19 14:22:03 -07:00
}
} ) ;
2015-11-20 01:05:31 +09:00
builder . setNegativeButton ( android . R . string . cancel , null ) ;
2012-07-19 14:22:03 -07:00
builder . show ( ) ;
}
2015-01-18 16:11:30 -10:00
private void handleViewMedia ( ) {
Intent intent = new Intent ( this , MediaOverviewActivity . class ) ;
2017-08-01 08:56:00 -07:00
intent . putExtra ( MediaOverviewActivity . ADDRESS_EXTRA , recipient . getAddress ( ) ) ;
2015-01-18 16:11:30 -10:00
startActivity ( intent ) ;
}
2018-04-08 12:55:30 +02:00
private void handleAddShortcut ( ) {
Log . i ( TAG , " Creating home screen shortcut for recipient " + recipient . getAddress ( ) ) ;
2018-08-06 14:42:22 -04:00
new AsyncTask < Void , Void , IconCompat > ( ) {
2018-04-08 12:55:30 +02:00
2018-08-06 14:42:22 -04:00
@Override
protected IconCompat doInBackground ( Void . . . voids ) {
Context context = getApplicationContext ( ) ;
IconCompat icon = null ;
2018-04-08 12:55:30 +02:00
2018-08-06 14:42:22 -04:00
if ( recipient . getContactPhoto ( ) ! = null ) {
try {
2018-08-25 10:33:14 -07:00
Bitmap bitmap = BitmapFactory . decodeStream ( recipient . getContactPhoto ( ) . openInputStream ( context ) ) ;
bitmap = BitmapUtil . createScaledBitmap ( bitmap , 300 , 300 ) ;
icon = IconCompat . createWithAdaptiveBitmap ( bitmap ) ;
2018-08-06 14:42:22 -04:00
} catch ( IOException e ) {
Log . w ( TAG , " Failed to decode contact photo during shortcut creation. Falling back to generic icon. " , e ) ;
}
}
2018-04-08 12:55:30 +02:00
2018-08-06 14:42:22 -04:00
if ( icon = = null ) {
icon = IconCompat . createWithResource ( context , recipient . isGroupRecipient ( ) ? R . mipmap . ic_group_shortcut
: R . mipmap . ic_person_shortcut ) ;
}
2018-04-08 12:55:30 +02:00
2018-08-06 14:42:22 -04:00
return icon ;
}
2018-04-08 12:55:30 +02:00
2018-08-06 14:42:22 -04:00
@Override
protected void onPostExecute ( IconCompat icon ) {
Context context = getApplicationContext ( ) ;
String name = Optional . fromNullable ( recipient . getName ( ) )
. or ( Optional . fromNullable ( recipient . getProfileName ( ) ) )
. or ( recipient . toShortString ( ) ) ;
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat . Builder ( context , recipient . getAddress ( ) . serialize ( ) + '-' + System . currentTimeMillis ( ) )
. setShortLabel ( name )
. setIcon ( icon )
. setIntent ( ShortcutLauncherActivity . createIntent ( context , recipient . getAddress ( ) ) )
. build ( ) ;
if ( ShortcutManagerCompat . requestPinShortcut ( context , shortcutInfo , null ) ) {
Toast . makeText ( context , getString ( R . string . ConversationActivity_added_to_home_screen ) , Toast . LENGTH_LONG ) . show ( ) ;
}
}
} . execute ( ) ;
2018-04-08 12:55:30 +02:00
}
2019-02-01 09:06:59 -08:00
private void handleSearch ( ) {
searchViewModel . onSearchOpened ( ) ;
}
2014-02-22 10:54:43 -08:00
private void handleLeavePushGroup ( ) {
2017-08-01 08:56:00 -07:00
if ( getRecipient ( ) = = null ) {
2014-02-22 10:54:43 -08:00
Toast . makeText ( this , getString ( R . string . ConversationActivity_invalid_recipient ) ,
Toast . LENGTH_LONG ) . show ( ) ;
return ;
}
2015-05-20 23:36:30 +02:00
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
2014-02-22 10:54:43 -08:00
builder . setTitle ( getString ( R . string . ConversationActivity_leave_group ) ) ;
2015-03-24 13:44:22 +01:00
builder . setIconAttribute ( R . attr . dialog_info_icon ) ;
2014-02-22 10:54:43 -08:00
builder . setCancelable ( true ) ;
builder . setMessage ( getString ( R . string . ConversationActivity_are_you_sure_you_want_to_leave_this_group ) ) ;
2017-12-05 11:52:03 -08:00
builder . setPositiveButton ( R . string . yes , ( dialog , which ) - > {
2018-09-10 08:40:00 -07:00
Recipient groupRecipient = getRecipient ( ) ;
long threadId = DatabaseFactory . getThreadDatabase ( this ) . getThreadIdFor ( groupRecipient ) ;
Optional < OutgoingGroupMediaMessage > leaveMessage = GroupUtil . createGroupLeaveMessage ( this , groupRecipient ) ;
2017-12-05 11:52:03 -08:00
2018-09-10 08:40:00 -07:00
if ( threadId ! = - 1 & & leaveMessage . isPresent ( ) ) {
MessageSender . send ( this , leaveMessage . get ( ) , threadId , false , null ) ;
2017-12-05 11:52:03 -08:00
2020-02-04 08:31:38 +11:00
// We need to remove the master device from the group
String masterHexEncodedPublicKey = TextSecurePreferences . getMasterHexEncodedPublicKey ( this ) ;
String localNumber = masterHexEncodedPublicKey ! = null ? masterHexEncodedPublicKey : TextSecurePreferences . getLocalNumber ( this ) ;
2018-09-10 08:40:00 -07:00
GroupDatabase groupDatabase = DatabaseFactory . getGroupDatabase ( this ) ;
String groupId = groupRecipient . getAddress ( ) . toGroupString ( ) ;
groupDatabase . setActive ( groupId , false ) ;
2020-02-04 08:31:38 +11:00
groupDatabase . remove ( groupId , Address . fromSerialized ( localNumber ) ) ;
2017-12-05 11:52:03 -08:00
initializeEnabledCheck ( ) ;
2018-09-10 08:40:00 -07:00
} else {
Toast . makeText ( this , R . string . ConversationActivity_error_leaving_group , Toast . LENGTH_LONG ) . show ( ) ;
2014-02-22 10:54:43 -08:00
}
} ) ;
builder . setNegativeButton ( R . string . no , null ) ;
builder . show ( ) ;
}
private void handleEditPushGroup ( ) {
2020-02-05 10:36:26 +11:00
AlertDialog . Builder alert = new AlertDialog . Builder ( this ) ;
alert . setMessage ( " The ability to add members to a closed group is coming soon. " ) ;
alert . setPositiveButton ( " OK " , ( dialog , which ) - > dialog . dismiss ( ) ) ;
alert . create ( ) . show ( ) ;
2014-02-22 10:54:43 -08:00
}
2013-04-25 18:59:49 -07:00
private void handleDistributionBroadcastEnabled ( MenuItem item ) {
distributionType = ThreadDatabase . DistributionTypes . BROADCAST ;
item . setChecked ( true ) ;
if ( threadId ! = - 1 ) {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
DatabaseFactory . getThreadDatabase ( ConversationActivity . this )
. setDistributionType ( threadId , ThreadDatabase . DistributionTypes . BROADCAST ) ;
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2013-04-25 18:59:49 -07:00
}
}
private void handleDistributionConversationEnabled ( MenuItem item ) {
distributionType = ThreadDatabase . DistributionTypes . CONVERSATION ;
item . setChecked ( true ) ;
if ( threadId ! = - 1 ) {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
DatabaseFactory . getThreadDatabase ( ConversationActivity . this )
. setDistributionType ( threadId , ThreadDatabase . DistributionTypes . CONVERSATION ) ;
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2013-04-25 18:59:49 -07:00
}
}
2018-09-27 11:45:56 -07:00
private void handleDial ( final Recipient recipient , boolean isSecure ) {
2015-09-21 17:41:27 -07:00
if ( recipient = = null ) return ;
2018-09-27 11:45:56 -07:00
if ( isSecure ) {
2018-04-26 17:03:54 -07:00
CommunicationActions . startVoiceCall ( this , recipient ) ;
2015-09-21 17:41:27 -07:00
} else {
try {
Intent dialIntent = new Intent ( Intent . ACTION_DIAL ,
2017-07-26 09:59:15 -07:00
Uri . parse ( " tel: " + recipient . getAddress ( ) . serialize ( ) ) ) ;
2015-09-21 17:41:27 -07:00
startActivity ( dialIntent ) ;
} catch ( ActivityNotFoundException anfe ) {
Log . w ( TAG , anfe ) ;
Dialogs . showAlertDialog ( this ,
getString ( R . string . ConversationActivity_calls_not_supported ) ,
getString ( R . string . ConversationActivity_this_device_does_not_appear_to_support_dial_actions ) ) ;
}
}
2012-07-19 14:22:03 -07:00
}
2012-10-21 14:34:09 -07:00
private void handleDisplayGroupRecipients ( ) {
2017-08-01 08:56:00 -07:00
new GroupMembersDialog ( this , getRecipient ( ) ) . display ( ) ;
2012-10-21 14:34:09 -07:00
}
2014-06-04 01:24:44 +02:00
private void handleAddToContacts ( ) {
2017-08-01 08:56:00 -07:00
if ( recipient . getAddress ( ) . isGroup ( ) ) return ;
2017-07-26 09:59:15 -07:00
2015-12-11 22:57:18 -08:00
try {
2019-03-11 16:40:26 -03:00
startActivityForResult ( RecipientExporter . export ( recipient ) . asAddContactIntent ( ) , ADD_CONTACT ) ;
2015-12-11 22:57:18 -08:00
} catch ( ActivityNotFoundException e ) {
Log . w ( TAG , e ) ;
}
2013-10-17 02:28:36 +02:00
}
2017-10-05 20:07:44 +02:00
private boolean handleDisplayQuickContact ( ) {
if ( recipient . getAddress ( ) . isGroup ( ) ) return false ;
2020-01-10 11:35:16 +11:00
// if (recipient.getContactUri() != null) {
// ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
// } else {
// handleAddToContacts();
// }
2017-10-05 20:07:44 +02:00
return true ;
}
2012-07-19 14:22:03 -07:00
private void handleAddAttachment ( ) {
2015-09-29 14:26:37 -07:00
if ( this . isMmsEnabled | | isSecureText ) {
2017-01-18 18:46:40 -08:00
if ( attachmentTypeSelector = = null ) {
2020-01-13 10:11:20 +11:00
attachmentTypeSelector = new AttachmentTypeSelector ( this , getSupportLoaderManager ( ) , new AttachmentTypeListener ( ) , keyboardHeight ) ;
2017-01-18 18:46:40 -08:00
}
2020-01-13 16:47:50 +11:00
attachmentTypeSelector . keyboardHeight = keyboardHeight ;
2015-10-28 09:47:09 -07:00
attachmentTypeSelector . show ( this , attachButton ) ;
2013-03-04 17:43:04 -08:00
} else {
2013-09-16 00:55:01 -07:00
handleManualMmsRequired ( ) ;
2013-03-04 17:43:04 -08:00
}
2012-07-19 14:22:03 -07:00
}
2013-09-16 00:55:01 -07:00
private void handleManualMmsRequired ( ) {
Toast . makeText ( this , R . string . MmsDownloader_error_reading_mms_settings , Toast . LENGTH_LONG ) . show ( ) ;
2017-12-05 11:52:03 -08:00
Bundle extras = getIntent ( ) . getExtras ( ) ;
2013-09-16 00:55:01 -07:00
Intent intent = new Intent ( this , PromptMmsActivity . class ) ;
2017-12-05 11:52:03 -08:00
if ( extras ! = null ) intent . putExtras ( extras ) ;
2013-09-16 00:55:01 -07:00
startActivity ( intent ) ;
}
2017-06-06 18:03:09 -07:00
private void handleUnverifiedRecipients ( ) {
List < Recipient > unverifiedRecipients = identityRecords . getUnverifiedRecipients ( this ) ;
List < IdentityRecord > unverifiedRecords = identityRecords . getUnverifiedRecords ( ) ;
String message = IdentityUtil . getUnverifiedSendDialogDescription ( this , unverifiedRecipients ) ;
if ( message = = null ) return ;
2017-12-05 11:52:03 -08:00
//noinspection CodeBlock2Expr
new UnverifiedSendDialog ( this , message , unverifiedRecords , ( ) - > {
initializeIdentityRecords ( ) . addListener ( new ListenableFuture . Listener < Boolean > ( ) {
@Override
public void onSuccess ( Boolean result ) {
sendMessage ( ) ;
}
2017-06-06 18:03:09 -07:00
2017-12-05 11:52:03 -08:00
@Override
public void onFailure ( ExecutionException e ) {
throw new AssertionError ( e ) ;
}
} ) ;
2017-06-06 18:03:09 -07:00
} ) . show ( ) ;
}
private void handleUntrustedRecipients ( ) {
List < Recipient > untrustedRecipients = identityRecords . getUntrustedRecipients ( this ) ;
List < IdentityRecord > untrustedRecords = identityRecords . getUntrustedRecords ( ) ;
String untrustedMessage = IdentityUtil . getUntrustedSendDialogDescription ( this , untrustedRecipients ) ;
if ( untrustedMessage = = null ) return ;
2017-12-05 11:52:03 -08:00
//noinspection CodeBlock2Expr
new UntrustedSendDialog ( this , untrustedMessage , untrustedRecords , ( ) - > {
initializeIdentityRecords ( ) . addListener ( new ListenableFuture . Listener < Boolean > ( ) {
@Override
public void onSuccess ( Boolean result ) {
sendMessage ( ) ;
}
2017-06-06 18:03:09 -07:00
2017-12-05 11:52:03 -08:00
@Override
public void onFailure ( ExecutionException e ) {
throw new AssertionError ( e ) ;
}
} ) ;
2017-06-06 18:03:09 -07:00
} ) . show ( ) ;
}
2017-03-14 13:24:24 -07:00
private void handleSecurityChange ( boolean isSecureText , boolean isDefaultSms ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " handleSecurityChange( " + isSecureText + " , " + isDefaultSms + " ) " ) ;
2017-09-06 17:54:32 -07:00
if ( isSecurityInitialized & & isSecureText = = this . isSecureText & & isDefaultSms = = this . isDefaultSms ) {
return ;
}
this . isSecureText = isSecureText ;
this . isDefaultSms = isDefaultSms ;
this . isSecurityInitialized = true ;
2015-09-29 14:26:37 -07:00
2017-08-01 08:56:00 -07:00
boolean isMediaMessage = recipient . isMmsGroupRecipient ( ) | | attachmentManager . isAttachmentPresent ( ) ;
2015-09-23 15:47:48 -07:00
sendButton . resetAvailableTransports ( isMediaMessage ) ;
2019-12-16 09:17:31 +11:00
sendButton . setDefaultTransport ( Type . TEXTSECURE ) ;
2015-09-23 15:47:48 -07:00
2019-12-16 09:17:31 +11:00
/ * Loki - We don ' t support SMS
2017-12-25 15:57:33 -08:00
if ( ! isSecureText & & ! isPushGroupConversation ( ) ) sendButton . disableTransport ( Type . TEXTSECURE ) ;
if ( recipient . isPushGroupRecipient ( ) ) sendButton . disableTransport ( Type . SMS ) ;
2015-09-23 15:47:48 -07:00
2019-05-10 12:13:59 -03:00
if ( ! recipient . isPushGroupRecipient ( ) & & recipient . isForceSmsSelection ( ) ) {
2019-04-12 16:22:38 -03:00
sendButton . setDefaultTransport ( Type . SMS ) ;
} else {
if ( isSecureText | | isPushGroupConversation ( ) ) sendButton . setDefaultTransport ( Type . TEXTSECURE ) ;
else sendButton . setDefaultTransport ( Type . SMS ) ;
}
2019-12-16 09:17:31 +11:00
* /
2015-09-23 15:47:48 -07:00
calculateCharactersRemaining ( ) ;
supportInvalidateOptionsMenu ( ) ;
2017-08-01 08:56:00 -07:00
setBlockedUserState ( recipient , isSecureText , isDefaultSms ) ;
2015-09-23 15:47:48 -07:00
}
2012-07-19 14:22:03 -07:00
///// Initializers
2018-07-25 11:30:48 -04:00
private ListenableFuture < Boolean > initializeDraft ( ) {
final SettableFuture < Boolean > result = new SettableFuture < > ( ) ;
2019-04-17 10:21:30 -04:00
final String draftText = getIntent ( ) . getStringExtra ( TEXT_EXTRA ) ;
final Uri draftMedia = getIntent ( ) . getData ( ) ;
final MediaType draftMediaType = MediaType . from ( getIntent ( ) . getType ( ) ) ;
final List < Media > mediaList = getIntent ( ) . getParcelableArrayListExtra ( MEDIA_EXTRA ) ;
final StickerLocator stickerLocator = getIntent ( ) . getParcelableExtra ( STICKER_EXTRA ) ;
if ( stickerLocator ! = null & & draftMedia ! = null ) {
sendSticker ( stickerLocator , draftMedia , 0 , true ) ;
return new SettableFuture < > ( false ) ;
}
2018-11-20 09:59:23 -08:00
if ( ! Util . isEmpty ( mediaList ) ) {
2019-03-13 16:05:25 -07:00
Intent sendIntent = MediaSendActivity . buildEditorIntent ( this , mediaList , recipient , draftText , sendButton . getSelectedTransport ( ) ) ;
2018-11-20 09:59:23 -08:00
startActivityForResult ( sendIntent , MEDIA_SENDER ) ;
return new SettableFuture < > ( false ) ;
}
2018-07-25 11:30:48 -04:00
if ( draftText ! = null ) {
2019-04-17 10:21:30 -04:00
composeText . setText ( " " ) ;
composeText . append ( draftText ) ;
2018-07-25 11:30:48 -04:00
result . set ( true ) ;
}
2018-11-20 09:59:23 -08:00
2018-07-25 11:30:48 -04:00
if ( draftMedia ! = null & & draftMediaType ! = null ) {
return setMedia ( draftMedia , draftMediaType ) ;
}
2015-09-04 17:33:22 -07:00
2015-11-18 12:54:40 -08:00
if ( draftText = = null & & draftMedia = = null & & draftMediaType = = null ) {
2018-07-25 11:30:48 -04:00
return initializeDraftFromDatabase ( ) ;
2015-05-18 10:26:32 -07:00
} else {
updateToggleButtonState ( ) ;
2018-07-25 11:30:48 -04:00
result . set ( false ) ;
2013-02-04 00:13:07 -08:00
}
2018-07-25 11:30:48 -04:00
return result ;
2013-02-04 00:13:07 -08:00
}
2014-02-22 10:54:43 -08:00
private void initializeEnabledCheck ( ) {
2014-02-23 14:37:41 -08:00
boolean enabled = ! ( isPushGroupConversation ( ) & & ! isActiveGroup ( ) ) ;
2015-11-22 10:44:57 -08:00
inputPanel . setEnabled ( enabled ) ;
2014-02-22 10:54:43 -08:00
sendButton . setEnabled ( enabled ) ;
2015-11-22 10:44:57 -08:00
attachButton . setEnabled ( enabled ) ;
2014-02-22 10:54:43 -08:00
}
2018-07-25 11:30:48 -04:00
private ListenableFuture < Boolean > initializeDraftFromDatabase ( ) {
SettableFuture < Boolean > future = new SettableFuture < > ( ) ;
2013-02-04 00:13:07 -08:00
new AsyncTask < Void , Void , List < Draft > > ( ) {
@Override
protected List < Draft > doInBackground ( Void . . . params ) {
DraftDatabase draftDatabase = DatabaseFactory . getDraftDatabase ( ConversationActivity . this ) ;
2018-01-24 19:17:44 -08:00
List < Draft > results = draftDatabase . getDrafts ( threadId ) ;
2013-02-04 00:13:07 -08:00
draftDatabase . clearDrafts ( threadId ) ;
return results ;
}
@Override
protected void onPostExecute ( List < Draft > drafts ) {
2018-10-29 15:14:31 -07:00
if ( drafts . isEmpty ( ) ) {
future . set ( false ) ;
updateToggleButtonState ( ) ;
return ;
}
2018-07-25 11:30:48 -04:00
AtomicInteger draftsRemaining = new AtomicInteger ( drafts . size ( ) ) ;
AtomicBoolean success = new AtomicBoolean ( false ) ;
ListenableFuture . Listener < Boolean > listener = new AssertedSuccessListener < Boolean > ( ) {
@Override
public void onSuccess ( Boolean result ) {
success . compareAndSet ( false , result ) ;
if ( draftsRemaining . decrementAndGet ( ) < = 0 ) {
future . set ( success . get ( ) ) ;
}
}
} ;
2013-02-04 00:13:07 -08:00
for ( Draft draft : drafts ) {
2016-01-04 13:02:22 -08:00
try {
2017-12-05 11:52:03 -08:00
switch ( draft . getType ( ) ) {
case Draft . TEXT :
composeText . setText ( draft . getValue ( ) ) ;
2018-07-25 11:30:48 -04:00
listener . onSuccess ( true ) ;
2017-12-05 11:52:03 -08:00
break ;
case Draft . LOCATION :
2018-07-25 11:30:48 -04:00
attachmentManager . setLocation ( SignalPlace . deserialize ( draft . getValue ( ) ) , getCurrentMediaConstraints ( ) ) . addListener ( listener ) ;
2017-12-05 11:52:03 -08:00
break ;
case Draft . IMAGE :
2018-07-25 11:30:48 -04:00
setMedia ( Uri . parse ( draft . getValue ( ) ) , MediaType . IMAGE ) . addListener ( listener ) ;
2017-12-05 11:52:03 -08:00
break ;
case Draft . AUDIO :
2018-07-25 11:30:48 -04:00
setMedia ( Uri . parse ( draft . getValue ( ) ) , MediaType . AUDIO ) . addListener ( listener ) ;
2017-12-05 11:52:03 -08:00
break ;
case Draft . VIDEO :
2018-07-25 11:30:48 -04:00
setMedia ( Uri . parse ( draft . getValue ( ) ) , MediaType . VIDEO ) . addListener ( listener ) ;
2017-12-05 11:52:03 -08:00
break ;
2018-04-24 11:09:54 -07:00
case Draft . QUOTE :
2018-07-25 11:30:48 -04:00
SettableFuture < Boolean > quoteResult = new SettableFuture < > ( ) ;
new QuoteRestorationTask ( draft . getValue ( ) , quoteResult ) . execute ( ) ;
quoteResult . addListener ( listener ) ;
2018-04-24 11:09:54 -07:00
break ;
2016-01-04 13:02:22 -08:00
}
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
2014-05-22 23:00:53 +02:00
}
2013-02-04 00:13:07 -08:00
}
2015-05-18 10:26:32 -07:00
updateToggleButtonState ( ) ;
2013-02-04 00:13:07 -08:00
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2018-07-25 11:30:48 -04:00
return future ;
2013-02-02 20:37:40 -08:00
}
2015-10-01 17:46:47 -07:00
private ListenableFuture < Boolean > initializeSecurity ( final boolean currentSecureText ,
2017-01-18 11:01:13 -08:00
final boolean currentIsDefaultSms )
2015-09-29 14:26:37 -07:00
{
2015-10-01 17:46:47 -07:00
final SettableFuture < Boolean > future = new SettableFuture < > ( ) ;
2017-03-14 13:24:24 -07:00
handleSecurityChange ( currentSecureText | | isPushGroupConversation ( ) , currentIsDefaultSms ) ;
2015-09-21 17:41:27 -07:00
2017-08-01 08:56:00 -07:00
new AsyncTask < Recipient , Void , boolean [ ] > ( ) {
2015-09-21 17:41:27 -07:00
@Override
2017-08-01 08:56:00 -07:00
protected boolean [ ] doInBackground ( Recipient . . . params ) {
2017-08-22 10:44:04 -07:00
Context context = ConversationActivity . this ;
Recipient recipient = params [ 0 ] ;
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Resolving registered state... " ) ;
2017-11-25 09:50:36 -08:00
RegisteredState registeredState ;
2017-11-27 09:44:50 -08:00
if ( recipient . isPushGroupRecipient ( ) ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Push group recipient... " ) ;
2017-11-27 09:44:50 -08:00
registeredState = RegisteredState . REGISTERED ;
} else if ( recipient . isResolving ( ) ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Talking to DB directly. " ) ;
2017-11-25 09:50:36 -08:00
registeredState = DatabaseFactory . getRecipientDatabase ( ConversationActivity . this ) . isRegistered ( recipient . getAddress ( ) ) ;
} else {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Checking through resolved recipient " ) ;
2017-11-25 09:50:36 -08:00
registeredState = recipient . resolve ( ) . getRegistered ( ) ;
}
2019-06-07 13:54:50 +10:00
// Loki - Override the flag below
registeredState = RegisteredState . REGISTERED ;
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Resolved registered state: " + registeredState ) ;
2019-06-07 13:54:50 +10:00
// Loki - Override the flag below
boolean signalEnabled = true ; // TextSecurePreferences.isPushRegistered(context);
2015-09-29 14:26:37 -07:00
2017-08-22 10:44:04 -07:00
if ( registeredState = = RegisteredState . UNKNOWN ) {
2017-01-31 21:46:20 -08:00
try {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Refreshing directory for user: " + recipient . getAddress ( ) . serialize ( ) ) ;
2018-01-24 19:17:44 -08:00
registeredState = DirectoryHelper . refreshDirectoryFor ( context , recipient ) ;
2017-01-31 21:46:20 -08:00
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
2015-09-29 14:26:37 -07:00
}
2015-09-21 17:41:27 -07:00
}
2017-01-31 21:46:20 -08:00
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Returning registered state... " ) ;
2017-09-14 16:46:12 -07:00
return new boolean [ ] { registeredState = = RegisteredState . REGISTERED & & signalEnabled ,
Util . isDefaultSmsProvider ( context ) } ;
2015-09-21 17:41:27 -07:00
}
@Override
2017-01-18 11:01:13 -08:00
protected void onPostExecute ( boolean [ ] result ) {
2017-03-14 13:24:24 -07:00
if ( result [ 0 ] ! = currentSecureText | | result [ 1 ] ! = currentIsDefaultSms ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onPostExecute() handleSecurityChange: " + result [ 0 ] + " , " + result [ 1 ] ) ;
2017-03-14 13:24:24 -07:00
handleSecurityChange ( result [ 0 ] , result [ 1 ] ) ;
2015-09-21 17:41:27 -07:00
}
2015-10-01 17:46:47 -07:00
future . set ( true ) ;
2015-10-13 21:44:01 -07:00
onSecurityUpdated ( ) ;
2015-09-21 17:41:27 -07:00
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , recipient ) ;
2015-10-01 17:46:47 -07:00
return future ;
2015-09-21 17:41:27 -07:00
}
2015-10-13 21:44:01 -07:00
private void onSecurityUpdated ( ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onSecurityUpdated() " ) ;
2017-11-15 16:29:00 -08:00
updateReminders ( recipient . hasSeenInviteReminder ( ) ) ;
2017-08-22 10:44:04 -07:00
updateDefaultSubscriptionId ( recipient . getDefaultSubscriptionId ( ) ) ;
2016-02-05 16:10:33 -08:00
}
2017-11-15 16:29:00 -08:00
protected void updateReminders ( boolean seenInvite ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " updateReminders( " + seenInvite + " ) " ) ;
2017-11-15 16:29:00 -08:00
if ( UnauthorizedReminder . isEligible ( this ) ) {
reminderView . get ( ) . showReminder ( new UnauthorizedReminder ( this ) ) ;
} else if ( ExpiredBuildReminder . isEligible ( ) ) {
reminderView . get ( ) . showReminder ( new ExpiredBuildReminder ( this ) ) ;
2018-06-11 09:37:01 -07:00
} else if ( ServiceOutageReminder . isEligible ( this ) ) {
2019-03-28 08:56:35 -07:00
ApplicationContext . getInstance ( this ) . getJobManager ( ) . add ( new ServiceOutageDetectionJob ( ) ) ;
2018-06-11 09:37:01 -07:00
reminderView . get ( ) . showReminder ( new ServiceOutageReminder ( this ) ) ;
2017-11-15 16:29:00 -08:00
} else if ( TextSecurePreferences . isPushRegistered ( this ) & &
TextSecurePreferences . isShowInviteReminders ( this ) & &
! isSecureText & &
! seenInvite & &
! recipient . isGroupRecipient ( ) )
2015-10-26 12:36:45 -07:00
{
2017-08-01 08:56:00 -07:00
InviteReminder reminder = new InviteReminder ( this , recipient ) ;
2017-11-15 16:29:00 -08:00
reminder . setOkListener ( v - > {
handleInviteLink ( ) ;
reminderView . get ( ) . requestDismiss ( ) ;
2016-02-05 16:10:33 -08:00
} ) ;
2017-01-18 18:46:40 -08:00
reminderView . get ( ) . showReminder ( reminder ) ;
} else if ( reminderView . resolved ( ) ) {
reminderView . get ( ) . hide ( ) ;
2015-10-13 21:44:01 -07:00
}
}
2014-04-14 03:55:20 +03:00
2019-12-06 13:00:08 +11:00
protected void updateSessionRestoreBanner ( ) {
Set < String > devices = DatabaseFactory . getLokiThreadDatabase ( this ) . getSessionRestoreDevices ( threadId ) ;
if ( devices . size ( ) > 0 ) {
2019-12-06 16:33:17 +11:00
sessionRestoreBannerView . update ( recipient ) ;
sessionRestoreBannerView . show ( ) ;
2019-12-06 13:00:08 +11:00
} else {
2019-12-06 16:33:17 +11:00
sessionRestoreBannerView . hide ( ) ;
2019-12-06 13:00:08 +11:00
}
}
2016-02-05 16:10:33 -08:00
private void updateDefaultSubscriptionId ( Optional < Integer > defaultSubscriptionId ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " updateDefaultSubscriptionId( " + defaultSubscriptionId . orNull ( ) + " ) " ) ;
2016-02-05 16:10:33 -08:00
sendButton . setDefaultSubscriptionId ( defaultSubscriptionId ) ;
}
2013-03-04 17:43:04 -08:00
private void initializeMmsEnabledCheck ( ) {
new AsyncTask < Void , Void , Boolean > ( ) {
@Override
protected Boolean doInBackground ( Void . . . params ) {
2014-12-29 14:01:02 -08:00
return Util . isMmsCapable ( ConversationActivity . this ) ;
2013-03-04 17:43:04 -08:00
}
@Override
protected void onPostExecute ( Boolean isMmsEnabled ) {
ConversationActivity . this . isMmsEnabled = isMmsEnabled ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2013-03-04 17:43:04 -08:00
}
2017-06-06 18:03:09 -07:00
private ListenableFuture < Boolean > initializeIdentityRecords ( ) {
final SettableFuture < Boolean > future = new SettableFuture < > ( ) ;
2017-08-01 08:56:00 -07:00
new AsyncTask < Recipient , Void , Pair < IdentityRecordList , String > > ( ) {
2017-06-06 18:03:09 -07:00
@Override
2017-08-01 08:56:00 -07:00
protected @NonNull Pair < IdentityRecordList , String > doInBackground ( Recipient . . . params ) {
IdentityDatabase identityDatabase = DatabaseFactory . getIdentityDatabase ( ConversationActivity . this ) ;
IdentityRecordList identityRecordList = new IdentityRecordList ( ) ;
List < Recipient > recipients = new LinkedList < > ( ) ;
if ( params [ 0 ] . isGroupRecipient ( ) ) {
recipients . addAll ( DatabaseFactory . getGroupDatabase ( ConversationActivity . this )
. getGroupMembers ( params [ 0 ] . getAddress ( ) . toGroupString ( ) , false ) ) ;
} else {
recipients . add ( params [ 0 ] ) ;
}
2017-06-06 18:03:09 -07:00
2017-08-01 08:56:00 -07:00
for ( Recipient recipient : recipients ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Loading identity for: " + recipient . getAddress ( ) ) ;
2017-08-01 08:56:00 -07:00
identityRecordList . add ( identityDatabase . getIdentity ( recipient . getAddress ( ) ) ) ;
}
2017-06-06 18:03:09 -07:00
2017-08-01 08:56:00 -07:00
String message = null ;
2017-06-06 18:03:09 -07:00
2017-08-01 08:56:00 -07:00
if ( identityRecordList . isUnverified ( ) ) {
message = IdentityUtil . getUnverifiedBannerDescription ( ConversationActivity . this , identityRecordList . getUnverifiedRecipients ( ConversationActivity . this ) ) ;
2017-06-06 18:03:09 -07:00
}
2017-08-01 08:56:00 -07:00
return new Pair < > ( identityRecordList , message ) ;
2017-06-06 18:03:09 -07:00
}
@Override
protected void onPostExecute ( @NonNull Pair < IdentityRecordList , String > result ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Got identity records: " + result . first . isUnverified ( ) ) ;
2017-06-06 18:03:09 -07:00
identityRecords . replaceWith ( result . first ) ;
if ( result . second ! = null ) {
2018-08-02 09:25:33 -04:00
Log . d ( TAG , " Replacing banner... " ) ;
2017-06-06 18:03:09 -07:00
unverifiedBannerView . get ( ) . display ( result . second , result . first . getUnverifiedRecords ( ) ,
new UnverifiedClickedListener ( ) ,
new UnverifiedDismissedListener ( ) ) ;
} else if ( unverifiedBannerView . resolved ( ) ) {
2018-08-02 09:25:33 -04:00
Log . d ( TAG , " Clearing banner... " ) ;
2017-06-06 18:03:09 -07:00
unverifiedBannerView . get ( ) . hide ( ) ;
}
2020-01-10 11:35:16 +11:00
// titleView.setVerified(isSecureText && identityRecords.isVerified());
2017-06-06 18:03:09 -07:00
future . set ( true ) ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , recipient ) ;
2017-06-06 18:03:09 -07:00
return future ;
}
2014-12-24 18:32:51 -08:00
private void initializeViews ( ) {
2020-01-10 11:35:16 +11:00
titleTextView = findViewById ( R . id . titleTextView ) ;
2018-08-10 12:18:02 -04:00
buttonToggle = ViewUtil . findById ( this , R . id . button_toggle ) ;
sendButton = ViewUtil . findById ( this , R . id . send_button ) ;
attachButton = ViewUtil . findById ( this , R . id . attach_button ) ;
composeText = ViewUtil . findById ( this , R . id . embedded_text_editor ) ;
charactersLeft = ViewUtil . findById ( this , R . id . space_left ) ;
emojiDrawerStub = ViewUtil . findStubById ( this , R . id . emoji_drawer_stub ) ;
unblockButton = ViewUtil . findById ( this , R . id . unblock_button ) ;
makeDefaultSmsButton = ViewUtil . findById ( this , R . id . make_default_sms_button ) ;
registerButton = ViewUtil . findById ( this , R . id . register_button ) ;
composePanel = ViewUtil . findById ( this , R . id . bottom_panel ) ;
container = ViewUtil . findById ( this , R . id . layout_container ) ;
reminderView = ViewUtil . findStubById ( this , R . id . reminder_stub ) ;
unverifiedBannerView = ViewUtil . findStubById ( this , R . id . unverified_banner_stub ) ;
groupShareProfileView = ViewUtil . findStubById ( this , R . id . group_share_profile_view_stub ) ;
quickAttachmentToggle = ViewUtil . findById ( this , R . id . quick_attachment_toggle ) ;
inlineAttachmentToggle = ViewUtil . findById ( this , R . id . inline_attachment_container ) ;
inputPanel = ViewUtil . findById ( this , R . id . bottom_panel ) ;
2019-02-01 09:06:59 -08:00
searchNav = ViewUtil . findById ( this , R . id . conversation_search_nav ) ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionViewContainer = ViewUtil . findById ( this , R . id . mentionCandidateSelectionViewContainer ) ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView = ViewUtil . findById ( this , R . id . userSelectionView ) ;
2020-01-28 11:13:07 +11:00
sessionRestoreBannerView = ViewUtil . findById ( this , R . id . sessionRestoreBannerView ) ;
2020-01-16 14:35:51 +11:00
messageStatusProgressBar = ViewUtil . findById ( this , R . id . messageStatusProgressBar ) ;
2020-01-17 10:37:06 +11:00
muteIndicatorImageView = ViewUtil . findById ( this , R . id . muteIndicatorImageView ) ;
2020-01-16 14:35:51 +11:00
actionBarSubtitleTextView = ViewUtil . findById ( this , R . id . subtitleTextView ) ;
2018-08-10 12:18:02 -04:00
ImageButton quickCameraToggle = ViewUtil . findById ( this , R . id . quick_camera_toggle ) ;
ImageButton inlineAttachmentButton = ViewUtil . findById ( this , R . id . inline_attachment_button ) ;
2015-04-16 01:38:33 -04:00
2015-07-25 16:58:51 +02:00
container . addOnKeyboardShownListener ( this ) ;
2017-01-19 11:31:41 -08:00
inputPanel . setListener ( this ) ;
2016-12-26 15:14:23 -08:00
inputPanel . setMediaListener ( this ) ;
2015-07-25 16:58:51 +02:00
2017-01-18 18:46:40 -08:00
attachmentTypeSelector = null ;
2015-10-28 09:47:09 -07:00
attachmentManager = new AttachmentManager ( this , this ) ;
2018-01-24 19:17:44 -08:00
audioRecorder = new AudioRecorder ( this ) ;
2018-10-29 15:14:31 -07:00
typingTextWatcher = new TypingStatusTextWatcher ( ) ;
2019-10-10 14:44:08 +11:00
mentionTextWatcher = new MentionTextWatcher ( ) ;
2012-07-19 14:22:03 -07:00
2013-06-27 20:57:27 -07:00
SendButtonListener sendButtonListener = new SendButtonListener ( ) ;
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener ( ) ;
2012-07-19 14:22:03 -07:00
2015-07-20 19:25:54 -07:00
composeText . setOnEditorActionListener ( sendButtonListener ) ;
2019-02-14 13:55:48 -08:00
composeText . setCursorPositionChangedListener ( this ) ;
2015-05-18 10:26:32 -07:00
attachButton . setOnClickListener ( new AttachButtonListener ( ) ) ;
2015-07-26 22:49:43 +01:00
attachButton . setOnLongClickListener ( new AttachButtonLongClickListener ( ) ) ;
2012-07-19 14:22:03 -07:00
sendButton . setOnClickListener ( sendButtonListener ) ;
2013-03-13 17:45:32 -07:00
sendButton . setEnabled ( true ) ;
2017-12-05 11:52:03 -08:00
sendButton . addOnTransportChangedListener ( ( newTransport , manuallySelected ) - > {
calculateCharactersRemaining ( ) ;
2019-01-15 00:41:05 -08:00
updateLinkPreviewState ( ) ;
2017-12-05 11:52:03 -08:00
composeText . setTransport ( newTransport ) ;
2019-04-12 16:22:38 -03:00
if ( manuallySelected ) recordTransportPreference ( newTransport ) ;
2015-02-16 02:38:09 -08:00
} ) ;
2019-07-22 13:08:56 +10:00
/ *
2017-10-03 16:27:12 -07:00
titleView . setOnClickListener ( v - > handleConversationSettings ( ) ) ;
2017-10-05 20:07:44 +02:00
titleView . setOnLongClickListener ( v - > handleDisplayQuickContact ( ) ) ;
2019-07-22 13:08:56 +10:00
* /
2017-09-12 22:48:30 -07:00
unblockButton . setOnClickListener ( v - > handleUnblock ( ) ) ;
makeDefaultSmsButton . setOnClickListener ( v - > handleMakeDefaultSms ( ) ) ;
2017-12-25 15:57:33 -08:00
registerButton . setOnClickListener ( v - > handleRegisterForSignal ( ) ) ;
2016-11-20 15:56:47 -08:00
2013-06-27 20:57:27 -07:00
composeText . setOnKeyListener ( composeKeyPressedListener ) ;
composeText . addTextChangedListener ( composeKeyPressedListener ) ;
2012-07-19 14:22:03 -07:00
composeText . setOnEditorActionListener ( sendButtonListener ) ;
2013-06-27 20:57:27 -07:00
composeText . setOnClickListener ( composeKeyPressedListener ) ;
2014-05-28 20:53:34 -07:00
composeText . setOnFocusChangeListener ( composeKeyPressedListener ) ;
2015-04-16 01:38:33 -04:00
2019-02-25 15:21:37 -08:00
if ( getPackageManager ( ) . hasSystemFeature ( PackageManager . FEATURE_CAMERA ) & & Camera . getNumberOfCameras ( ) > 0 ) {
quickCameraToggle . setVisibility ( View . VISIBLE ) ;
2015-11-18 14:52:26 -08:00
quickCameraToggle . setOnClickListener ( new QuickCameraToggleListener ( ) ) ;
2015-04-16 01:38:33 -04:00
} else {
2015-11-18 14:52:26 -08:00
quickCameraToggle . setVisibility ( View . GONE ) ;
2015-04-16 01:38:33 -04:00
}
2018-08-10 12:18:02 -04:00
2019-02-01 09:06:59 -08:00
searchNav . setEventListener ( this ) ;
2018-08-10 12:18:02 -04:00
inlineAttachmentButton . setOnClickListener ( v - > handleAddAttachment ( ) ) ;
2014-12-24 18:32:51 -08:00
}
2015-06-22 08:46:43 -07:00
protected void initializeActionBar ( ) {
2019-05-24 14:31:48 -03:00
Toolbar toolbar = findViewById ( R . id . toolbar ) ;
2020-01-15 17:13:46 +11:00
toolbar . getOverflowIcon ( ) . setColorFilter ( Color . WHITE , PorterDuff . Mode . SRC_IN ) ;
2019-05-24 14:31:48 -03:00
setSupportActionBar ( toolbar ) ;
2017-12-05 11:52:03 -08:00
ActionBar supportActionBar = getSupportActionBar ( ) ;
if ( supportActionBar = = null ) throw new AssertionError ( ) ;
2020-01-10 11:35:16 +11:00
// supportActionBar.setDisplayHomeAsUpEnabled(true);
2017-12-05 11:52:03 -08:00
supportActionBar . setDisplayShowTitleEnabled ( false ) ;
2015-06-09 07:37:20 -07:00
}
2014-12-24 18:32:51 -08:00
private void initializeResources ( ) {
2017-08-01 08:56:00 -07:00
if ( recipient ! = null ) recipient . removeListener ( this ) ;
2018-08-06 14:42:22 -04:00
recipient = Recipient . from ( this , getIntent ( ) . getParcelableExtra ( ADDRESS_EXTRA ) , true ) ;
2014-12-24 18:32:51 -08:00
threadId = getIntent ( ) . getLongExtra ( THREAD_ID_EXTRA , - 1 ) ;
2015-12-01 01:38:37 +01:00
archived = getIntent ( ) . getBooleanExtra ( IS_ARCHIVED_EXTRA , false ) ;
2014-12-24 18:32:51 -08:00
distributionType = getIntent ( ) . getIntExtra ( DISTRIBUTION_TYPE_EXTRA , ThreadDatabase . DistributionTypes . DEFAULT ) ;
2017-10-16 13:11:42 -07:00
glideRequests = GlideApp . with ( this ) ;
2012-07-19 14:22:03 -07:00
2017-08-01 08:56:00 -07:00
recipient . addListener ( this ) ;
2015-02-10 03:15:50 -08:00
}
2019-01-15 00:41:05 -08:00
private void initializeLinkPreviewObserver ( ) {
2019-04-17 10:21:30 -04:00
linkPreviewViewModel = ViewModelProviders . of ( this , new LinkPreviewViewModel . Factory ( new LinkPreviewRepository ( this ) ) ) . get ( LinkPreviewViewModel . class ) ;
2019-01-15 00:41:05 -08:00
if ( ! TextSecurePreferences . isLinkPreviewsEnabled ( this ) ) {
linkPreviewViewModel . onUserCancel ( ) ;
return ;
}
linkPreviewViewModel . getLinkPreviewState ( ) . observe ( this , previewState - > {
if ( previewState = = null ) return ;
if ( previewState . isLoading ( ) ) {
Log . d ( TAG , " Loading link preview. " ) ;
inputPanel . setLinkPreviewLoading ( ) ;
} else {
Log . d ( TAG , " Setting link preview: " + previewState . getLinkPreview ( ) . isPresent ( ) ) ;
inputPanel . setLinkPreview ( glideRequests , previewState . getLinkPreview ( ) ) ;
}
updateToggleButtonState ( ) ;
} ) ;
}
2019-02-01 09:06:59 -08:00
private void initializeSearchObserver ( ) {
searchViewModel = ViewModelProviders . of ( this ) . get ( ConversationSearchViewModel . class ) ;
searchViewModel . getSearchResults ( ) . observe ( this , result - > {
if ( result = = null ) return ;
if ( ! result . getResults ( ) . isEmpty ( ) ) {
MessageResult messageResult = result . getResults ( ) . get ( result . getPosition ( ) ) ;
fragment . jumpToMessage ( messageResult . messageRecipient . getAddress ( ) , messageResult . receivedTimestampMs , searchViewModel : : onMissingResult ) ;
}
searchNav . setData ( result . getPosition ( ) , result . getResults ( ) . size ( ) ) ;
} ) ;
}
2019-04-17 10:21:30 -04:00
private void initializeStickerObserver ( ) {
StickerSearchRepository repository = new StickerSearchRepository ( this ) ;
stickerViewModel = ViewModelProviders . of ( this , new ConversationStickerViewModel . Factory ( getApplication ( ) , repository ) )
. get ( ConversationStickerViewModel . class ) ;
stickerViewModel . getStickerResults ( ) . observe ( this , stickers - > {
if ( stickers = = null ) return ;
inputPanel . setStickerSuggestions ( stickers ) ;
} ) ;
stickerViewModel . getStickersAvailability ( ) . observe ( this , stickersAvailable - > {
if ( stickersAvailable = = null ) return ;
boolean isSystemEmojiPreferred = TextSecurePreferences . isSystemEmojiPreferred ( this ) ;
MediaKeyboardMode keyboardMode = TextSecurePreferences . getMediaKeyboardMode ( this ) ;
boolean stickerIntro = ! TextSecurePreferences . hasSeenStickerIntroTooltip ( this ) ;
if ( stickersAvailable ) {
inputPanel . showMediaKeyboardToggle ( true ) ;
inputPanel . setMediaKeyboardToggleMode ( isSystemEmojiPreferred | | keyboardMode = = MediaKeyboardMode . STICKER ) ;
if ( stickerIntro ) showStickerIntroductionTooltip ( ) ;
}
if ( emojiDrawerStub . resolved ( ) ) {
initializeMediaKeyboardProviders ( emojiDrawerStub . get ( ) , stickersAvailable ) ;
}
} ) ;
}
private void showStickerIntroductionTooltip ( ) {
TextSecurePreferences . setMediaKeyboardMode ( this , MediaKeyboardMode . STICKER ) ;
inputPanel . setMediaKeyboardToggleMode ( true ) ;
TooltipPopup . forTarget ( inputPanel . getMediaKeyboardToggleAnchorView ( ) )
. setBackgroundTint ( getResources ( ) . getColor ( R . color . core_blue ) )
. setTextColor ( getResources ( ) . getColor ( R . color . core_white ) )
. setText ( R . string . ConversationActivity_new_say_it_with_stickers )
. setOnDismissListener ( ( ) - > {
TextSecurePreferences . setHasSeenStickerIntroTooltip ( this , true ) ;
EventBus . getDefault ( ) . removeStickyEvent ( StickerPackInstallEvent . class ) ;
} )
. show ( TooltipPopup . POSITION_ABOVE ) ;
}
2019-02-01 09:06:59 -08:00
@Override
public void onSearchMoveUpPressed ( ) {
searchViewModel . onMoveUp ( ) ;
}
@Override
public void onSearchMoveDownPressed ( ) {
searchViewModel . onMoveDown ( ) ;
}
2019-01-15 00:41:05 -08:00
2017-05-19 18:01:40 -07:00
private void initializeProfiles ( ) {
2017-06-14 09:53:22 -07:00
if ( ! isSecureText ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " SMS contact, no profile fetch needed. " ) ;
2017-06-14 09:53:22 -07:00
return ;
}
2017-05-19 18:01:40 -07:00
ApplicationContext . getInstance ( this )
. getJobManager ( )
2019-03-28 08:56:35 -07:00
. add ( new RetrieveProfileJob ( recipient ) ) ;
2017-05-19 18:01:40 -07:00
}
2015-02-10 03:15:50 -08:00
@Override
2017-08-01 08:56:00 -07:00
public void onModified ( final Recipient recipient ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onModified( " + recipient . getAddress ( ) . serialize ( ) + " ) " ) ;
2017-10-04 14:56:31 -07:00
Util . runOnMain ( ( ) - > {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onModifiedRun(): " + recipient . getRegistered ( ) ) ;
2020-01-10 11:35:16 +11:00
updateTitleTextView ( glideRequests , recipient ) ;
2020-01-17 10:37:06 +11:00
updateSubtitleTextView ( ) ;
2020-01-10 11:35:16 +11:00
// titleView.setVerified(identityRecords.isVerified());
2017-10-04 14:56:31 -07:00
setBlockedUserState ( recipient , isSecureText , isDefaultSms ) ;
setActionBarColor ( recipient . getColor ( ) ) ;
setGroupShareProfileReminder ( recipient ) ;
2017-11-15 16:29:00 -08:00
updateReminders ( recipient . hasSeenInviteReminder ( ) ) ;
2017-10-04 14:56:31 -07:00
updateDefaultSubscriptionId ( recipient . getDefaultSubscriptionId ( ) ) ;
initializeSecurity ( isSecureText , isDefaultSms ) ;
2019-02-01 09:06:59 -08:00
2019-02-15 19:24:23 -08:00
if ( searchViewItem = = null | | ! searchViewItem . isActionViewExpanded ( ) ) {
2019-02-01 09:06:59 -08:00
invalidateOptionsMenu ( ) ;
}
2015-06-11 12:53:38 -07:00
} ) ;
2012-07-19 14:22:03 -07:00
}
2014-02-15 18:40:08 -08:00
2017-06-06 18:03:09 -07:00
@Subscribe ( threadMode = ThreadMode . MAIN )
public void onIdentityRecordUpdate ( final IdentityRecord event ) {
initializeIdentityRecords ( ) ;
}
2019-04-17 10:21:30 -04:00
@Subscribe ( threadMode = ThreadMode . MAIN , sticky = true )
public void onStickerPackInstalled ( final StickerPackInstallEvent event ) {
if ( ! TextSecurePreferences . hasSeenStickerIntroTooltip ( this ) ) return ;
EventBus . getDefault ( ) . removeStickyEvent ( event ) ;
TooltipPopup . forTarget ( inputPanel . getMediaKeyboardToggleAnchorView ( ) )
. setText ( R . string . ConversationActivity_sticker_pack_installed )
. setIconGlideModel ( event . getIconGlideModel ( ) )
. show ( TooltipPopup . POSITION_ABOVE ) ;
}
2012-07-19 14:22:03 -07:00
private void initializeReceivers ( ) {
securityUpdateReceiver = new BroadcastReceiver ( ) {
@Override
public void onReceive ( Context context , Intent intent ) {
2017-03-14 13:24:24 -07:00
initializeSecurity ( isSecureText , isDefaultSms ) ;
2015-09-23 15:47:48 -07:00
calculateCharactersRemaining ( ) ;
2012-07-19 14:22:03 -07:00
}
} ;
registerReceiver ( securityUpdateReceiver ,
2014-11-03 15:16:04 -08:00
new IntentFilter ( SecurityEvent . SECURITY_UPDATE_EVENT ) ,
2012-07-19 14:22:03 -07:00
KeyCachingService . KEY_PERMISSION , null ) ;
}
//////// Helper Methods
private void addAttachment ( int type ) {
2019-01-15 00:41:05 -08:00
linkPreviewViewModel . onUserCancel ( ) ;
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Selected: " + type ) ;
2012-07-19 14:22:03 -07:00
switch ( type ) {
2017-04-18 16:33:03 -07:00
case AttachmentTypeSelector . ADD_GALLERY :
2019-01-17 11:42:01 -08:00
AttachmentManager . selectGallery ( this , MEDIA_SENDER , recipient , composeText . getTextTrimmed ( ) , sendButton . getSelectedTransport ( ) ) ; break ;
2017-04-18 16:33:03 -07:00
case AttachmentTypeSelector . ADD_DOCUMENT :
AttachmentManager . selectDocument ( this , PICK_DOCUMENT ) ; break ;
case AttachmentTypeSelector . ADD_SOUND :
2012-07-19 14:22:03 -07:00
AttachmentManager . selectAudio ( this , PICK_AUDIO ) ; break ;
2017-04-18 16:33:03 -07:00
case AttachmentTypeSelector . ADD_CONTACT_INFO :
2018-04-26 17:03:54 -07:00
AttachmentManager . selectContactInfo ( this , PICK_CONTACT ) ; break ;
2015-12-18 14:37:11 -08:00
case AttachmentTypeSelector . ADD_LOCATION :
AttachmentManager . selectLocation ( this , PICK_LOCATION ) ; break ;
2017-04-18 16:33:03 -07:00
case AttachmentTypeSelector . TAKE_PHOTO :
2015-11-23 17:56:41 -08:00
attachmentManager . capturePhoto ( this , TAKE_PHOTO ) ; break ;
2016-10-16 19:05:07 -07:00
case AttachmentTypeSelector . ADD_GIF :
2020-02-01 15:12:06 +11:00
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Search GIFs? " ) ;
builder . setMessage ( " You will not have full metadata protection when sending GIFs. " ) ;
builder . setPositiveButton ( " OK " , ( dialog , which ) - > {
AttachmentManager . selectGif ( this , PICK_GIF , ! isSecureText ) ;
dialog . dismiss ( ) ;
} ) ;
builder . setNegativeButton ( " Cancel " , ( dialog , which ) - > dialog . dismiss ( ) ) ;
builder . create ( ) . show ( ) ;
break ;
2012-07-19 14:22:03 -07:00
}
}
2018-07-25 11:30:48 -04:00
private ListenableFuture < Boolean > setMedia ( @Nullable Uri uri , @NonNull MediaType mediaType ) {
return setMedia ( uri , mediaType , 0 , 0 ) ;
2018-03-20 11:27:11 -07:00
}
2018-07-25 11:30:48 -04:00
private ListenableFuture < Boolean > setMedia ( @Nullable Uri uri , @NonNull MediaType mediaType , int width , int height ) {
if ( uri = = null ) {
return new SettableFuture < > ( false ) ;
}
2018-05-16 23:40:14 -07:00
2018-06-29 11:08:13 -07:00
if ( MediaType . VCARD . equals ( mediaType ) & & isSecureText ) {
openContactShareEditor ( uri ) ;
2018-07-25 11:30:48 -04:00
return new SettableFuture < > ( false ) ;
2018-11-20 09:59:23 -08:00
} else if ( MediaType . IMAGE . equals ( mediaType ) | | MediaType . GIF . equals ( mediaType ) | | MediaType . VIDEO . equals ( mediaType ) ) {
2019-02-11 15:05:37 -08:00
Media media = new Media ( uri , MediaUtil . getMimeType ( this , uri ) , 0 , width , height , 0 , Optional . absent ( ) , Optional . absent ( ) ) ;
2019-03-13 16:05:25 -07:00
startActivityForResult ( MediaSendActivity . buildEditorIntent ( ConversationActivity . this , Collections . singletonList ( media ) , recipient , composeText . getTextTrimmed ( ) , sendButton . getSelectedTransport ( ) ) , MEDIA_SENDER ) ;
2018-11-20 09:59:23 -08:00
return new SettableFuture < > ( false ) ;
2018-06-29 11:08:13 -07:00
} else {
2018-07-25 11:30:48 -04:00
return attachmentManager . setMedia ( glideRequests , uri , mediaType , getCurrentMediaConstraints ( ) , width , height ) ;
2018-06-29 11:08:13 -07:00
}
2012-07-19 14:22:03 -07:00
}
2018-04-26 17:03:54 -07:00
private void openContactShareEditor ( Uri contactUri ) {
2018-05-16 23:40:14 -07:00
Intent intent = ContactShareEditActivity . getIntent ( this , Collections . singletonList ( contactUri ) ) ;
2018-04-26 17:03:54 -07:00
startActivityForResult ( intent , GET_CONTACT_DETAILS ) ;
}
2014-06-04 01:24:44 +02:00
private void addAttachmentContactInfo ( Uri contactUri ) {
2013-10-17 02:28:36 +02:00
ContactAccessor contactDataList = ContactAccessor . getInstance ( ) ;
ContactData contactData = contactDataList . getContactData ( this , contactUri ) ;
if ( contactData . numbers . size ( ) = = 1 ) composeText . append ( contactData . numbers . get ( 0 ) . number ) ;
else if ( contactData . numbers . size ( ) > 1 ) selectContactInfo ( contactData ) ;
}
2018-04-26 17:03:54 -07:00
private void sendSharedContact ( List < Contact > contacts ) {
int subscriptionId = sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ;
long expiresIn = recipient . getExpireMessages ( ) * 1000L ;
boolean initiating = threadId = = - 1 ;
2019-04-17 10:21:30 -04:00
sendMediaMessage ( isSmsForced ( ) , " " , attachmentManager . buildSlideDeck ( ) , null , contacts , Collections . emptyList ( ) , expiresIn , subscriptionId , initiating , false ) ;
2018-04-26 17:03:54 -07:00
}
2013-10-17 02:28:36 +02:00
private void selectContactInfo ( ContactData contactData ) {
2014-12-12 18:31:20 -08:00
final CharSequence [ ] numbers = new CharSequence [ contactData . numbers . size ( ) ] ;
2013-10-17 02:28:36 +02:00
final CharSequence [ ] numberItems = new CharSequence [ contactData . numbers . size ( ) ] ;
2014-12-12 18:31:20 -08:00
2013-10-17 02:28:36 +02:00
for ( int i = 0 ; i < contactData . numbers . size ( ) ; i + + ) {
2014-12-12 18:31:20 -08:00
numbers [ i ] = contactData . numbers . get ( i ) . number ;
2013-10-17 02:28:36 +02:00
numberItems [ i ] = contactData . numbers . get ( i ) . type + " : " + contactData . numbers . get ( i ) . number ;
}
2015-05-20 23:36:30 +02:00
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
2015-03-24 13:44:22 +01:00
builder . setIconAttribute ( R . attr . conversation_attach_contact_info ) ;
2013-10-17 02:28:36 +02:00
builder . setTitle ( R . string . ConversationActivity_select_contact_info ) ;
2017-12-05 11:52:03 -08:00
builder . setItems ( numberItems , ( dialog , which ) - > composeText . append ( numbers [ which ] ) ) ;
2013-10-17 02:28:36 +02:00
builder . show ( ) ;
}
2014-12-11 17:13:01 -08:00
private Drafts getDraftsForCurrentState ( ) {
Drafts drafts = new Drafts ( ) ;
2013-02-04 00:13:07 -08:00
if ( ! Util . isEmpty ( composeText ) ) {
2017-07-04 19:09:36 +02:00
drafts . add ( new Draft ( Draft . TEXT , composeText . getTextTrimmed ( ) ) ) ;
2013-02-04 00:13:07 -08:00
}
2015-10-15 14:40:45 -07:00
for ( Slide slide : attachmentManager . buildSlideDeck ( ) . getSlides ( ) ) {
2016-12-11 13:37:27 -08:00
if ( slide . hasAudio ( ) & & slide . getUri ( ) ! = null ) drafts . add ( new Draft ( Draft . AUDIO , slide . getUri ( ) . toString ( ) ) ) ;
else if ( slide . hasVideo ( ) & & slide . getUri ( ) ! = null ) drafts . add ( new Draft ( Draft . VIDEO , slide . getUri ( ) . toString ( ) ) ) ;
else if ( slide . hasLocation ( ) ) drafts . add ( new Draft ( Draft . LOCATION , ( ( LocationSlide ) slide ) . getPlace ( ) . serialize ( ) ) ) ;
else if ( slide . hasImage ( ) & & slide . getUri ( ) ! = null ) drafts . add ( new Draft ( Draft . IMAGE , slide . getUri ( ) . toString ( ) ) ) ;
2013-02-04 00:13:07 -08:00
}
2018-04-24 11:09:54 -07:00
Optional < QuoteModel > quote = inputPanel . getQuote ( ) ;
if ( quote . isPresent ( ) ) {
drafts . add ( new Draft ( Draft . QUOTE , new QuoteId ( quote . get ( ) . getId ( ) , quote . get ( ) . getAuthor ( ) ) . serialize ( ) ) ) ;
}
2013-02-04 00:13:07 -08:00
return drafts ;
}
2015-06-22 08:46:43 -07:00
protected ListenableFuture < Long > saveDraft ( ) {
final SettableFuture < Long > future = new SettableFuture < > ( ) ;
2017-08-01 08:56:00 -07:00
if ( this . recipient = = null ) {
2015-06-22 08:46:43 -07:00
future . set ( threadId ) ;
return future ;
}
2013-02-04 00:13:07 -08:00
2015-01-04 00:25:35 +01:00
final Drafts drafts = getDraftsForCurrentState ( ) ;
final long thisThreadId = this . threadId ;
final int thisDistributionType = this . distributionType ;
2013-02-04 00:13:07 -08:00
2015-06-22 08:46:43 -07:00
new AsyncTask < Long , Void , Long > ( ) {
2013-02-04 00:13:07 -08:00
@Override
2015-06-22 08:46:43 -07:00
protected Long doInBackground ( Long . . . params ) {
2014-12-11 17:13:01 -08:00
ThreadDatabase threadDatabase = DatabaseFactory . getThreadDatabase ( ConversationActivity . this ) ;
2015-01-04 00:25:35 +01:00
DraftDatabase draftDatabase = DatabaseFactory . getDraftDatabase ( ConversationActivity . this ) ;
long threadId = params [ 0 ] ;
2014-12-11 17:13:01 -08:00
if ( drafts . size ( ) > 0 ) {
2017-08-01 08:56:00 -07:00
if ( threadId = = - 1 ) threadId = threadDatabase . getThreadIdFor ( getRecipient ( ) , thisDistributionType ) ;
2015-01-04 00:25:35 +01:00
2018-01-24 19:17:44 -08:00
draftDatabase . insertDrafts ( threadId , drafts ) ;
2015-10-16 13:59:40 -07:00
threadDatabase . updateSnippet ( threadId , drafts . getSnippet ( ConversationActivity . this ) ,
2018-01-24 19:17:44 -08:00
drafts . getUriSnippet ( ) ,
2015-11-23 15:07:41 -08:00
System . currentTimeMillis ( ) , Types . BASE_DRAFT_TYPE , true ) ;
2015-01-04 00:25:35 +01:00
} else if ( threadId > 0 ) {
2015-11-23 15:07:41 -08:00
threadDatabase . update ( threadId , false ) ;
2014-12-11 17:13:01 -08:00
}
2015-06-22 08:46:43 -07:00
return threadId ;
2013-02-04 00:13:07 -08:00
}
2015-06-22 08:46:43 -07:00
@Override
protected void onPostExecute ( Long result ) {
future . set ( result ) ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , thisThreadId ) ;
2015-06-22 08:46:43 -07:00
return future ;
2013-02-04 00:13:07 -08:00
}
2015-06-30 09:16:05 -07:00
private void setActionBarColor ( MaterialColor color ) {
2017-12-05 11:52:03 -08:00
ActionBar supportActionBar = getSupportActionBar ( ) ;
if ( supportActionBar = = null ) throw new AssertionError ( ) ;
2020-01-10 11:35:16 +11:00
supportActionBar . setBackgroundDrawable ( new ColorDrawable ( getResources ( ) . getColor ( R . color . action_bar_background ) ) ) ;
setStatusBarColor ( getResources ( ) . getColor ( R . color . action_bar_background ) ) ;
2015-06-23 15:10:50 -07:00
}
2017-08-01 08:56:00 -07:00
private void setBlockedUserState ( Recipient recipient , boolean isSecureText , boolean isDefaultSms ) {
2019-12-12 10:07:17 +11:00
if ( recipient . isGroupRecipient ( ) & & recipient . getAddress ( ) . isRSSFeed ( ) ) {
2019-08-28 16:07:20 +10:00
unblockButton . setVisibility ( View . GONE ) ;
composePanel . setVisibility ( View . GONE ) ;
makeDefaultSmsButton . setVisibility ( View . GONE ) ;
registerButton . setVisibility ( View . GONE ) ;
} else if ( recipient . isBlocked ( ) ) {
2015-06-09 07:37:20 -07:00
unblockButton . setVisibility ( View . VISIBLE ) ;
composePanel . setVisibility ( View . GONE ) ;
2016-11-20 15:56:47 -08:00
makeDefaultSmsButton . setVisibility ( View . GONE ) ;
2017-12-25 15:57:33 -08:00
registerButton . setVisibility ( View . GONE ) ;
} else if ( ! isSecureText & & isPushGroupConversation ( ) ) {
unblockButton . setVisibility ( View . GONE ) ;
composePanel . setVisibility ( View . GONE ) ;
makeDefaultSmsButton . setVisibility ( View . GONE ) ;
registerButton . setVisibility ( View . VISIBLE ) ;
2017-01-18 11:01:13 -08:00
} else if ( ! isSecureText & & ! isDefaultSms ) {
2016-11-20 15:56:47 -08:00
unblockButton . setVisibility ( View . GONE ) ;
composePanel . setVisibility ( View . GONE ) ;
makeDefaultSmsButton . setVisibility ( View . VISIBLE ) ;
2017-12-25 15:57:33 -08:00
registerButton . setVisibility ( View . GONE ) ;
2015-06-09 07:37:20 -07:00
} else {
composePanel . setVisibility ( View . VISIBLE ) ;
unblockButton . setVisibility ( View . GONE ) ;
2016-11-20 15:56:47 -08:00
makeDefaultSmsButton . setVisibility ( View . GONE ) ;
2017-12-25 15:57:33 -08:00
registerButton . setVisibility ( View . GONE ) ;
2015-06-09 07:37:20 -07:00
}
}
2017-08-22 11:51:01 -07:00
private void setGroupShareProfileReminder ( @NonNull Recipient recipient ) {
2019-12-12 10:07:17 +11:00
if ( recipient . isPushGroupRecipient ( ) & & ! recipient . isProfileSharing ( ) & & ! recipient . getAddress ( ) . isPublicChat ( ) & & ! recipient . getAddress ( ) . isRSSFeed ( ) ) {
2017-08-22 11:51:01 -07:00
groupShareProfileView . get ( ) . setRecipient ( recipient ) ;
2020-02-04 14:31:44 +11:00
groupShareProfileView . get ( ) . setVisibility ( View . GONE ) ; // Loki - Always hide for now
2017-08-22 11:51:01 -07:00
} else if ( groupShareProfileView . resolved ( ) ) {
groupShareProfileView . get ( ) . setVisibility ( View . GONE ) ;
}
}
2012-07-19 14:22:03 -07:00
private void calculateCharactersRemaining ( ) {
2019-09-18 14:49:08 +10:00
/ *
2017-07-04 19:09:36 +02:00
String messageBody = composeText . getTextTrimmed ( ) ;
2015-03-07 09:02:10 -08:00
TransportOption transportOption = sendButton . getSelectedTransport ( ) ;
2016-04-17 18:09:31 +02:00
CharacterState characterState = transportOption . calculateCharacters ( messageBody ) ;
2015-03-07 09:02:10 -08:00
2015-03-11 14:23:45 -07:00
if ( characterState . charactersRemaining < = 15 | | characterState . messagesSpent > 1 ) {
2017-12-05 11:52:03 -08:00
charactersLeft . setText ( String . format ( dynamicLanguage . getCurrentLocale ( ) ,
" %d/%d (%d) " ,
characterState . charactersRemaining ,
2019-02-26 19:29:52 -08:00
characterState . maxTotalMessageSize ,
2017-12-05 11:52:03 -08:00
characterState . messagesSpent ) ) ;
2015-03-11 14:23:45 -07:00
charactersLeft . setVisibility ( View . VISIBLE ) ;
} else {
charactersLeft . setVisibility ( View . GONE ) ;
2014-01-08 12:29:05 -10:00
}
2019-09-18 14:49:08 +10:00
* /
2012-07-19 14:22:03 -07:00
}
2019-04-17 10:21:30 -04:00
private void initializeMediaKeyboardProviders ( @NonNull MediaKeyboard mediaKeyboard , boolean stickersAvailable ) {
boolean isSystemEmojiPreferred = TextSecurePreferences . isSystemEmojiPreferred ( this ) ;
if ( stickersAvailable ) {
if ( isSystemEmojiPreferred ) {
mediaKeyboard . setProviders ( 0 , new StickerKeyboardProvider ( this , this ) ) ;
} else {
MediaKeyboardMode keyboardMode = TextSecurePreferences . getMediaKeyboardMode ( this ) ;
int index = keyboardMode = = MediaKeyboardMode . STICKER ? 1 : 0 ;
mediaKeyboard . setProviders ( index ,
new EmojiKeyboardProvider ( this , inputPanel ) ,
new StickerKeyboardProvider ( this , this ) ) ;
}
} else if ( ! isSystemEmojiPreferred ) {
mediaKeyboard . setProviders ( 0 , new EmojiKeyboardProvider ( this , inputPanel ) ) ;
}
}
2012-08-01 17:39:36 -07:00
private boolean isSingleConversation ( ) {
2017-08-01 08:56:00 -07:00
return getRecipient ( ) ! = null & & ! getRecipient ( ) . isGroupRecipient ( ) ;
2012-10-21 14:34:09 -07:00
}
2014-02-22 10:54:43 -08:00
private boolean isActiveGroup ( ) {
if ( ! isGroupConversation ( ) ) return false ;
2017-08-07 16:47:38 -07:00
Optional < GroupRecord > record = DatabaseFactory . getGroupDatabase ( this ) . getGroup ( getRecipient ( ) . getAddress ( ) . toGroupString ( ) ) ;
return record . isPresent ( ) & & record . get ( ) . isActive ( ) ;
2014-02-22 10:54:43 -08:00
}
2017-12-05 11:52:03 -08:00
@SuppressWarnings ( " SimplifiableIfStatement " )
2015-10-01 11:14:00 -07:00
private boolean isSelfConversation ( ) {
2017-08-01 08:56:00 -07:00
if ( ! TextSecurePreferences . isPushRegistered ( this ) ) return false ;
2017-12-05 11:52:03 -08:00
if ( recipient . isGroupRecipient ( ) ) return false ;
2015-10-01 11:14:00 -07:00
2017-08-01 08:56:00 -07:00
return Util . isOwnNumber ( this , recipient . getAddress ( ) ) ;
2015-10-01 11:14:00 -07:00
}
2012-10-21 14:34:09 -07:00
private boolean isGroupConversation ( ) {
2017-08-01 08:56:00 -07:00
return getRecipient ( ) ! = null & & getRecipient ( ) . isGroupRecipient ( ) ;
2012-07-19 14:22:03 -07:00
}
2014-02-17 20:25:40 -08:00
private boolean isPushGroupConversation ( ) {
2017-08-01 08:56:00 -07:00
return getRecipient ( ) ! = null & & getRecipient ( ) . isPushGroupRecipient ( ) ;
2014-02-17 20:25:40 -08:00
}
2018-04-26 17:03:54 -07:00
private boolean isSmsForced ( ) {
return sendButton . isManualSelection ( ) & & sendButton . getSelectedTransport ( ) . isSms ( ) ;
}
2017-08-01 08:56:00 -07:00
protected Recipient getRecipient ( ) {
return this . recipient ;
2012-07-19 14:22:03 -07:00
}
2015-06-22 08:46:43 -07:00
protected long getThreadId ( ) {
return this . threadId ;
}
2012-07-19 14:22:03 -07:00
private String getMessage ( ) throws InvalidMessageException {
2019-10-11 11:13:34 +11:00
String result = composeText . getTextTrimmed ( ) ;
if ( result . length ( ) < 1 & & ! attachmentManager . isAttachmentPresent ( ) ) throw new InvalidMessageException ( ) ;
for ( Mention mention : mentions ) {
2019-10-11 14:59:13 +11:00
try {
2019-10-11 16:37:28 +11:00
int startIndex = result . indexOf ( " @ " + mention . getDisplayName ( ) ) ;
2019-10-11 14:59:13 +11:00
int endIndex = startIndex + mention . getDisplayName ( ) . length ( ) + 1 ; // + 1 to include the @
result = result . substring ( 0 , startIndex ) + " @ " + mention . getHexEncodedPublicKey ( ) + result . substring ( endIndex ) ;
} catch ( Exception exception ) {
2019-10-11 16:37:28 +11:00
Log . d ( " Loki " , " Couldn't process mention due to error: " + exception . toString ( ) + " . " ) ;
2019-10-11 14:59:13 +11:00
}
2019-10-11 11:13:34 +11:00
}
return result ;
2012-07-19 14:22:03 -07:00
}
2019-02-26 19:29:52 -08:00
private Pair < String , Optional < Slide > > getSplitMessage ( String rawText , int maxPrimaryMessageSize ) {
String bodyText = rawText ;
2019-03-01 18:14:47 -08:00
Optional < Slide > textSlide = Optional . absent ( ) ;
2019-02-26 19:29:52 -08:00
if ( bodyText . length ( ) > maxPrimaryMessageSize ) {
bodyText = rawText . substring ( 0 , maxPrimaryMessageSize ) ;
2019-03-01 18:14:47 -08:00
byte [ ] textData = rawText . getBytes ( ) ;
2019-02-26 19:29:52 -08:00
String timestamp = new SimpleDateFormat ( " yyyy-MM-dd-HHmmss " , Locale . US ) . format ( new Date ( ) ) ;
String filename = String . format ( " signal-%s.txt " , timestamp ) ;
2019-02-25 17:47:30 -08:00
Uri textUri = BlobProvider . getInstance ( )
. forData ( textData )
. withMimeType ( MediaUtil . LONG_TEXT )
. withFileName ( filename )
. createForSingleSessionInMemory ( ) ;
2019-02-26 19:29:52 -08:00
2019-03-01 18:14:47 -08:00
textSlide = Optional . of ( new TextSlide ( this , textUri , filename , textData . length ) ) ;
2019-02-26 19:29:52 -08:00
}
2019-03-01 18:14:47 -08:00
return new Pair < > ( bodyText , textSlide ) ;
2019-02-26 19:29:52 -08:00
}
2015-09-04 17:33:22 -07:00
private MediaConstraints getCurrentMediaConstraints ( ) {
return sendButton . getSelectedTransport ( ) . getType ( ) = = Type . TEXTSECURE
2017-05-08 15:32:59 -07:00
? MediaConstraints . getPushMediaConstraints ( )
: MediaConstraints . getMmsMediaConstraints ( sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ) ;
2015-09-04 17:33:22 -07:00
}
2013-02-03 18:41:34 -08:00
private void markThreadAsRead ( ) {
new AsyncTask < Long , Void , Void > ( ) {
@Override
protected Void doInBackground ( Long . . . params ) {
2016-10-10 11:13:37 -07:00
Context context = ConversationActivity . this ;
2017-02-22 15:05:35 -08:00
List < MarkedMessageInfo > messageIds = DatabaseFactory . getThreadDatabase ( context ) . setRead ( params [ 0 ] , false ) ;
2016-02-19 17:07:41 -08:00
2016-10-10 11:13:37 -07:00
MarkReadReceiver . process ( context , messageIds ) ;
2016-02-19 17:07:41 -08:00
2013-02-03 18:41:34 -08:00
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , threadId ) ;
2013-02-03 18:41:34 -08:00
}
2017-02-13 22:35:47 -08:00
private void markLastSeen ( ) {
new AsyncTask < Long , Void , Void > ( ) {
@Override
protected Void doInBackground ( Long . . . params ) {
DatabaseFactory . getThreadDatabase ( ConversationActivity . this ) . setLastSeen ( params [ 0 ] ) ;
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , threadId ) ;
2017-02-13 22:35:47 -08:00
}
2015-06-22 08:46:43 -07:00
protected void sendComplete ( long threadId ) {
2014-11-08 11:35:58 -08:00
boolean refreshFragment = ( threadId ! = this . threadId ) ;
this . threadId = threadId ;
2012-07-19 14:22:03 -07:00
2015-04-08 17:43:14 -07:00
if ( fragment = = null | | ! fragment . isVisible ( ) | | isFinishing ( ) ) {
2014-11-24 22:48:50 -08:00
return ;
}
2017-02-13 22:35:47 -08:00
fragment . setLastSeen ( 0 ) ;
2014-02-17 15:47:58 -08:00
if ( refreshFragment ) {
2017-08-01 08:56:00 -07:00
fragment . reload ( recipient , threadId ) ;
2015-10-08 19:15:07 +02:00
MessageNotifier . setVisibleThread ( threadId ) ;
2012-07-19 14:22:03 -07:00
}
2014-11-08 11:35:58 -08:00
2014-03-26 15:11:56 -07:00
fragment . scrollToBottom ( ) ;
2015-05-18 10:26:32 -07:00
attachmentManager . cleanup ( ) ;
2019-01-15 00:41:05 -08:00
updateLinkPreviewState ( ) ;
2019-06-25 11:48:39 +10:00
}
2019-08-28 15:17:13 +10:00
@Override
public void handleThreadFriendRequestStatusChanged ( long threadID ) {
2019-10-29 12:13:22 +11:00
if ( threadID ! = this . threadId ) {
Recipient threadRecipient = DatabaseFactory . getThreadDatabase ( this ) . getRecipientForThreadId ( threadID ) ;
if ( threadRecipient ! = null & & ! threadRecipient . isGroupRecipient ( ) ) {
2020-02-11 09:38:05 +11:00
LokiFileServerAPI . shared . getAllDevicePublicKeys ( threadRecipient . getAddress ( ) . serialize ( ) ) . success ( devices - > {
2019-10-31 11:36:52 +11:00
// We should update our input if this thread is a part of the other threads device
if ( devices . contains ( recipient . getAddress ( ) . serialize ( ) ) ) {
this . updateInputPanel ( ) ;
}
return Unit . INSTANCE ;
} ) ;
2019-10-29 12:13:22 +11:00
}
2019-10-31 11:36:52 +11:00
return ;
2019-10-29 12:13:22 +11:00
}
2019-10-31 11:36:52 +11:00
this . updateInputPanel ( ) ;
2019-08-28 15:17:13 +10:00
}
2019-12-06 13:00:08 +11:00
@Override
public void handleSessionRestoreDevicesChanged ( long threadId ) {
if ( threadId = = this . threadId ) {
2019-12-06 16:33:17 +11:00
runOnUiThread ( this : : updateSessionRestoreBanner ) ;
2019-12-06 13:00:08 +11:00
}
}
2019-08-28 15:17:13 +10:00
private void updateInputPanel ( ) {
2019-11-13 12:28:17 +11:00
/ *
2019-11-13 16:05:37 +11:00
isFriendsWithAnyDevice caches whether we are friends with any of the other users device .
2019-11-13 12:28:17 +11:00
This stops the case where the input panel disables and enables rapidly .
- This can occur when we are not friends with the current thread BUT multi - device tells us that we are friends with another one of their devices .
* /
2019-11-13 16:05:37 +11:00
if ( recipient . isGroupRecipient ( ) | | isNoteToSelf ( ) | | isFriendsWithAnyDevice ) {
2019-10-31 11:36:52 +11:00
setInputPanelEnabled ( true ) ;
return ;
2019-07-19 13:15:17 +10:00
}
2019-10-31 11:36:52 +11:00
2019-11-01 12:15:40 +11:00
// It could take a while before our promise resolves, so we assume the best case
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory . getLokiThreadDatabase ( this ) . getFriendRequestStatus ( threadId ) ;
boolean isPending = friendRequestStatus = = LokiThreadFriendRequestStatus . REQUEST_SENDING | | friendRequestStatus = = LokiThreadFriendRequestStatus . REQUEST_SENT | | friendRequestStatus = = LokiThreadFriendRequestStatus . REQUEST_RECEIVED ;
setInputPanelEnabled ( ! isPending ) ;
2019-11-13 16:05:37 +11:00
// We should always have the input panel enabled if we are friends with the current user
isFriendsWithAnyDevice = friendRequestStatus = = LokiThreadFriendRequestStatus . FRIENDS ;
2019-11-13 12:28:17 +11:00
2019-11-13 16:05:37 +11:00
// Multi-device input logic
if ( ! isFriendsWithAnyDevice ) {
// We should enable the input if we don't have any pending friend requests OR we are friends with a linked device
MultiDeviceUtilities . hasPendingFriendRequestWithAnyLinkedDevice ( this , recipient ) . success ( hasPendingRequests - > {
if ( ! hasPendingRequests ) {
setInputPanelEnabled ( true ) ;
} else {
MultiDeviceUtilities . isFriendsWithAnyLinkedDevice ( this , recipient ) . success ( isFriends - > {
// If we are friend with any of the other devices then we want to make sure the input panel is always enabled for the duration of this conversation
isFriendsWithAnyDevice = isFriends ;
setInputPanelEnabled ( isFriends ) ;
return Unit . INSTANCE ;
} ) ;
}
2019-11-12 08:33:54 +11:00
return Unit . INSTANCE ;
} ) ;
}
2019-10-31 11:36:52 +11:00
}
private void setInputPanelEnabled ( boolean enabled ) {
Util . runOnMain ( ( ) - > {
updateToggleButtonState ( ) ;
2020-01-10 16:35:32 +11:00
String hint = enabled ? " Message " : " Pending message request " ;
inputPanel . setHint ( hint ) ;
2019-10-31 11:36:52 +11:00
inputPanel . setEnabled ( enabled ) ;
if ( enabled ) {
inputPanel . composeText . requestFocus ( ) ;
InputMethodManager inputMethodManager = ( InputMethodManager ) getSystemService ( INPUT_METHOD_SERVICE ) ;
inputMethodManager . showSoftInput ( inputPanel . composeText , 0 ) ;
}
} ) ;
2012-07-19 14:22:03 -07:00
}
2014-07-18 19:31:03 -07:00
private void sendMessage ( ) {
2019-03-28 15:04:38 -03:00
if ( inputPanel . isRecordingInLockedMode ( ) ) {
inputPanel . releaseRecordingLock ( ) ;
return ;
}
2012-07-19 14:22:03 -07:00
try {
2017-08-01 08:56:00 -07:00
Recipient recipient = getRecipient ( ) ;
2016-11-28 09:14:44 -08:00
2017-08-01 08:56:00 -07:00
if ( recipient = = null ) {
2016-11-28 09:14:44 -08:00
throw new RecipientFormattingException ( " Badly formatted " ) ;
}
2019-03-06 08:12:00 -08:00
String message = getMessage ( ) ;
TransportOption transport = sendButton . getSelectedTransport ( ) ;
2019-04-28 11:34:56 -07:00
boolean forceSms = ( recipient . isForceSmsSelection ( ) | | sendButton . isManualSelection ( ) ) & & transport . isSms ( ) ;
2019-03-06 08:12:00 -08:00
int subscriptionId = sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ;
long expiresIn = recipient . getExpireMessages ( ) * 1000L ;
boolean initiating = threadId = = - 1 ;
boolean needsSplit = ! transport . isSms ( ) & & message . length ( ) > transport . calculateCharacters ( message ) . maxPrimaryMessageSize ;
2019-09-17 11:46:47 +10:00
boolean isMediaMessage = attachmentManager . isAttachmentPresent ( ) | |
recipient . isGroupRecipient ( ) | |
recipient . getAddress ( ) . isEmail ( ) | |
inputPanel . getQuote ( ) . isPresent ( ) | |
linkPreviewViewModel . hasLinkPreview ( ) | |
2019-09-18 10:00:18 +10:00
LinkPreviewUtil . isWhitelistedMediaUrl ( message ) | | // Loki - Send GIFs as media messages
2019-03-06 08:12:00 -08:00
needsSplit ;
2015-03-11 14:23:45 -07:00
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " isManual Selection: " + sendButton . isManualSelection ( ) ) ;
Log . i ( TAG , " forceSms: " + forceSms ) ;
2012-08-01 17:39:36 -07:00
2017-09-11 13:04:20 -07:00
if ( ( recipient . isMmsGroupRecipient ( ) | | recipient . getAddress ( ) . isEmail ( ) ) & & ! isMmsEnabled ) {
2013-09-16 00:55:01 -07:00
handleManualMmsRequired ( ) ;
2017-06-06 18:03:09 -07:00
} else if ( ! forceSms & & identityRecords . isUnverified ( ) ) {
handleUnverifiedRecipients ( ) ;
2019-07-01 16:49:42 +10:00
} / * else if ( ! forceSms & & identityRecords . isUntrusted ( ) ) {
2017-06-06 18:03:09 -07:00
handleUntrustedRecipients ( ) ;
2019-07-01 16:49:42 +10:00
} * / else if ( isMediaMessage ) {
2017-08-16 21:49:41 -07:00
sendMediaMessage ( forceSms , expiresIn , subscriptionId , initiating ) ;
2012-07-19 14:22:03 -07:00
} else {
2017-08-16 21:49:41 -07:00
sendTextMessage ( forceSms , expiresIn , subscriptionId , initiating ) ;
2012-07-19 14:22:03 -07:00
}
} catch ( RecipientFormattingException ex ) {
2012-09-07 20:03:23 -07:00
Toast . makeText ( ConversationActivity . this ,
2012-09-19 19:56:04 -07:00
R . string . ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation ,
2012-09-07 20:03:23 -07:00
Toast . LENGTH_LONG ) . show ( ) ;
2014-02-17 20:25:40 -08:00
Log . w ( TAG , ex ) ;
2012-07-19 14:22:03 -07:00
} catch ( InvalidMessageException ex ) {
2019-08-28 15:17:13 +10:00
// Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_message_is_empty_exclamation,
// Toast.LENGTH_SHORT).show();
2014-02-17 20:25:40 -08:00
Log . w ( TAG , ex ) ;
2012-07-19 14:22:03 -07:00
}
}
2017-08-16 21:49:41 -07:00
private void sendMediaMessage ( final boolean forceSms , final long expiresIn , final int subscriptionId , boolean initiating )
2014-11-08 11:35:58 -08:00
throws InvalidMessageException
2014-06-11 15:31:59 -07:00
{
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Sending media message... " ) ;
2019-04-17 10:21:30 -04:00
sendMediaMessage ( forceSms , getMessage ( ) , attachmentManager . buildSlideDeck ( ) , inputPanel . getQuote ( ) . orNull ( ) , Collections . emptyList ( ) , linkPreviewViewModel . getActiveLinkPreviews ( ) , expiresIn , subscriptionId , initiating , true ) ;
2015-11-18 14:52:26 -08:00
}
2019-01-15 00:41:05 -08:00
private ListenableFuture < Void > sendMediaMessage ( final boolean forceSms ,
String body ,
SlideDeck slideDeck ,
2019-04-17 10:21:30 -04:00
QuoteModel quote ,
2019-01-15 00:41:05 -08:00
List < Contact > contacts ,
List < LinkPreview > previews ,
final long expiresIn ,
final int subscriptionId ,
2019-04-17 10:21:30 -04:00
final boolean initiating ,
final boolean clearComposeBox )
2019-01-15 00:41:05 -08:00
{
2018-12-12 18:15:09 -08:00
if ( ! isDefaultSms & & ( ! isSecureText | | forceSms ) ) {
showDefaultSmsPrompt ( ) ;
2018-11-20 09:59:23 -08:00
return new SettableFuture < > ( null ) ;
2018-12-12 18:15:09 -08:00
}
2019-02-26 19:29:52 -08:00
if ( isSecureText & & ! forceSms ) {
Pair < String , Optional < Slide > > splitMessage = getSplitMessage ( body , sendButton . getSelectedTransport ( ) . calculateCharacters ( body ) . maxPrimaryMessageSize ) ;
body = splitMessage . first ;
if ( splitMessage . second . isPresent ( ) ) {
slideDeck . addSlide ( splitMessage . second . get ( ) ) ;
}
}
2019-04-17 10:21:30 -04:00
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage ( recipient , slideDeck , body , System . currentTimeMillis ( ) , subscriptionId , expiresIn , distributionType , quote , contacts , previews ) ;
2014-06-11 15:31:59 -07:00
2019-08-07 15:24:05 +10:00
final SettableFuture < Void > future = new SettableFuture < > ( ) ;
2017-12-05 11:35:15 -08:00
final Context context = getApplicationContext ( ) ;
2014-11-08 11:35:58 -08:00
2017-12-05 11:35:15 -08:00
final OutgoingMediaMessage outgoingMessage ;
2017-08-16 21:49:41 -07:00
2017-12-05 11:35:15 -08:00
if ( isSecureText & & ! forceSms ) {
outgoingMessage = new OutgoingSecureMediaMessage ( outgoingMessageCandidate ) ;
2018-10-29 15:14:31 -07:00
ApplicationContext . getInstance ( context ) . getTypingStatusSender ( ) . onTypingStopped ( threadId ) ;
2017-12-05 11:35:15 -08:00
} else {
outgoingMessage = outgoingMessageCandidate ;
}
2014-11-08 11:35:58 -08:00
2019-09-18 17:33:34 +10:00
// Loki - Send a friend request if we're not yet friends with the user in question
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory . getLokiThreadDatabase ( context ) . getFriendRequestStatus ( threadId ) ;
2020-02-03 13:53:58 +11:00
outgoingMessage . isFriendRequest = ! isGroupConversation ( ) & & friendRequestStatus ! = LokiThreadFriendRequestStatus . FRIENDS ; // Needed for stageOutgoingMessage(...)
2019-09-18 17:33:34 +10:00
2017-12-05 11:35:15 -08:00
Permissions . with ( this )
2017-12-20 11:21:00 -08:00
. request ( Manifest . permission . SEND_SMS , Manifest . permission . READ_SMS )
2017-12-13 10:29:19 -08:00
. ifNecessary ( ! isSecureText | | forceSms )
2017-12-05 11:35:15 -08:00
. withPermanentDenialDialog ( getString ( R . string . ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms ) )
. onAllGranted ( ( ) - > {
2019-04-17 10:21:30 -04:00
if ( clearComposeBox ) {
inputPanel . clearQuote ( ) ;
attachmentManager . clear ( glideRequests , false ) ;
silentlySetComposeText ( " " ) ;
}
2017-12-05 11:35:15 -08:00
final long id = fragment . stageOutgoingMessage ( outgoingMessage ) ;
new AsyncTask < Void , Void , Long > ( ) {
@Override
protected Long doInBackground ( Void . . . param ) {
if ( initiating ) {
DatabaseFactory . getRecipientDatabase ( context ) . setProfileSharing ( recipient , true ) ;
}
2018-01-24 19:17:44 -08:00
return MessageSender . send ( context , outgoingMessage , threadId , forceSms , ( ) - > fragment . releaseOutgoingMessage ( id ) ) ;
2017-12-05 11:35:15 -08:00
}
@Override
protected void onPostExecute ( Long result ) {
sendComplete ( result ) ;
future . set ( null ) ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
} )
. onAnyDenied ( ( ) - > future . set ( null ) )
. execute ( ) ;
2015-11-18 14:52:26 -08:00
return future ;
2014-06-11 15:31:59 -07:00
}
2017-08-16 21:49:41 -07:00
private void sendTextMessage ( final boolean forceSms , final long expiresIn , final int subscriptionId , final boolean initiatingConversation )
2014-06-11 15:31:59 -07:00
throws InvalidMessageException
{
2018-12-12 18:15:09 -08:00
if ( ! isDefaultSms & & ( ! isSecureText | | forceSms ) ) {
showDefaultSmsPrompt ( ) ;
return ;
}
2017-12-05 11:35:15 -08:00
final Context context = getApplicationContext ( ) ;
final String messageBody = getMessage ( ) ;
2014-06-11 15:31:59 -07:00
OutgoingTextMessage message ;
2015-09-29 14:26:37 -07:00
if ( isSecureText & & ! forceSms ) {
2017-12-05 11:35:15 -08:00
message = new OutgoingEncryptedMessage ( recipient , messageBody , expiresIn ) ;
2018-10-29 15:14:31 -07:00
ApplicationContext . getInstance ( context ) . getTypingStatusSender ( ) . onTypingStopped ( threadId ) ;
2014-06-11 15:31:59 -07:00
} else {
2017-12-05 11:35:15 -08:00
message = new OutgoingTextMessage ( recipient , messageBody , expiresIn , subscriptionId ) ;
2014-06-11 15:31:59 -07:00
}
2019-07-01 16:49:42 +10:00
// Loki - Send a friend request if we're not yet friends with the user in question
2019-07-22 09:38:12 +10:00
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory . getLokiThreadDatabase ( context ) . getFriendRequestStatus ( threadId ) ;
2020-02-03 13:53:58 +11:00
message . isFriendRequest = ! isGroupConversation ( ) & & friendRequestStatus ! = LokiThreadFriendRequestStatus . FRIENDS ; // Needed for stageOutgoingMessage(...)
2019-06-17 11:57:40 +10:00
2017-12-05 11:35:15 -08:00
Permissions . with ( this )
. request ( Manifest . permission . SEND_SMS )
. ifNecessary ( forceSms | | ! isSecureText )
. withPermanentDenialDialog ( getString ( R . string . ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms ) )
. onAllGranted ( ( ) - > {
2018-10-29 15:14:31 -07:00
silentlySetComposeText ( " " ) ;
2017-12-05 11:35:15 -08:00
final long id = fragment . stageOutgoingMessage ( message ) ;
new AsyncTask < OutgoingTextMessage , Void , Long > ( ) {
@Override
protected Long doInBackground ( OutgoingTextMessage . . . messages ) {
if ( initiatingConversation ) {
DatabaseFactory . getRecipientDatabase ( context ) . setProfileSharing ( recipient , true ) ;
}
2018-01-24 19:17:44 -08:00
return MessageSender . send ( context , messages [ 0 ] , threadId , forceSms , ( ) - > fragment . releaseOutgoingMessage ( id ) ) ;
2017-12-05 11:35:15 -08:00
}
@Override
protected void onPostExecute ( Long result ) {
sendComplete ( result ) ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR , message ) ;
} )
. execute ( ) ;
2014-06-11 15:31:59 -07:00
}
2018-12-12 18:15:09 -08:00
private void showDefaultSmsPrompt ( ) {
new AlertDialog . Builder ( this )
. setMessage ( R . string . ConversationActivity_signal_cannot_sent_sms_mms_messages_because_it_is_not_your_default_sms_app )
. setNegativeButton ( R . string . ConversationActivity_no , ( dialog , which ) - > dialog . dismiss ( ) )
. setPositiveButton ( R . string . ConversationActivity_yes , ( dialog , which ) - > handleMakeDefaultSms ( ) )
. show ( ) ;
}
2015-05-18 10:26:32 -07:00
private void updateToggleButtonState ( ) {
2019-11-13 16:05:37 +11:00
// Don't allow attachments if we're not friends with any device
if ( ! isNoteToSelf ( ) & & ! recipient . isGroupRecipient ( ) & & ! isFriendsWithAnyDevice ) {
2019-10-18 16:12:49 +11:00
buttonToggle . display ( sendButton ) ;
quickAttachmentToggle . hide ( ) ;
inlineAttachmentToggle . hide ( ) ;
return ;
}
2019-03-28 15:04:38 -03:00
if ( inputPanel . isRecordingInLockedMode ( ) ) {
buttonToggle . display ( sendButton ) ;
quickAttachmentToggle . show ( ) ;
inlineAttachmentToggle . hide ( ) ;
return ;
}
2017-11-19 16:53:12 -08:00
if ( composeText . getText ( ) . length ( ) = = 0 & & ! attachmentManager . isAttachmentPresent ( ) ) {
2019-10-18 12:40:41 +11:00
buttonToggle . display ( attachButton ) ;
2015-06-08 11:07:46 -07:00
quickAttachmentToggle . show ( ) ;
2018-08-10 12:18:02 -04:00
inlineAttachmentToggle . hide ( ) ;
2015-05-18 10:26:32 -07:00
} else {
buttonToggle . display ( sendButton ) ;
2015-06-08 11:07:46 -07:00
quickAttachmentToggle . hide ( ) ;
2018-08-15 12:35:41 -07:00
2019-01-15 00:41:05 -08:00
if ( ! attachmentManager . isAttachmentPresent ( ) & & ! linkPreviewViewModel . hasLinkPreview ( ) ) {
2018-08-15 12:35:41 -07:00
inlineAttachmentToggle . show ( ) ;
} else {
inlineAttachmentToggle . hide ( ) ;
}
2015-05-18 10:26:32 -07:00
}
}
2014-06-11 15:31:59 -07:00
2019-01-15 00:41:05 -08:00
private void updateLinkPreviewState ( ) {
if ( TextSecurePreferences . isLinkPreviewsEnabled ( this ) & & ! sendButton . getSelectedTransport ( ) . isSms ( ) & & ! attachmentManager . isAttachmentPresent ( ) ) {
linkPreviewViewModel . onEnabled ( ) ;
2019-02-14 13:55:48 -08:00
linkPreviewViewModel . onTextChanged ( this , composeText . getTextTrimmed ( ) , composeText . getSelectionStart ( ) , composeText . getSelectionEnd ( ) ) ;
2019-01-15 00:41:05 -08:00
} else {
linkPreviewViewModel . onUserCancel ( ) ;
}
}
2019-04-12 16:22:38 -03:00
private void recordTransportPreference ( TransportOption transportOption ) {
2016-02-05 16:10:33 -08:00
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
2019-04-12 16:22:38 -03:00
RecipientDatabase recipientDatabase = DatabaseFactory . getRecipientDatabase ( ConversationActivity . this ) ;
recipientDatabase . setDefaultSubscriptionId ( recipient , transportOption . getSimSubscriptionId ( ) . or ( - 1 ) ) ;
2019-05-10 12:13:59 -03:00
if ( ! recipient . isPushGroupRecipient ( ) ) {
recipientDatabase . setForceSmsSelection ( recipient , recipient . getRegistered ( ) = = RegisteredState . REGISTERED & & transportOption . isSms ( ) ) ;
}
2019-04-12 16:22:38 -03:00
2016-02-05 16:10:33 -08:00
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2016-02-05 16:10:33 -08:00
}
2017-11-24 22:00:30 -08:00
@Override
public void onRecorderPermissionRequired ( ) {
Permissions . with ( this )
. request ( Manifest . permission . RECORD_AUDIO )
. ifNecessary ( )
. withRationaleDialog ( getString ( R . string . ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone ) , R . drawable . ic_mic_white_48dp )
. withPermanentDenialDialog ( getString ( R . string . ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages ) )
. execute ( ) ;
}
2015-11-18 14:52:26 -08:00
@Override
public void onRecorderStarted ( ) {
2017-12-05 11:52:03 -08:00
Vibrator vibrator = ServiceUtil . getVibrator ( this ) ;
2015-11-22 10:44:44 -08:00
vibrator . vibrate ( 20 ) ;
2017-12-05 11:52:03 -08:00
2017-02-16 21:28:06 +01:00
getWindow ( ) . addFlags ( WindowManager . LayoutParams . FLAG_KEEP_SCREEN_ON ) ;
2015-11-18 14:52:26 -08:00
2015-11-22 10:44:44 -08:00
audioRecorder . startRecording ( ) ;
2015-11-18 14:52:26 -08:00
}
2019-03-28 15:04:38 -03:00
@Override
public void onRecorderLocked ( ) {
updateToggleButtonState ( ) ;
}
2015-11-18 14:52:26 -08:00
@Override
public void onRecorderFinished ( ) {
2019-03-28 15:04:38 -03:00
updateToggleButtonState ( ) ;
2017-12-05 11:52:03 -08:00
Vibrator vibrator = ServiceUtil . getVibrator ( this ) ;
2015-11-18 14:52:26 -08:00
vibrator . vibrate ( 20 ) ;
2017-12-05 11:52:03 -08:00
2017-02-16 21:28:06 +01:00
getWindow ( ) . clearFlags ( WindowManager . LayoutParams . FLAG_KEEP_SCREEN_ON ) ;
2015-11-18 14:52:26 -08:00
ListenableFuture < Pair < Uri , Long > > future = audioRecorder . stopRecording ( ) ;
future . addListener ( new ListenableFuture . Listener < Pair < Uri , Long > > ( ) {
@Override
public void onSuccess ( final @NonNull Pair < Uri , Long > result ) {
2017-12-05 11:35:15 -08:00
boolean forceSms = sendButton . isManualSelection ( ) & & sendButton . getSelectedTransport ( ) . isSms ( ) ;
int subscriptionId = sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ;
2018-01-12 11:39:10 +00:00
long expiresIn = recipient . getExpireMessages ( ) * 1000L ;
2017-12-05 11:35:15 -08:00
boolean initiating = threadId = = - 1 ;
AudioSlide audioSlide = new AudioSlide ( ConversationActivity . this , result . first , result . second , MediaUtil . AUDIO_AAC , true ) ;
SlideDeck slideDeck = new SlideDeck ( ) ;
slideDeck . addSlide ( audioSlide ) ;
2019-04-17 10:21:30 -04:00
sendMediaMessage ( forceSms , " " , slideDeck , inputPanel . getQuote ( ) . orNull ( ) , Collections . emptyList ( ) , Collections . emptyList ( ) , expiresIn , subscriptionId , initiating , true ) . addListener ( new AssertedSuccessListener < Void > ( ) {
2017-12-05 11:35:15 -08:00
@Override
public void onSuccess ( Void nothing ) {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
2019-02-25 17:47:30 -08:00
BlobProvider . getInstance ( ) . delete ( ConversationActivity . this , result . first ) ;
2017-12-05 11:35:15 -08:00
return null ;
}
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
}
} ) ;
2015-11-18 14:52:26 -08:00
}
@Override
public void onFailure ( ExecutionException e ) {
Toast . makeText ( ConversationActivity . this , R . string . ConversationActivity_unable_to_record_audio , Toast . LENGTH_LONG ) . show ( ) ;
}
} ) ;
}
@Override
public void onRecorderCanceled ( ) {
2019-03-28 15:04:38 -03:00
updateToggleButtonState ( ) ;
2017-12-05 11:52:03 -08:00
Vibrator vibrator = ServiceUtil . getVibrator ( this ) ;
2015-11-18 14:52:26 -08:00
vibrator . vibrate ( 50 ) ;
2017-12-05 11:52:03 -08:00
2017-02-16 21:28:06 +01:00
getWindow ( ) . clearFlags ( WindowManager . LayoutParams . FLAG_KEEP_SCREEN_ON ) ;
2015-11-18 14:52:26 -08:00
ListenableFuture < Pair < Uri , Long > > future = audioRecorder . stopRecording ( ) ;
future . addListener ( new ListenableFuture . Listener < Pair < Uri , Long > > ( ) {
@Override
public void onSuccess ( final Pair < Uri , Long > result ) {
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
2019-02-25 17:47:30 -08:00
BlobProvider . getInstance ( ) . delete ( ConversationActivity . this , result . first ) ;
2015-11-18 14:52:26 -08:00
return null ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2015-11-18 14:52:26 -08:00
}
@Override
public void onFailure ( ExecutionException e ) { }
} ) ;
}
2015-11-22 10:44:53 -08:00
@Override
public void onEmojiToggle ( ) {
2017-01-19 11:31:41 -08:00
if ( ! emojiDrawerStub . resolved ( ) ) {
2019-04-17 10:21:30 -04:00
Boolean stickersAvailable = stickerViewModel . getStickersAvailability ( ) . getValue ( ) ;
initializeMediaKeyboardProviders ( emojiDrawerStub . get ( ) , stickersAvailable = = null ? false : stickersAvailable ) ;
inputPanel . setMediaKeyboard ( emojiDrawerStub . get ( ) ) ;
2017-01-19 11:31:41 -08:00
}
if ( container . getCurrentInput ( ) = = emojiDrawerStub . get ( ) ) {
container . showSoftkey ( composeText ) ;
} else {
container . show ( composeText , emojiDrawerStub . get ( ) ) ;
}
2015-11-22 10:44:53 -08:00
}
2019-01-15 00:41:05 -08:00
@Override
public void onLinkPreviewCanceled ( ) {
linkPreviewViewModel . onUserCancel ( ) ;
}
2019-04-17 10:21:30 -04:00
@Override
public void onStickerSuggestionSelected ( @NonNull StickerRecord sticker ) {
sendSticker ( sticker , true ) ;
}
2016-12-26 15:14:23 -08:00
@Override
public void onMediaSelected ( @NonNull Uri uri , String contentType ) {
if ( ! TextUtils . isEmpty ( contentType ) & & contentType . trim ( ) . equals ( " image/gif " ) ) {
setMedia ( uri , MediaType . GIF ) ;
2017-05-08 15:32:59 -07:00
} else if ( MediaUtil . isImageType ( contentType ) ) {
2016-12-26 15:14:23 -08:00
setMedia ( uri , MediaType . IMAGE ) ;
2017-05-08 15:32:59 -07:00
} else if ( MediaUtil . isVideoType ( contentType ) ) {
2016-12-26 15:14:23 -08:00
setMedia ( uri , MediaType . VIDEO ) ;
2017-05-08 15:32:59 -07:00
} else if ( MediaUtil . isAudioType ( contentType ) ) {
2016-12-26 15:14:23 -08:00
setMedia ( uri , MediaType . AUDIO ) ;
}
}
2019-02-14 13:55:48 -08:00
@Override
public void onCursorPositionChanged ( int start , int end ) {
linkPreviewViewModel . onTextChanged ( this , composeText . getTextTrimmed ( ) , start , end ) ;
}
2019-04-17 10:21:30 -04:00
@Override
public void onStickerSelected ( @NonNull StickerRecord stickerRecord ) {
sendSticker ( stickerRecord , false ) ;
}
@Override
public void onStickerManagementClicked ( ) {
startActivity ( StickerManagementActivity . getIntent ( this ) ) ;
container . hideAttachedInput ( true ) ;
}
private void sendSticker ( @NonNull StickerRecord stickerRecord , boolean clearCompose ) {
sendSticker ( new StickerLocator ( stickerRecord . getPackId ( ) , stickerRecord . getPackKey ( ) , stickerRecord . getStickerId ( ) ) , stickerRecord . getUri ( ) , stickerRecord . getSize ( ) , clearCompose ) ;
AsyncTask . THREAD_POOL_EXECUTOR . execute ( ( ) - > {
DatabaseFactory . getStickerDatabase ( this ) . updateStickerLastUsedTime ( stickerRecord . getRowId ( ) , System . currentTimeMillis ( ) ) ;
} ) ;
}
private void sendSticker ( @NonNull StickerLocator stickerLocator , @NonNull Uri uri , long size , boolean clearCompose ) {
if ( sendButton . getSelectedTransport ( ) . isSms ( ) ) {
Media media = new Media ( uri , MediaUtil . IMAGE_WEBP , System . currentTimeMillis ( ) , StickerSlide . WIDTH , StickerSlide . HEIGHT , size , Optional . absent ( ) , Optional . absent ( ) ) ;
Intent intent = MediaSendActivity . buildEditorIntent ( this , Collections . singletonList ( media ) , recipient , composeText . getTextTrimmed ( ) , sendButton . getSelectedTransport ( ) ) ;
startActivityForResult ( intent , MEDIA_SENDER ) ;
return ;
}
long expiresIn = recipient . getExpireMessages ( ) * 1000L ;
int subscriptionId = sendButton . getSelectedTransport ( ) . getSimSubscriptionId ( ) . or ( - 1 ) ;
boolean initiating = threadId = = - 1 ;
TransportOption transport = sendButton . getSelectedTransport ( ) ;
SlideDeck slideDeck = new SlideDeck ( ) ;
Slide stickerSlide = new StickerSlide ( this , uri , size , stickerLocator ) ;
slideDeck . addSlide ( stickerSlide ) ;
sendMediaMessage ( transport . isSms ( ) , " " , slideDeck , null , Collections . emptyList ( ) , Collections . emptyList ( ) , expiresIn , subscriptionId , initiating , clearCompose ) ;
}
2018-10-29 15:14:31 -07:00
private void silentlySetComposeText ( String text ) {
typingTextWatcher . setEnabled ( false ) ;
composeText . setText ( text ) ;
2019-10-11 16:37:28 +11:00
if ( text . isEmpty ( ) ) resetMentions ( ) ;
2018-10-29 15:14:31 -07:00
typingTextWatcher . setEnabled ( true ) ;
}
2016-12-26 15:14:23 -08:00
2012-07-19 14:22:03 -07:00
// Listeners
2015-10-28 09:47:09 -07:00
private class AttachmentTypeListener implements AttachmentTypeSelector . AttachmentClickedListener {
2013-02-02 20:37:40 -08:00
@Override
2015-10-28 09:47:09 -07:00
public void onClick ( int type ) {
addAttachment ( type ) ;
2012-07-19 14:22:03 -07:00
}
2016-11-25 22:37:23 -08:00
@Override
2019-03-14 17:01:23 -07:00
public void onQuickAttachment ( Uri uri , String mimeType , String bucketId , long dateTaken , int width , int height , long size ) {
2019-01-15 00:41:05 -08:00
linkPreviewViewModel . onUserCancel ( ) ;
2019-03-14 17:01:23 -07:00
Media media = new Media ( uri , mimeType , dateTaken , width , height , size , Optional . of ( Media . ALL_MEDIA_BUCKET_ID ) , Optional . absent ( ) ) ;
2019-03-13 16:05:25 -07:00
startActivityForResult ( MediaSendActivity . buildEditorIntent ( ConversationActivity . this , Collections . singletonList ( media ) , recipient , composeText . getTextTrimmed ( ) , sendButton . getSelectedTransport ( ) ) , MEDIA_SENDER ) ;
2016-11-25 22:37:23 -08:00
}
2012-07-19 14:22:03 -07:00
}
2015-11-18 14:52:26 -08:00
private class QuickCameraToggleListener implements OnClickListener {
2015-04-16 01:38:33 -04:00
@Override
public void onClick ( View v ) {
2018-09-20 13:27:18 -07:00
Permissions . with ( ConversationActivity . this )
. request ( Manifest . permission . CAMERA )
. ifNecessary ( )
. withRationaleDialog ( getString ( R . string . ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera ) , R . drawable . ic_photo_camera_white_48dp )
. withPermanentDenialDialog ( getString ( R . string . ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video ) )
. onAllGranted ( ( ) - > {
composeText . clearFocus ( ) ;
2019-03-13 16:05:25 -07:00
startActivityForResult ( MediaSendActivity . buildCameraIntent ( ConversationActivity . this , recipient , sendButton . getSelectedTransport ( ) ) , MEDIA_SENDER ) ;
2018-09-20 13:27:18 -07:00
overridePendingTransition ( R . anim . camera_slide_from_bottom , R . anim . stationary ) ;
} )
. onAnyDenied ( ( ) - > Toast . makeText ( ConversationActivity . this , R . string . ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video , Toast . LENGTH_LONG ) . show ( ) )
. execute ( ) ;
2015-04-16 01:38:33 -04:00
}
}
2012-07-19 14:22:03 -07:00
private class SendButtonListener implements OnClickListener , TextView . OnEditorActionListener {
2013-02-02 20:37:40 -08:00
@Override
2012-07-19 14:22:03 -07:00
public void onClick ( View v ) {
2014-07-18 19:31:03 -07:00
sendMessage ( ) ;
2012-07-19 14:22:03 -07:00
}
2013-02-02 20:37:40 -08:00
@Override
2012-07-19 14:22:03 -07:00
public boolean onEditorAction ( TextView v , int actionId , KeyEvent event ) {
if ( actionId = = EditorInfo . IME_ACTION_SEND ) {
sendButton . performClick ( ) ;
return true ;
}
return false ;
}
2013-04-25 18:59:49 -07:00
}
2012-07-19 14:22:03 -07:00
2015-05-18 10:26:32 -07:00
private class AttachButtonListener implements OnClickListener {
@Override
public void onClick ( View v ) {
handleAddAttachment ( ) ;
}
}
2015-07-26 22:49:43 +01:00
private class AttachButtonLongClickListener implements View . OnLongClickListener {
@Override
public boolean onLongClick ( View v ) {
return sendButton . performLongClick ( ) ;
}
}
2014-05-28 20:53:34 -07:00
private class ComposeKeyPressedListener implements OnKeyListener , OnClickListener , TextWatcher , OnFocusChangeListener {
2015-05-18 10:26:32 -07:00
int beforeLength ;
2013-06-27 20:57:27 -07:00
@Override
public boolean onKey ( View v , int keyCode , KeyEvent event ) {
if ( event . getAction ( ) = = KeyEvent . ACTION_DOWN ) {
if ( keyCode = = KeyEvent . KEYCODE_ENTER ) {
2013-07-09 18:26:18 -07:00
if ( TextSecurePreferences . isEnterSendsEnabled ( ConversationActivity . this ) ) {
2013-06-27 20:57:27 -07:00
sendButton . dispatchKeyEvent ( new KeyEvent ( KeyEvent . ACTION_DOWN , KeyEvent . KEYCODE_ENTER ) ) ;
sendButton . dispatchKeyEvent ( new KeyEvent ( KeyEvent . ACTION_UP , KeyEvent . KEYCODE_ENTER ) ) ;
return true ;
}
}
}
return false ;
}
@Override
public void onClick ( View v ) {
2015-07-24 13:22:28 -07:00
container . showSoftkey ( composeText ) ;
2013-06-27 20:57:27 -07:00
}
2015-05-18 10:26:32 -07:00
@Override
public void beforeTextChanged ( CharSequence s , int start , int count , int after ) {
2017-07-04 19:09:36 +02:00
beforeLength = composeText . getTextTrimmed ( ) . length ( ) ;
2015-05-18 10:26:32 -07:00
}
2013-02-02 20:37:40 -08:00
@Override
2012-07-19 14:22:03 -07:00
public void afterTextChanged ( Editable s ) {
calculateCharactersRemaining ( ) ;
2015-05-18 10:26:32 -07:00
2019-02-14 13:55:48 -08:00
if ( composeText . getTextTrimmed ( ) . length ( ) = = 0 | | beforeLength = = 0 ) {
2017-12-05 11:52:03 -08:00
composeText . postDelayed ( ConversationActivity . this : : updateToggleButtonState , 50 ) ;
2015-05-18 10:26:32 -07:00
}
2019-04-17 10:21:30 -04:00
stickerViewModel . onInputTextUpdated ( s . toString ( ) ) ;
2012-07-19 14:22:03 -07:00
}
2015-05-18 10:26:32 -07:00
2013-02-02 20:37:40 -08:00
@Override
2012-07-19 14:22:03 -07:00
public void onTextChanged ( CharSequence s , int start , int before , int count ) { }
2014-05-28 20:53:34 -07:00
@Override
2015-07-24 13:22:28 -07:00
public void onFocusChange ( View v , boolean hasFocus ) { }
2012-07-19 14:22:03 -07:00
}
2012-08-03 17:34:09 -07:00
2018-10-29 15:14:31 -07:00
private class TypingStatusTextWatcher extends SimpleTextWatcher {
private boolean enabled = true ;
@Override
public void onTextChanged ( String text ) {
if ( enabled & & threadId > 0 & & isSecureText & & ! isSmsForced ( ) ) {
ApplicationContext . getInstance ( ConversationActivity . this ) . getTypingStatusSender ( ) . onTypingStarted ( threadId ) ;
}
}
public void setEnabled ( boolean enabled ) {
this . enabled = enabled ;
}
}
2019-10-10 14:44:08 +11:00
private class MentionTextWatcher extends SimpleTextWatcher {
@Override
public void onTextChanged ( String text ) {
2019-10-11 11:13:34 +11:00
boolean isBackspace = text . length ( ) < oldText . length ( ) ;
if ( isBackspace ) {
currentMentionStartIndex = - 1 ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . hide ( ) ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionViewContainer . setVisibility ( View . GONE ) ;
2019-10-15 16:19:00 +11:00
ArrayList < Mention > mentionsToRemove = new ArrayList < > ( ) ;
for ( Mention mention : mentions ) {
if ( ! text . contains ( mention . getDisplayName ( ) ) ) {
mentionsToRemove . add ( mention ) ;
2019-10-11 11:13:34 +11:00
}
}
2019-10-15 16:19:00 +11:00
mentions . removeAll ( mentionsToRemove ) ;
2019-10-15 16:07:34 +11:00
}
if ( text . length ( ) > 0 ) {
2019-10-11 11:42:01 +11:00
if ( currentMentionStartIndex > text . length ( ) ) {
2019-10-11 16:37:28 +11:00
resetMentions ( ) ; // Should never occur
2019-10-11 11:42:01 +11:00
}
2019-10-11 16:37:28 +11:00
int lastCharacterIndex = text . length ( ) - 1 ;
char lastCharacter = text . charAt ( lastCharacterIndex ) ;
2019-11-20 09:17:31 +11:00
char secondToLastCharacter = ' ' ;
2019-11-18 17:10:28 +11:00
if ( lastCharacterIndex > 0 ) {
2019-11-20 09:17:31 +11:00
secondToLastCharacter = text . charAt ( lastCharacterIndex - 1 ) ;
2019-11-18 17:10:28 +11:00
}
2019-10-15 13:51:18 +11:00
String userHexEncodedPublicKey = TextSecurePreferences . getLocalNumber ( ConversationActivity . this ) ;
LokiThreadDatabase threadDatabase = DatabaseFactory . getLokiThreadDatabase ( ConversationActivity . this ) ;
2019-10-11 11:13:34 +11:00
LokiUserDatabase userDatabase = DatabaseFactory . getLokiUserDatabase ( ConversationActivity . this ) ;
2019-11-20 09:17:31 +11:00
if ( lastCharacter = = '@' & & Character . isWhitespace ( secondToLastCharacter ) ) {
2019-10-15 13:51:18 +11:00
List < Mention > mentionCandidates = LokiAPI . Companion . getMentionCandidates ( " " , threadId , userHexEncodedPublicKey , threadDatabase , userDatabase ) ;
2019-10-11 16:37:28 +11:00
currentMentionStartIndex = lastCharacterIndex ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionViewContainer . setVisibility ( View . VISIBLE ) ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . show ( mentionCandidates , threadId ) ;
2019-10-11 11:13:34 +11:00
} else if ( Character . isWhitespace ( lastCharacter ) ) {
currentMentionStartIndex = - 1 ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . hide ( ) ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionViewContainer . setVisibility ( View . GONE ) ;
2019-10-11 11:13:34 +11:00
} else {
if ( currentMentionStartIndex ! = - 1 ) {
String query = text . substring ( currentMentionStartIndex + 1 ) ; // + 1 to get rid of the @
2019-10-15 13:51:18 +11:00
List < Mention > mentionCandidates = LokiAPI . Companion . getMentionCandidates ( query , threadId , userHexEncodedPublicKey , threadDatabase , userDatabase ) ;
2020-01-16 15:15:08 +11:00
mentionCandidateSelectionViewContainer . setVisibility ( View . VISIBLE ) ;
2019-10-11 16:37:28 +11:00
mentionCandidateSelectionView . show ( mentionCandidates , threadId ) ;
2019-10-11 11:13:34 +11:00
}
2019-10-10 14:44:08 +11:00
}
}
2019-10-11 16:37:28 +11:00
ConversationActivity . this . oldText = text ;
2019-10-10 14:44:08 +11:00
}
2019-10-11 11:13:34 +11:00
}
2019-10-10 14:44:08 +11:00
2019-10-11 16:37:28 +11:00
private void resetMentions ( ) {
2019-10-11 11:13:34 +11:00
oldText = " " ;
currentMentionStartIndex = - 1 ;
mentions . clear ( ) ;
2019-10-10 14:44:08 +11:00
}
2015-03-31 13:36:04 -07:00
@Override
public void setThreadId ( long threadId ) {
this . threadId = threadId ;
}
2018-02-07 14:01:37 -08:00
@Override
public void handleReplyMessage ( MessageRecord messageRecord ) {
Recipient author ;
if ( messageRecord . isOutgoing ( ) ) {
author = Recipient . from ( this , Address . fromSerialized ( TextSecurePreferences . getLocalNumber ( this ) ) , true ) ;
} else {
author = messageRecord . getIndividualRecipient ( ) ;
}
2018-04-26 17:03:54 -07:00
if ( messageRecord . isMms ( ) & & ! ( ( MmsMessageRecord ) messageRecord ) . getSharedContacts ( ) . isEmpty ( ) ) {
Contact contact = ( ( MmsMessageRecord ) messageRecord ) . getSharedContacts ( ) . get ( 0 ) ;
String displayName = ContactUtil . getDisplayName ( contact ) ;
String body = getString ( R . string . ConversationActivity_quoted_contact_message , EmojiStrings . BUST_IN_SILHOUETTE , displayName ) ;
SlideDeck slideDeck = new SlideDeck ( ) ;
if ( contact . getAvatarAttachment ( ) ! = null ) {
slideDeck . addSlide ( MediaUtil . getSlideForAttachment ( this , contact . getAvatarAttachment ( ) ) ) ;
}
inputPanel . setQuote ( GlideApp . with ( this ) ,
2019-10-10 14:44:08 +11:00
messageRecord . getDateSent ( ) ,
author ,
body ,
slideDeck ,
2020-01-15 12:20:10 +11:00
recipient ,
threadId ) ;
2019-01-15 00:41:05 -08:00
} else if ( messageRecord . isMms ( ) & & ! ( ( MmsMessageRecord ) messageRecord ) . getLinkPreviews ( ) . isEmpty ( ) ) {
LinkPreview linkPreview = ( ( MmsMessageRecord ) messageRecord ) . getLinkPreviews ( ) . get ( 0 ) ;
SlideDeck slideDeck = new SlideDeck ( ) ;
if ( linkPreview . getThumbnail ( ) . isPresent ( ) ) {
slideDeck . addSlide ( MediaUtil . getSlideForAttachment ( this , linkPreview . getThumbnail ( ) . get ( ) ) ) ;
}
inputPanel . setQuote ( GlideApp . with ( this ) ,
2019-10-10 14:44:08 +11:00
messageRecord . getDateSent ( ) ,
author ,
messageRecord . getBody ( ) ,
slideDeck ,
2020-01-15 12:20:10 +11:00
recipient ,
threadId ) ;
2018-04-26 17:03:54 -07:00
} else {
inputPanel . setQuote ( GlideApp . with ( this ) ,
2019-10-10 14:44:08 +11:00
messageRecord . getDateSent ( ) ,
author ,
messageRecord . getBody ( ) ,
messageRecord . isMms ( ) ? ( ( MmsMessageRecord ) messageRecord ) . getSlideDeck ( ) : new SlideDeck ( ) ,
2020-01-15 12:20:10 +11:00
recipient ,
threadId ) ;
2018-04-26 17:03:54 -07:00
}
2018-02-07 14:01:37 -08:00
}
2019-02-01 09:06:59 -08:00
@Override
public void onMessageActionToolbarOpened ( ) {
2020-01-15 17:13:46 +11:00
searchViewItem . collapseActionView ( ) ;
2019-02-01 09:06:59 -08:00
}
2019-04-09 09:35:47 -04:00
@Override
public void onForwardClicked ( ) {
inputPanel . clearQuote ( ) ;
}
2014-04-15 12:43:14 +02:00
@Override
public void onAttachmentChanged ( ) {
2017-03-14 13:24:24 -07:00
handleSecurityChange ( isSecureText , isDefaultSms ) ;
2015-05-18 10:26:32 -07:00
updateToggleButtonState ( ) ;
2019-01-15 00:41:05 -08:00
updateLinkPreviewState ( ) ;
2014-04-15 12:43:14 +02:00
}
2015-09-04 17:33:22 -07:00
2017-06-06 18:03:09 -07:00
private class UnverifiedDismissedListener implements UnverifiedBannerView . DismissListener {
@Override
public void onDismissed ( final List < IdentityRecord > unverifiedIdentities ) {
final IdentityDatabase identityDatabase = DatabaseFactory . getIdentityDatabase ( ConversationActivity . this ) ;
new AsyncTask < Void , Void , Void > ( ) {
@Override
protected Void doInBackground ( Void . . . params ) {
synchronized ( SESSION_LOCK ) {
for ( IdentityRecord identityRecord : unverifiedIdentities ) {
2017-07-26 09:59:15 -07:00
identityDatabase . setVerified ( identityRecord . getAddress ( ) ,
2017-06-06 18:03:09 -07:00
identityRecord . getIdentityKey ( ) ,
VerifiedStatus . DEFAULT ) ;
}
}
return null ;
}
@Override
protected void onPostExecute ( Void result ) {
initializeIdentityRecords ( ) ;
}
2017-10-23 13:03:32 -07:00
} . executeOnExecutor ( AsyncTask . THREAD_POOL_EXECUTOR ) ;
2017-06-06 18:03:09 -07:00
}
}
private class UnverifiedClickedListener implements UnverifiedBannerView . ClickListener {
@Override
public void onClicked ( final List < IdentityRecord > unverifiedIdentities ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " onClicked: " + unverifiedIdentities . size ( ) ) ;
2017-06-06 18:03:09 -07:00
if ( unverifiedIdentities . size ( ) = = 1 ) {
Intent intent = new Intent ( ConversationActivity . this , VerifyIdentityActivity . class ) ;
2017-07-26 09:59:15 -07:00
intent . putExtra ( VerifyIdentityActivity . ADDRESS_EXTRA , unverifiedIdentities . get ( 0 ) . getAddress ( ) ) ;
2017-06-06 18:03:09 -07:00
intent . putExtra ( VerifyIdentityActivity . IDENTITY_EXTRA , new IdentityKeyParcelable ( unverifiedIdentities . get ( 0 ) . getIdentityKey ( ) ) ) ;
intent . putExtra ( VerifyIdentityActivity . VERIFIED_EXTRA , false ) ;
startActivity ( intent ) ;
} else {
String [ ] unverifiedNames = new String [ unverifiedIdentities . size ( ) ] ;
for ( int i = 0 ; i < unverifiedIdentities . size ( ) ; i + + ) {
2017-08-21 18:32:38 -07:00
unverifiedNames [ i ] = Recipient . from ( ConversationActivity . this , unverifiedIdentities . get ( i ) . getAddress ( ) , false ) . toShortString ( ) ;
2017-06-06 18:03:09 -07:00
}
AlertDialog . Builder builder = new AlertDialog . Builder ( ConversationActivity . this ) ;
builder . setIconAttribute ( R . attr . dialog_alert_icon ) ;
builder . setTitle ( " No longer verified " ) ;
2017-12-05 11:52:03 -08:00
builder . setItems ( unverifiedNames , ( dialog , which ) - > {
Intent intent = new Intent ( ConversationActivity . this , VerifyIdentityActivity . class ) ;
intent . putExtra ( VerifyIdentityActivity . ADDRESS_EXTRA , unverifiedIdentities . get ( which ) . getAddress ( ) ) ;
intent . putExtra ( VerifyIdentityActivity . IDENTITY_EXTRA , new IdentityKeyParcelable ( unverifiedIdentities . get ( which ) . getIdentityKey ( ) ) ) ;
intent . putExtra ( VerifyIdentityActivity . VERIFIED_EXTRA , false ) ;
2017-06-06 18:03:09 -07:00
2017-12-05 11:52:03 -08:00
startActivity ( intent ) ;
2017-06-06 18:03:09 -07:00
} ) ;
builder . show ( ) ;
}
}
}
2018-04-24 11:09:54 -07:00
private class QuoteRestorationTask extends AsyncTask < Void , Void , MessageRecord > {
2018-07-25 11:30:48 -04:00
private final String serialized ;
private final SettableFuture < Boolean > future ;
2018-04-24 11:09:54 -07:00
2018-07-25 11:30:48 -04:00
QuoteRestorationTask ( @NonNull String serialized , @NonNull SettableFuture < Boolean > future ) {
2018-04-24 11:09:54 -07:00
this . serialized = serialized ;
2018-07-25 11:30:48 -04:00
this . future = future ;
2018-04-24 11:09:54 -07:00
}
@Override
protected MessageRecord doInBackground ( Void . . . voids ) {
QuoteId quoteId = QuoteId . deserialize ( serialized ) ;
if ( quoteId ! = null ) {
return DatabaseFactory . getMmsSmsDatabase ( getApplicationContext ( ) ) . getMessageFor ( quoteId . getId ( ) , quoteId . getAuthor ( ) ) ;
}
return null ;
}
@Override
protected void onPostExecute ( MessageRecord messageRecord ) {
if ( messageRecord ! = null ) {
handleReplyMessage ( messageRecord ) ;
2018-07-25 11:30:48 -04:00
future . set ( true ) ;
2018-04-24 11:09:54 -07:00
} else {
Log . e ( TAG , " Failed to restore a quote from a draft. No matching message record. " ) ;
2018-07-25 11:30:48 -04:00
future . set ( false ) ;
2018-04-24 11:09:54 -07:00
}
}
}
2019-06-28 12:32:23 +10:00
// region Loki
2020-01-10 11:35:16 +11:00
private void updateTitleTextView ( GlideRequests glide , Recipient recipient ) {
2020-01-17 10:37:06 +11:00
String userHexEncodedPublicKey = TextSecurePreferences . getLocalNumber ( this ) ;
List < PairingAuthorisation > deviceLinks = DatabaseFactory . getLokiAPIDatabase ( this ) . getPairingAuthorisations ( userHexEncodedPublicKey ) ;
HashSet < String > userLinkedDeviceHexEncodedPublicKeys = new HashSet < > ( ) ;
for ( PairingAuthorisation deviceLink : deviceLinks ) {
userLinkedDeviceHexEncodedPublicKeys . add ( deviceLink . getPrimaryDevicePublicKey ( ) . toLowerCase ( ) ) ;
userLinkedDeviceHexEncodedPublicKeys . add ( deviceLink . getSecondaryDevicePublicKey ( ) . toLowerCase ( ) ) ;
}
userLinkedDeviceHexEncodedPublicKeys . add ( userHexEncodedPublicKey . toLowerCase ( ) ) ;
if ( recipient = = null ) {
titleTextView . setText ( " Compose " ) ;
} else if ( userLinkedDeviceHexEncodedPublicKeys . contains ( recipient . getAddress ( ) . toString ( ) . toLowerCase ( ) ) ) {
titleTextView . setText ( " Note to Self " ) ;
} else {
2020-01-17 15:00:36 +11:00
titleTextView . setText ( ( recipient . getName ( ) = = null | | recipient . getName ( ) . isEmpty ( ) ) ? recipient . getAddress ( ) . toString ( ) : recipient . getName ( ) ) ;
2020-01-17 10:37:06 +11:00
}
2020-01-10 11:35:16 +11:00
}
2020-01-16 14:35:51 +11:00
private void updateSubtitleTextView ( ) {
2020-01-17 10:37:06 +11:00
muteIndicatorImageView . setVisibility ( View . GONE ) ;
actionBarSubtitleTextView . setVisibility ( View . VISIBLE ) ;
2020-01-16 14:35:51 +11:00
if ( messageStatus ! = null ) {
switch ( messageStatus ) {
case " calculatingPoW " : actionBarSubtitleTextView . setText ( " Encrypting message " ) ; break ;
case " contactingNetwork " : actionBarSubtitleTextView . setText ( " Tracing a path " ) ; break ;
case " sendingMessage " : actionBarSubtitleTextView . setText ( " Sending message " ) ; break ;
case " messageSent " : actionBarSubtitleTextView . setText ( " Message sent securely " ) ; break ;
case " messageFailed " : actionBarSubtitleTextView . setText ( " Message failed to send " ) ; break ;
}
2020-01-17 10:37:06 +11:00
} else if ( recipient . isMuted ( ) ) {
muteIndicatorImageView . setVisibility ( View . VISIBLE ) ;
actionBarSubtitleTextView . setText ( " Muted until " + DateUtils . getFormattedDateTime ( recipient . mutedUntil , " EEE, MMM d, yyyy HH:mm " , Locale . getDefault ( ) ) ) ;
2020-02-04 13:07:54 +11:00
} else if ( recipient . isGroupRecipient ( ) & & recipient . getName ( ) ! = null & & ! recipient . getName ( ) . equals ( " Session Updates " ) & & ! recipient . getName ( ) . equals ( " Loki News " ) ) {
2020-01-22 10:46:04 +11:00
LokiPublicChat publicChat = DatabaseFactory . getLokiThreadDatabase ( this ) . getPublicChat ( threadId ) ;
if ( publicChat ! = null ) {
Integer userCount = DatabaseFactory . getLokiAPIDatabase ( this ) . getUserCount ( publicChat . getChannel ( ) , publicChat . getServer ( ) ) ;
if ( userCount = = null ) { userCount = 0 ; }
2020-01-24 10:00:20 +11:00
if ( userCount > = 200 ) {
actionBarSubtitleTextView . setText ( " 200+ members " ) ;
2020-01-22 10:46:04 +11:00
} else {
actionBarSubtitleTextView . setText ( userCount + " members " ) ;
}
2020-01-22 15:28:39 +11:00
} else if ( PublicKeyValidation . isValid ( recipient . getAddress ( ) . toString ( ) ) ) {
actionBarSubtitleTextView . setText ( recipient . getAddress ( ) . toString ( ) ) ;
2020-01-22 10:46:04 +11:00
} else {
actionBarSubtitleTextView . setVisibility ( View . GONE ) ;
}
2020-01-22 15:28:39 +11:00
} else if ( PublicKeyValidation . isValid ( recipient . getAddress ( ) . toString ( ) ) ) {
actionBarSubtitleTextView . setText ( recipient . getAddress ( ) . toString ( ) ) ;
2020-01-17 10:37:06 +11:00
} else {
actionBarSubtitleTextView . setVisibility ( View . GONE ) ;
2020-01-16 14:35:51 +11:00
}
2020-01-22 10:46:04 +11:00
titleTextView . setTextSize ( TypedValue . COMPLEX_UNIT_PX , getResources ( ) . getDimension ( ( actionBarSubtitleTextView . getVisibility ( ) = = View . GONE ) ? R . dimen . very_large_font_size : R . dimen . large_font_size ) ) ;
2020-01-16 14:35:51 +11:00
}
private void setMessageStatusProgressAnimatedIfPossible ( int progress ) {
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
messageStatusProgressBar . setProgress ( progress , true ) ;
} else {
messageStatusProgressBar . setProgress ( progress ) ;
}
}
private void updateMessageStatusProgressBar ( ) {
if ( messageStatus ! = null ) {
messageStatusProgressBar . setAlpha ( 1 . 0f ) ;
switch ( messageStatus ) {
case " calculatingPoW " : setMessageStatusProgressAnimatedIfPossible ( 25 ) ; break ;
case " contactingNetwork " : setMessageStatusProgressAnimatedIfPossible ( 50 ) ; break ;
case " sendingMessage " : setMessageStatusProgressAnimatedIfPossible ( 75 ) ; break ;
case " messageSent " :
setMessageStatusProgressAnimatedIfPossible ( 100 ) ;
new Handler ( ) . postDelayed ( ( ) - > messageStatusProgressBar . animate ( ) . alpha ( 0 ) . setDuration ( 250 ) . start ( ) , 250 ) ;
new Handler ( ) . postDelayed ( ( ) - > messageStatusProgressBar . setProgress ( 0 ) , 500 ) ;
break ;
case " messageFailed " :
messageStatusProgressBar . animate ( ) . alpha ( 0 ) . setDuration ( 250 ) . start ( ) ;
new Handler ( ) . postDelayed ( ( ) - > messageStatusProgressBar . setProgress ( 0 ) , 250 ) ;
break ;
}
}
}
private void handleMessageStatusChanged ( String newMessageStatus , long timestamp ) {
if ( timestamp = = 0 ) { return ; }
updateForNewMessageStatusIfNeeded ( newMessageStatus , timestamp ) ;
if ( newMessageStatus . equals ( " messageFailed " ) | | newMessageStatus . equals ( " messageSent " ) ) {
new Handler ( ) . postDelayed ( ( ) - > clearMessageStatusIfNeeded ( timestamp ) , 1000 ) ;
}
}
private int precedence ( String messageStatus ) {
if ( messageStatus ! = null ) {
switch ( messageStatus ) {
case " calculatingPoW " : return 0 ;
case " contactingNetwork " : return 1 ;
case " sendingMessage " : return 2 ;
case " messageSent " : return 3 ;
case " messageFailed " : return 4 ;
default : return - 1 ;
}
} else {
return - 1 ;
}
}
private void updateForNewMessageStatusIfNeeded ( String newMessageStatus , long timestamp ) {
if ( ! DatabaseFactory . getSmsDatabase ( this ) . isOutgoingMessage ( timestamp ) & & ! DatabaseFactory . getMmsDatabase ( this ) . isOutgoingMessage ( timestamp ) ) { return ; }
if ( precedence ( newMessageStatus ) > precedence ( messageStatus ) ) {
messageStatus = newMessageStatus ;
updateSubtitleTextView ( ) ;
updateMessageStatusProgressBar ( ) ;
}
}
private void clearMessageStatusIfNeeded ( long timestamp ) {
if ( ! DatabaseFactory . getSmsDatabase ( this ) . isOutgoingMessage ( timestamp ) & & ! DatabaseFactory . getMmsDatabase ( this ) . isOutgoingMessage ( timestamp ) ) { return ; }
messageStatus = null ;
updateSubtitleTextView ( ) ;
updateMessageStatusProgressBar ( ) ;
}
2019-06-28 12:32:23 +10:00
@Override
public void acceptFriendRequest ( @NotNull MessageRecord friendRequest ) {
2019-10-08 13:23:03 +11:00
// Send the accept to the original friend request thread id
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory . getLokiMessageDatabase ( this ) ;
long originalThreadID = lokiMessageDatabase . getOriginalThreadID ( friendRequest . id ) ;
long threadId = originalThreadID < 0 ? this . threadId : originalThreadID ;
2019-11-27 15:25:53 +11:00
Recipient contact = DatabaseFactory . getThreadDatabase ( this ) . getRecipientForThreadId ( threadId ) ;
Address address = contact . getAddress ( ) ;
String contactPubKey = address . serialize ( ) ;
2019-11-14 12:00:46 +11:00
DatabaseFactory . getLokiThreadDatabase ( this ) . setFriendRequestStatus ( threadId , LokiThreadFriendRequestStatus . FRIENDS ) ;
lokiMessageDatabase . setFriendRequestStatus ( friendRequest . id , LokiMessageFriendRequestStatus . REQUEST_ACCEPTED ) ;
2019-11-27 15:25:53 +11:00
DatabaseFactory . getRecipientDatabase ( this ) . setProfileSharing ( contact , true ) ;
2019-11-14 12:00:46 +11:00
MessageSender . sendBackgroundMessageToAllDevices ( this , contactPubKey ) ;
2019-11-27 15:25:53 +11:00
MessageSender . syncContact ( this , address ) ;
2019-11-14 12:00:46 +11:00
updateInputPanel ( ) ;
2019-06-28 12:32:23 +10:00
}
@Override
public void rejectFriendRequest ( @NotNull MessageRecord friendRequest ) {
2019-10-08 13:23:03 +11:00
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory . getLokiMessageDatabase ( this ) ;
long originalThreadID = lokiMessageDatabase . getOriginalThreadID ( friendRequest . id ) ;
long threadId = originalThreadID < 0 ? this . threadId : originalThreadID ;
DatabaseFactory . getLokiThreadDatabase ( this ) . setFriendRequestStatus ( threadId , LokiThreadFriendRequestStatus . NONE ) ;
String contactID = DatabaseFactory . getThreadDatabase ( this ) . getRecipientForThreadId ( threadId ) . getAddress ( ) . toString ( ) ;
2019-06-28 12:32:23 +10:00
DatabaseFactory . getLokiPreKeyBundleDatabase ( this ) . removePreKeyBundle ( contactID ) ;
2019-10-24 12:17:58 +11:00
updateInputPanel ( ) ;
2019-06-28 12:32:23 +10:00
}
2019-10-23 12:40:25 +11:00
2019-10-31 11:36:52 +11:00
public boolean isNoteToSelf ( ) {
2019-11-14 12:00:46 +11:00
return TextSecurePreferences . getLocalNumber ( this ) . equals ( recipient . getAddress ( ) . serialize ( ) ) ;
2019-10-31 11:36:52 +11:00
}
2019-06-28 12:32:23 +10:00
// endregion
2019-12-05 13:56:12 +11:00
2019-12-06 13:00:08 +11:00
public void restoreSession ( ) {
2019-12-05 13:56:12 +11:00
// Loki - User clicked restore session
2020-01-28 11:13:07 +11:00
if ( recipient . isGroupRecipient ( ) ) { return ; }
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory . getLokiThreadDatabase ( this ) ;
SmsDatabase smsDatabase = DatabaseFactory . getSmsDatabase ( this ) ;
Set < String > devices = lokiThreadDatabase . getSessionRestoreDevices ( threadId ) ;
for ( String device : devices ) { MessageSender . sendRestoreSessionMessage ( this , device ) ; }
long messageId = smsDatabase . insertMessageOutbox ( threadId , new OutgoingTextMessage ( recipient , " " , 0 , 0 ) , false , System . currentTimeMillis ( ) , null ) ;
if ( messageId > - 1 ) {
smsDatabase . markAsLokiSessionRestoreSent ( messageId ) ;
}
lokiThreadDatabase . removeAllSessionRestoreDevices ( threadId ) ;
updateSessionRestoreBanner ( ) ;
2019-12-05 13:56:12 +11:00
}
2012-07-19 14:22:03 -07:00
}