2017-10-16 22:11:42 +02:00
|
|
|
/*
|
2012-07-19 23:22:03 +02: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-02-01 04:28:40 +01:00
|
|
|
package org.thoughtcrime.securesms.conversation;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2017-11-25 07:00:30 +01:00
|
|
|
import android.Manifest;
|
2017-11-14 19:49:54 +01:00
|
|
|
import android.annotation.SuppressLint;
|
2016-11-21 00:56:47 +01:00
|
|
|
import android.annotation.TargetApi;
|
2013-06-18 23:43:27 +02:00
|
|
|
import android.content.ActivityNotFoundException;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.content.BroadcastReceiver;
|
2020-09-02 08:03:23 +02:00
|
|
|
import android.content.ClipData;
|
|
|
|
import android.content.ClipboardManager;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2019-02-26 00:21:37 +01:00
|
|
|
import android.content.pm.PackageManager;
|
2015-07-07 23:25:41 +02:00
|
|
|
import android.content.res.Configuration;
|
2018-08-25 19:33:14 +02:00
|
|
|
import android.graphics.Bitmap;
|
2018-08-06 20:42:22 +02:00
|
|
|
import android.graphics.BitmapFactory;
|
2020-09-11 01:49:32 +02:00
|
|
|
import android.graphics.PorterDuff;
|
|
|
|
import android.graphics.PorterDuffColorFilter;
|
2019-02-26 00:21:37 +01:00
|
|
|
import android.hardware.Camera;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.AsyncTask;
|
2015-11-24 20:47:50 +01:00
|
|
|
import android.os.Build;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.os.Bundle;
|
2020-01-16 04:35:51 +01:00
|
|
|
import android.os.Handler;
|
2015-11-18 23:52:26 +01:00
|
|
|
import android.os.Vibrator;
|
2016-03-30 00:52:54 +02:00
|
|
|
import android.provider.Browser;
|
2016-11-21 00:56:47 +01:00
|
|
|
import android.provider.Telephony;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.text.Editable;
|
2016-12-27 00:14:23 +01:00
|
|
|
import android.text.TextUtils;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.text.TextWatcher;
|
2015-09-29 23:26:37 +02:00
|
|
|
import android.util.Pair;
|
2020-01-22 00:46:04 +01:00
|
|
|
import android.util.TypedValue;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.view.KeyEvent;
|
2014-06-29 05:40:57 +02:00
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.View.OnClickListener;
|
2014-05-29 05:53:34 +02:00
|
|
|
import android.view.View.OnFocusChangeListener;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.view.View.OnKeyListener;
|
2017-02-16 21:28:06 +01:00
|
|
|
import android.view.WindowManager;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.view.inputmethod.EditorInfo;
|
2015-06-09 16:37:20 +02:00
|
|
|
import android.widget.Button;
|
2015-05-18 19:26:32 +02:00
|
|
|
import android.widget.ImageButton;
|
2020-01-17 00:37:06 +01:00
|
|
|
import android.widget.ImageView;
|
2020-01-16 04:35:51 +01:00
|
|
|
import android.widget.ProgressBar;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-09-11 01:49:32 +02:00
|
|
|
import androidx.annotation.ColorInt;
|
2020-08-21 07:06:39 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.appcompat.app.ActionBar;
|
|
|
|
import androidx.appcompat.app.AlertDialog;
|
|
|
|
import androidx.appcompat.widget.SearchView;
|
|
|
|
import androidx.appcompat.widget.Toolbar;
|
|
|
|
import androidx.core.content.pm.ShortcutInfoCompat;
|
|
|
|
import androidx.core.content.pm.ShortcutManagerCompat;
|
|
|
|
import androidx.core.graphics.drawable.IconCompat;
|
|
|
|
import androidx.core.view.MenuItemCompat;
|
|
|
|
import androidx.lifecycle.ViewModelProviders;
|
2020-09-02 03:55:01 +02:00
|
|
|
import androidx.loader.app.LoaderManager;
|
2020-08-21 07:06:39 +02:00
|
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
|
|
|
2019-02-25 22:58:10 +01:00
|
|
|
import com.annimon.stream.Stream;
|
2013-11-26 02:00:20 +01:00
|
|
|
|
2017-02-18 05:43:24 +01:00
|
|
|
import org.greenrobot.eventbus.EventBus;
|
|
|
|
import org.greenrobot.eventbus.Subscribe;
|
|
|
|
import org.greenrobot.eventbus.ThreadMode;
|
2021-01-21 05:42:43 +01:00
|
|
|
|
|
|
|
|
2021-04-27 06:26:26 +02:00
|
|
|
import org.session.libsession.messaging.mentions.MentionsManager;
|
2021-03-02 02:24:09 +01:00
|
|
|
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
|
|
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
2021-04-27 06:26:26 +02:00
|
|
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
2021-03-02 02:24:09 +01:00
|
|
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
|
|
|
import org.session.libsession.messaging.threads.DistributionTypes;
|
2021-02-17 06:09:36 +01:00
|
|
|
import org.session.libsession.utilities.GroupUtil;
|
2021-02-01 07:00:32 +01:00
|
|
|
import org.session.libsession.utilities.MediaTypes;
|
2020-12-07 07:40:47 +01:00
|
|
|
import org.session.libsignal.libsignal.InvalidMessageException;
|
|
|
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
2021-02-19 01:03:58 +01:00
|
|
|
import org.session.libsignal.service.loki.utilities.mentions.Mention;
|
2020-12-07 07:40:47 +01:00
|
|
|
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
|
|
|
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
2019-02-01 04:28:40 +01:00
|
|
|
import org.thoughtcrime.securesms.ApplicationContext;
|
|
|
|
import org.thoughtcrime.securesms.ExpirationDialog;
|
|
|
|
import org.thoughtcrime.securesms.MediaOverviewActivity;
|
|
|
|
import org.thoughtcrime.securesms.MuteDialog;
|
|
|
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
|
|
|
import org.thoughtcrime.securesms.ShortcutLauncherActivity;
|
2015-11-18 23:52:26 +01:00
|
|
|
import org.thoughtcrime.securesms.audio.AudioRecorder;
|
2015-10-24 02:00:51 +02:00
|
|
|
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
|
2015-05-18 19:26:32 +02:00
|
|
|
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
2015-10-28 17:47:09 +01:00
|
|
|
import org.thoughtcrime.securesms.components.AttachmentTypeSelector;
|
2015-03-11 22:23:45 +01:00
|
|
|
import org.thoughtcrime.securesms.components.ComposeText;
|
2019-02-01 18:06:59 +01:00
|
|
|
import org.thoughtcrime.securesms.components.ConversationSearchBottomBar;
|
2015-11-18 23:52:26 +01:00
|
|
|
import org.thoughtcrime.securesms.components.HidingLinearLayout;
|
2015-09-22 02:41:27 +02:00
|
|
|
import org.thoughtcrime.securesms.components.InputAwareLayout;
|
2015-11-18 23:52:26 +01:00
|
|
|
import org.thoughtcrime.securesms.components.InputPanel;
|
2015-07-03 01:47:03 +02:00
|
|
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
|
2018-04-27 02:03:54 +02:00
|
|
|
import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
2013-10-17 02:28:36 +02:00
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
2018-04-27 02:03:54 +02:00
|
|
|
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
2018-10-29 23:14:31 +01:00
|
|
|
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
2021-01-13 07:11:30 +01:00
|
|
|
import org.session.libsession.messaging.threads.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 21:22:04 +02:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.DraftDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
2014-12-12 02:13:01 +01:00
|
|
|
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
|
2016-10-10 20:13:37 +02:00
|
|
|
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
2014-12-12 02:13:01 +01:00
|
|
|
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
2013-04-26 03:59:49 +02:00
|
|
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
2018-02-07 23:01:37 +01:00
|
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
|
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
2018-03-20 19:27:11 +01:00
|
|
|
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
2019-06-24 06:10:09 +02:00
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
2019-09-17 03:46:47 +02:00
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
2019-06-24 06:10:09 +02:00
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
|
2021-02-03 02:22:40 +01:00
|
|
|
import org.session.libsignal.utilities.logging.Log;
|
2020-06-09 05:58:01 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
|
2020-05-14 02:44:13 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
2020-11-20 08:59:13 +01:00
|
|
|
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
|
2020-05-12 03:46:11 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
2020-05-11 08:19:26 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
2021-03-05 00:17:34 +01:00
|
|
|
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
2020-09-11 01:49:32 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
2020-05-14 02:44:13 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
2020-11-20 08:59:13 +01:00
|
|
|
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities;
|
2020-05-11 08:19:26 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
|
2020-09-07 08:13:34 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
|
2019-06-24 06:10:09 +02: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 21:22:04 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
2015-09-05 02:33:22 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
|
2015-11-18 23:52:26 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.AudioSlide;
|
2018-11-20 18:59:23 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.GifSlide;
|
2017-10-16 22:11:42 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
|
|
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
2018-09-20 22:27:18 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.ImageSlide;
|
2015-07-28 22:17:01 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
2021-03-02 02:24:09 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.MmsException;
|
2021-03-12 05:23:29 +01:00
|
|
|
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
|
|
|
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
|
2018-04-24 20:09:54 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.QuoteId;
|
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 21:22:04 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.Slide;
|
2015-11-18 23:52:26 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
2019-02-27 04:29:52 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.TextSlide;
|
2018-11-20 18:59:23 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
2016-10-10 20:13:37 +02:00
|
|
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
2017-11-25 07:00:30 +01:00
|
|
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
2019-02-26 02:47:30 +01:00
|
|
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
2021-01-13 07:11:30 +01:00
|
|
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
|
|
|
import org.session.libsession.messaging.threads.recipients.RecipientFormattingException;
|
|
|
|
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
2019-02-01 18:06:59 +01:00
|
|
|
import org.thoughtcrime.securesms.search.model.MessageResult;
|
2021-03-02 02:24:09 +01:00
|
|
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
|
|
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
2021-04-14 08:37:04 +02:00
|
|
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
2018-08-25 19:33:14 +02:00
|
|
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
2020-01-17 00:37:06 +01:00
|
|
|
import org.thoughtcrime.securesms.util.DateUtils;
|
2015-09-05 02:33:22 +02:00
|
|
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
2020-12-07 07:40:47 +01:00
|
|
|
import org.thoughtcrime.securesms.util.PushCharacterCalculator;
|
2021-01-29 06:35:47 +01:00
|
|
|
import org.session.libsession.utilities.ServiceUtil;
|
2021-02-02 05:10:23 +01:00
|
|
|
import org.session.libsession.utilities.Util;
|
2021-01-15 05:36:30 +01:00
|
|
|
|
2021-02-02 05:40:43 +01:00
|
|
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
2021-04-26 03:23:09 +02:00
|
|
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
2021-01-15 05:36:30 +01:00
|
|
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
|
|
|
import org.session.libsession.messaging.threads.GroupRecord;
|
|
|
|
import org.session.libsession.utilities.ExpirationUtil;
|
|
|
|
import org.session.libsession.utilities.views.Stub;
|
|
|
|
import org.session.libsession.utilities.ViewUtil;
|
2021-01-18 04:58:11 +01:00
|
|
|
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
|
2021-02-01 02:10:48 +01:00
|
|
|
import org.session.libsignal.utilities.concurrent.ListenableFuture;
|
|
|
|
import org.session.libsignal.utilities.concurrent.SettableFuture;
|
2021-01-18 04:58:11 +01:00
|
|
|
import org.session.libsession.utilities.TextSecurePreferences;
|
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 21:22:04 +02:00
|
|
|
|
|
|
|
import java.io.IOException;
|
2019-02-27 04:29:52 +01:00
|
|
|
import java.text.SimpleDateFormat;
|
2019-10-11 02:13:34 +02:00
|
|
|
import java.util.ArrayList;
|
2018-04-27 02:03:54 +02:00
|
|
|
import java.util.Collections;
|
2019-02-27 04:29:52 +01:00
|
|
|
import java.util.Date;
|
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 21:22:04 +02:00
|
|
|
import java.util.List;
|
2019-02-27 04:29:52 +01:00
|
|
|
import java.util.Locale;
|
2015-11-18 23:52:26 +01:00
|
|
|
import java.util.concurrent.ExecutionException;
|
2018-07-25 17:30:48 +02: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 21:22:04 +02:00
|
|
|
|
2019-10-10 06:30:34 +02:00
|
|
|
import kotlin.Unit;
|
2019-07-26 04:20:55 +02:00
|
|
|
import network.loki.messenger.R;
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
/**
|
|
|
|
* Activity for displaying a message thread, as well as
|
|
|
|
* composing/sending a new message into that thread.
|
|
|
|
*
|
|
|
|
* @author Moxie Marlinspike
|
|
|
|
*
|
|
|
|
*/
|
2017-11-14 19:49:54 +01:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
2014-06-29 05:40:57 +02:00
|
|
|
public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
2014-04-15 12:43:14 +02:00
|
|
|
implements ConversationFragment.ConversationFragmentListener,
|
2015-02-10 12:15:50 +01:00
|
|
|
AttachmentManager.AttachmentListener,
|
2017-11-25 07:00:30 +01:00
|
|
|
RecipientModifiedListener,
|
2015-06-08 20:07:46 +02:00
|
|
|
OnKeyboardShownListener,
|
2016-12-27 00:14:23 +01:00
|
|
|
InputPanel.Listener,
|
2019-02-01 18:06:59 +01:00
|
|
|
InputPanel.MediaListener,
|
2019-02-26 00:21:37 +01:00
|
|
|
ComposeText.CursorPositionChangedListener,
|
2021-02-23 01:56:03 +01:00
|
|
|
ConversationSearchBottomBar.EventListener
|
2013-04-26 03:59:49 +02:00
|
|
|
{
|
2014-02-19 01:28:54 +01:00
|
|
|
private static final String TAG = ConversationActivity.class.getSimpleName();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
public static final String ADDRESS_EXTRA = "address";
|
2013-04-26 03:59:49 +02: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 21:54:40 +01:00
|
|
|
public static final String TEXT_EXTRA = "draft_text";
|
2018-11-20 18:59:23 +01:00
|
|
|
public static final String MEDIA_EXTRA = "media_list";
|
2019-04-17 16:21:30 +02:00
|
|
|
public static final String STICKER_EXTRA = "media_list";
|
2013-04-26 03:59:49 +02:00
|
|
|
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
|
2017-01-19 20:37:19 +01:00
|
|
|
public static final String TIMING_EXTRA = "timing";
|
2017-02-14 07:35:47 +01:00
|
|
|
public static final String LAST_SEEN_EXTRA = "last_seen";
|
2018-04-07 03:15:24 +02:00
|
|
|
public static final String STARTING_POSITION_EXTRA = "starting_position";
|
2013-02-03 05:37:40 +01:00
|
|
|
|
2020-12-01 11:36:41 +01:00
|
|
|
// private static final int PICK_GALLERY = 1;
|
2018-04-27 02:03:54 +02:00
|
|
|
private static final int PICK_DOCUMENT = 2;
|
|
|
|
private static final int PICK_AUDIO = 3;
|
|
|
|
private static final int PICK_CONTACT = 4;
|
2020-12-02 13:01:04 +01:00
|
|
|
// private static final int GET_CONTACT_DETAILS = 5;
|
2020-12-01 11:36:41 +01:00
|
|
|
// private static final int GROUP_EDIT = 6;
|
2018-04-27 02:03:54 +02:00
|
|
|
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-14 00:05:25 +01:00
|
|
|
private static final int MEDIA_SENDER = 12;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2021-01-21 05:42:43 +01:00
|
|
|
private GlideRequests glideRequests;
|
2019-10-10 05:44:08 +02:00
|
|
|
protected ComposeText composeText;
|
|
|
|
private AnimatingToggle buttonToggle;
|
2020-12-07 07:40:47 +01:00
|
|
|
private ImageButton sendButton;
|
2019-10-10 05:44:08 +02:00
|
|
|
private ImageButton attachButton;
|
2020-09-07 08:13:34 +02:00
|
|
|
private ProfilePictureView profilePictureView;
|
2020-01-10 01:35:16 +01:00
|
|
|
private TextView titleTextView;
|
2019-10-10 05:44:08 +02:00
|
|
|
private ConversationFragment fragment;
|
|
|
|
private Button unblockButton;
|
|
|
|
private Button makeDefaultSmsButton;
|
|
|
|
private InputAwareLayout container;
|
2018-10-29 23:14:31 +01:00
|
|
|
private TypingStatusTextWatcher typingTextWatcher;
|
2019-10-10 05:44:08 +02:00
|
|
|
private MentionTextWatcher mentionTextWatcher;
|
2019-02-01 18:06:59 +01:00
|
|
|
private ConversationSearchBottomBar searchNav;
|
|
|
|
private MenuItem searchViewItem;
|
2020-01-16 04:35:51 +01:00
|
|
|
private ProgressBar messageStatusProgressBar;
|
2020-01-17 00:37:06 +01:00
|
|
|
private ImageView muteIndicatorImageView;
|
2020-05-14 02:44:13 +02:00
|
|
|
private TextView subtitleTextView;
|
2020-09-17 08:56:26 +02:00
|
|
|
private View homeButtonContainer;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2015-10-28 17:47:09 +01:00
|
|
|
private AttachmentTypeSelector attachmentTypeSelector;
|
|
|
|
private AttachmentManager attachmentManager;
|
2015-11-18 23:52:26 +01:00
|
|
|
private AudioRecorder audioRecorder;
|
2020-11-04 04:49:28 +01:00
|
|
|
private Handler audioHandler;
|
|
|
|
private Runnable stopRecordingTask;
|
2019-04-17 16:21:30 +02:00
|
|
|
private Stub<MediaKeyboard> emojiDrawerStub;
|
2015-11-18 23:52:26 +01:00
|
|
|
protected HidingLinearLayout quickAttachmentToggle;
|
2018-08-10 18:18:02 +02:00
|
|
|
protected HidingLinearLayout inlineAttachmentToggle;
|
2015-11-18 23:52:26 +01:00
|
|
|
private InputPanel inputPanel;
|
2019-02-01 18:06:59 +01:00
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
private LinkPreviewViewModel linkPreviewViewModel;
|
|
|
|
private ConversationSearchViewModel searchViewModel;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
private Recipient recipient;
|
2014-02-18 05:25:40 +01:00
|
|
|
private long threadId;
|
|
|
|
private int distributionType;
|
2020-03-12 23:35:47 +01:00
|
|
|
private boolean isDefaultSms = false;
|
2020-01-13 00:11:20 +01:00
|
|
|
private boolean isSecurityInitialized = false;
|
|
|
|
private int expandedKeyboardHeight = 0;
|
|
|
|
private int collapsedKeyboardHeight = Integer.MAX_VALUE;
|
|
|
|
private int keyboardHeight = 0;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-05-14 02:44:13 +02:00
|
|
|
// Message status bar
|
2020-01-16 04:35:51 +01:00
|
|
|
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
2020-07-15 06:26:20 +02:00
|
|
|
private String messageStatus = null;
|
2020-01-16 04:35:51 +01:00
|
|
|
|
2019-10-11 02:13:34 +02:00
|
|
|
// Mentions
|
2020-07-15 06:26:20 +02:00
|
|
|
private View mentionCandidateSelectionViewContainer;
|
2019-10-11 07:37:28 +02:00
|
|
|
private MentionCandidateSelectionView mentionCandidateSelectionView;
|
2020-07-15 06:26:20 +02:00
|
|
|
private int currentMentionStartIndex = -1;
|
|
|
|
private ArrayList<Mention> mentions = new ArrayList<>();
|
|
|
|
private String oldText = "";
|
2019-10-11 02:13:34 +02:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
private final PushCharacterCalculator characterCalculator = new PushCharacterCalculator();
|
2014-12-15 21:25:55 +01:00
|
|
|
|
|
|
|
@Override
|
2018-02-02 04:22:48 +01:00
|
|
|
protected void onCreate(Bundle state, boolean ready) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onCreate()");
|
2012-07-19 23:22:03 +02:00
|
|
|
|
|
|
|
setContentView(R.layout.conversation_activity);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), Locale.getDefault());
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-01-16 04:35:51 +01:00
|
|
|
registerMessageStatusObserver("calculatingPoW");
|
|
|
|
registerMessageStatusObserver("contactingNetwork");
|
|
|
|
registerMessageStatusObserver("sendingMessage");
|
|
|
|
registerMessageStatusObserver("messageSent");
|
|
|
|
registerMessageStatusObserver("messageFailed");
|
2020-02-12 04:44:23 +01:00
|
|
|
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Toast.makeText(ConversationActivity.this, "Your clock is out of sync with the service node network.", Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
broadcastReceivers.add(broadcastReceiver);
|
|
|
|
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("clockOutOfSync"));
|
2020-01-16 04:35:51 +01:00
|
|
|
|
2015-06-09 16:37:20 +02:00
|
|
|
initializeActionBar();
|
2014-12-25 03:32:51 +01:00
|
|
|
initializeViews();
|
2012-07-19 23:22:03 +02:00
|
|
|
initializeResources();
|
2019-01-15 09:41:05 +01:00
|
|
|
initializeLinkPreviewObserver();
|
2019-02-01 18:06:59 +01:00
|
|
|
initializeSearchObserver();
|
2017-03-14 21:24:24 +01:00
|
|
|
initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
|
2015-10-02 02:46:47 +02:00
|
|
|
@Override
|
|
|
|
public void onSuccess(Boolean result) {
|
2018-07-25 17:30:48 +02:00
|
|
|
initializeDraft().addListener(new AssertedSuccessListener<Boolean>() {
|
|
|
|
@Override
|
2018-10-29 23:14:31 +01:00
|
|
|
public void onSuccess(Boolean loadedDraft) {
|
|
|
|
if (loadedDraft != null && loadedDraft) {
|
2018-08-02 17:57:10 +02:00
|
|
|
Log.i(TAG, "Finished loading draft");
|
2018-07-25 17:30:48 +02:00
|
|
|
Util.runOnMain(() -> {
|
|
|
|
if (fragment != null && fragment.isResumed()) {
|
|
|
|
fragment.moveToLastSeen();
|
2018-08-02 17:57:10 +02:00
|
|
|
} else {
|
|
|
|
Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state");
|
2018-07-25 17:30:48 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-10-29 23:14:31 +01:00
|
|
|
|
|
|
|
if (TextSecurePreferences.isTypingIndicatorsEnabled(ConversationActivity.this)) {
|
|
|
|
composeText.addTextChangedListener(typingTextWatcher);
|
|
|
|
}
|
2019-02-14 22:55:48 +01:00
|
|
|
composeText.setSelection(composeText.length(), composeText.length());
|
2019-10-10 05:44:08 +02:00
|
|
|
composeText.addTextChangedListener(mentionTextWatcher);
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionView.setGlide(glideRequests);
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> {
|
|
|
|
mentions.add(mentionCandidate);
|
2019-10-10 06:30:34 +02:00
|
|
|
String oldText = composeText.getText().toString();
|
2020-06-22 06:05:12 +02:00
|
|
|
String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName() + " ";
|
2019-10-10 06:30:34 +02:00
|
|
|
composeText.setText(newText);
|
|
|
|
composeText.setSelection(newText.length());
|
2019-10-11 05:59:13 +02:00
|
|
|
currentMentionStartIndex = -1;
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.hide();
|
|
|
|
ConversationActivity.this.oldText = newText;
|
2019-10-10 06:30:34 +02:00
|
|
|
return Unit.INSTANCE;
|
|
|
|
});
|
2018-07-25 17:30:48 +02:00
|
|
|
}
|
|
|
|
});
|
2015-10-02 02:46:47 +02:00
|
|
|
}
|
|
|
|
});
|
2019-09-05 01:38:36 +02:00
|
|
|
|
2020-05-30 00:53:00 +02:00
|
|
|
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
|
2019-10-10 01:39:56 +02:00
|
|
|
|
2021-04-27 06:26:26 +02:00
|
|
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
2020-01-22 00:46:04 +01:00
|
|
|
if (publicChat != null) {
|
2020-11-20 08:59:13 +01:00
|
|
|
// Request open group info update and handle the successful result in #onOpenGroupInfoUpdated().
|
|
|
|
PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel());
|
2019-09-05 01:38:36 +02:00
|
|
|
}
|
2020-01-22 00:46:04 +01:00
|
|
|
|
2020-01-13 00:11:20 +01:00
|
|
|
View rootView = findViewById(R.id.rootView);
|
|
|
|
rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
|
|
|
int height = rootView.getRootView().getHeight() - rootView.getHeight();
|
2020-01-13 06:47:50 +01:00
|
|
|
int thresholdInDP = 120;
|
|
|
|
float scale = getResources().getDisplayMetrics().density;
|
|
|
|
int thresholdInPX = (int)(thresholdInDP * scale);
|
|
|
|
if (expandedKeyboardHeight == 0 || height > thresholdInPX) {
|
|
|
|
expandedKeyboardHeight = height;
|
|
|
|
}
|
2020-01-13 00:11:20 +01:00
|
|
|
collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height);
|
2020-01-14 00:26:18 +01:00
|
|
|
keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight;
|
2020-09-18 05:58:34 +02:00
|
|
|
|
|
|
|
// Use 300dp if the keyboard wasn't opened yet.
|
|
|
|
if (keyboardHeight == 0) {
|
|
|
|
keyboardHeight = (int)(300f * getResources().getDisplayMetrics().density);
|
|
|
|
}
|
2020-01-13 00:11:20 +01:00
|
|
|
});
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2020-01-16 04:35:51 +01: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 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2013-02-04 03:41:34 +01:00
|
|
|
@Override
|
2014-12-25 03:32:51 +01:00
|
|
|
protected void onNewIntent(Intent intent) {
|
2020-09-21 03:53:16 +02:00
|
|
|
super.onNewIntent(intent);
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onNewIntent()");
|
2016-01-31 00:33:47 +01:00
|
|
|
|
|
|
|
if (isFinishing()) {
|
|
|
|
Log.w(TAG, "Activity is finishing...");
|
|
|
|
return;
|
|
|
|
}
|
2014-12-15 21:25:55 +01:00
|
|
|
|
2021-02-02 05:10:23 +01:00
|
|
|
if (!org.thoughtcrime.securesms.util.Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) {
|
2014-12-25 03:32:51 +01:00
|
|
|
saveDraft();
|
2017-10-16 22:11:42 +02:00
|
|
|
attachmentManager.clear(glideRequests, false);
|
2018-10-29 23:14:31 +01:00
|
|
|
silentlySetComposeText("");
|
2014-12-25 03:32:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
setIntent(intent);
|
|
|
|
initializeResources();
|
2017-03-14 21:24:24 +01:00
|
|
|
initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
|
2015-10-02 02:46:47 +02:00
|
|
|
@Override
|
|
|
|
public void onSuccess(Boolean result) {
|
|
|
|
initializeDraft();
|
|
|
|
}
|
|
|
|
});
|
2014-10-08 20:11:02 +02:00
|
|
|
|
2014-12-25 03:32:51 +01:00
|
|
|
if (fragment != null) {
|
|
|
|
fragment.onNewIntent();
|
|
|
|
}
|
2019-03-01 01:51:10 +01:00
|
|
|
|
|
|
|
searchNav.setVisibility(View.GONE);
|
2013-02-04 03:41:34 +01:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
|
|
|
super.onResume();
|
2013-06-21 20:56:59 +02:00
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
EventBus.getDefault().register(this);
|
2014-02-22 19:54:43 +01:00
|
|
|
initializeEnabledCheck();
|
2020-12-07 07:40:47 +01:00
|
|
|
composeText.setTransport();
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2020-02-12 23:28:00 +01:00
|
|
|
updateTitleTextView(recipient);
|
2020-09-07 08:13:34 +02:00
|
|
|
updateProfilePicture();
|
2020-01-17 00:37:06 +01:00
|
|
|
updateSubtitleTextView();
|
2020-12-07 07:40:47 +01:00
|
|
|
updateInputUI(recipient);
|
2013-02-04 03:41:34 +01:00
|
|
|
|
2020-06-26 08:18:19 +02:00
|
|
|
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
2013-02-04 03:41:34 +01:00
|
|
|
markThreadAsRead();
|
2017-01-19 20:37:19 +01:00
|
|
|
|
2021-02-19 00:05:24 +01:00
|
|
|
inputPanel.setHint(getResources().getString(R.string.ConversationActivity_message));
|
2019-06-25 03:48:39 +02:00
|
|
|
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0)));
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-03-05 02:43:04 +01:00
|
|
|
protected void onPause() {
|
|
|
|
super.onPause();
|
2020-06-26 08:18:19 +02:00
|
|
|
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L);
|
2014-04-08 19:45:55 +02:00
|
|
|
if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
|
2015-11-18 23:52:26 +01:00
|
|
|
inputPanel.onPause();
|
|
|
|
|
2017-02-14 07:35:47 +01:00
|
|
|
fragment.setLastSeen(System.currentTimeMillis());
|
|
|
|
markLastSeen();
|
2015-10-24 02:00:51 +02:00
|
|
|
AudioSlidePlayer.stopAll();
|
2017-02-18 05:43:24 +01:00
|
|
|
EventBus.getDefault().unregister(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onStop() {
|
|
|
|
super.onStop();
|
|
|
|
EventBus.getDefault().unregister(this);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2015-09-24 00:47:48 +02:00
|
|
|
@Override
|
|
|
|
public void onConfigurationChanged(Configuration newConfig) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
|
2015-06-08 20:07:46 +02:00
|
|
|
super.onConfigurationChanged(newConfig);
|
2020-12-07 07:40:47 +01:00
|
|
|
composeText.setTransport();
|
2017-01-19 20:31:41 +01:00
|
|
|
|
|
|
|
if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) {
|
|
|
|
container.hideAttachedInput(true);
|
|
|
|
}
|
2015-06-08 20:07:46 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2014-12-25 03:32:51 +01:00
|
|
|
saveDraft();
|
2017-08-01 17:56:00 +02:00
|
|
|
if (recipient != null) recipient.removeListener(this);
|
2020-01-16 04:35:51 +01:00
|
|
|
for (BroadcastReceiver broadcastReceiver : broadcastReceivers) {
|
|
|
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-12-18 23:37:11 +01:00
|
|
|
public void onActivityResult(final int reqCode, int resultCode, Intent data) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onActivityResult called: " + reqCode + ", " + resultCode + " , " + data);
|
2012-07-19 23:22:03 +02: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 09:41:05 +01:00
|
|
|
updateLinkPreviewState();
|
2017-03-15 02:05:48 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-05-18 19:26:32 +02:00
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
switch (reqCode) {
|
2017-04-19 01:33:03 +02:00
|
|
|
case PICK_DOCUMENT:
|
|
|
|
setMedia(data.getData(), MediaType.DOCUMENT);
|
2012-07-19 23:22:03 +02:00
|
|
|
break;
|
|
|
|
case PICK_AUDIO:
|
2015-10-15 23:40:45 +02:00
|
|
|
setMedia(data.getData(), MediaType.AUDIO);
|
2012-07-19 23:22:03 +02:00
|
|
|
break;
|
2015-07-11 03:45:55 +02:00
|
|
|
case TAKE_PHOTO:
|
|
|
|
if (attachmentManager.getCaptureUri() != null) {
|
2015-10-15 23:40:45 +02:00
|
|
|
setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE);
|
2015-07-11 03:45:55 +02:00
|
|
|
}
|
|
|
|
break;
|
2015-10-16 15:39:27 +02:00
|
|
|
case ADD_CONTACT:
|
2017-08-22 03:32:38 +02:00
|
|
|
recipient = Recipient.from(this, recipient.getAddress(), true);
|
2017-08-01 17:56:00 +02:00
|
|
|
recipient.addListener(this);
|
2015-10-16 15:39:27 +02:00
|
|
|
fragment.reloadList();
|
|
|
|
break;
|
2020-02-27 23:07:01 +01:00
|
|
|
/*
|
2015-12-18 23:37:11 +01:00
|
|
|
case PICK_LOCATION:
|
2016-01-04 22:02:22 +01:00
|
|
|
SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
|
2018-01-25 04:17:44 +01:00
|
|
|
attachmentManager.setLocation(place, getCurrentMediaConstraints());
|
2015-12-18 23:37:11 +01:00
|
|
|
break;
|
2020-02-27 23:07:01 +01:00
|
|
|
*/
|
2016-10-17 04:05:07 +02:00
|
|
|
case PICK_GIF:
|
2018-03-20 19:27:11 +01:00
|
|
|
setMedia(data.getData(),
|
2020-05-22 01:53:04 +02:00
|
|
|
MediaType.GIF,
|
|
|
|
data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
|
|
|
|
data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
|
2016-10-17 04:05:07 +02:00
|
|
|
break;
|
2017-01-18 20:01:13 +01:00
|
|
|
case SMS_DEFAULT:
|
2020-12-07 07:40:47 +01:00
|
|
|
initializeSecurity(true, isDefaultSms);
|
2017-01-18 20:01:13 +01:00
|
|
|
break;
|
2019-03-14 00:05:25 +01:00
|
|
|
case MEDIA_SENDER:
|
2018-09-20 22:27:18 +02:00
|
|
|
long expiresIn = recipient.getExpireMessages() * 1000L;
|
2020-12-07 07:40:47 +01:00
|
|
|
int subscriptionId = -1;
|
2018-09-20 22:27:18 +02:00
|
|
|
boolean initiating = threadId == -1;
|
2019-03-14 00:05:25 +01:00
|
|
|
String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE);
|
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
2018-11-20 18:59:23 +01:00
|
|
|
|
|
|
|
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 22:58:10 +01:00
|
|
|
final Context context = ConversationActivity.this.getApplicationContext();
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
sendMediaMessage(message,
|
2019-04-17 16:21:30 +02:00
|
|
|
slideDeck,
|
|
|
|
inputPanel.getQuote().orNull(),
|
2021-03-02 02:24:09 +01:00
|
|
|
Optional.absent(),
|
|
|
|
initiating).addListener(new AssertedSuccessListener<Void>() {
|
2019-02-25 22:58:10 +01:00
|
|
|
@Override
|
|
|
|
public void onSuccess(Void result) {
|
|
|
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
|
|
Stream.of(slideDeck.getSlides())
|
|
|
|
.map(Slide::getUri)
|
|
|
|
.withoutNulls()
|
2019-03-14 00:05:25 +01:00
|
|
|
.filter(BlobProvider::isAuthority)
|
2019-02-26 02:47:30 +01:00
|
|
|
.forEach(uri -> BlobProvider.getInstance().delete(context, uri));
|
2019-02-25 22:58:10 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2018-11-20 18:59:23 +01:00
|
|
|
|
|
|
|
break;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-30 00:52:54 +02: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-30 00:52:54 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
@Override
|
|
|
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
2014-06-29 05:40:57 +02:00
|
|
|
MenuInflater inflater = this.getMenuInflater();
|
2012-07-19 23:22:03 +02:00
|
|
|
menu.clear();
|
|
|
|
|
2021-01-15 05:36:30 +01:00
|
|
|
boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup();
|
2019-08-06 07:43:47 +02:00
|
|
|
|
2021-02-02 02:00:11 +01:00
|
|
|
if (!isOpenGroupOrRSSFeed) {
|
2017-08-01 17:56:00 +02:00
|
|
|
if (recipient.getExpireMessages() > 0) {
|
2016-09-26 05:04:13 +02:00
|
|
|
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
|
|
|
|
2020-09-11 01:49:32 +02:00
|
|
|
final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
|
|
|
|
final View actionView = MenuItemCompat.getActionView(item);
|
|
|
|
final ImageView iconView = actionView.findViewById(R.id.menu_badge_icon);
|
|
|
|
final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
|
2016-09-26 05:04:13 +02:00
|
|
|
|
2020-09-11 01:49:32 +02:00
|
|
|
@ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getTheme());
|
|
|
|
iconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
|
2017-08-01 17:56:00 +02:00
|
|
|
badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.getExpireMessages()));
|
2017-12-05 20:52:03 +01:00
|
|
|
actionView.setOnClickListener(v -> onOptionsItemSelected(item));
|
2016-09-26 05:04:13 +02:00
|
|
|
} else {
|
|
|
|
inflater.inflate(R.menu.conversation_expiring_off, menu);
|
|
|
|
}
|
|
|
|
}
|
2016-08-16 05:23:56 +02:00
|
|
|
|
2012-08-08 04:03:28 +02:00
|
|
|
if (isSingleConversation()) {
|
2020-07-16 03:20:39 +02:00
|
|
|
if (recipient.isBlocked()) {
|
|
|
|
inflater.inflate(R.menu.conversation_unblock, menu);
|
|
|
|
} else {
|
|
|
|
inflater.inflate(R.menu.conversation_block, menu);
|
|
|
|
}
|
2020-09-02 08:03:23 +02:00
|
|
|
inflater.inflate(R.menu.conversation_copy_session_id, menu);
|
2020-02-12 23:28:00 +01:00
|
|
|
} else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
|
2020-08-18 02:22:21 +02:00
|
|
|
// inflater.inflate(R.menu.conversation_group_options, menu);
|
2013-04-26 03:59:49 +02:00
|
|
|
|
2014-02-18 05:25:40 +01:00
|
|
|
if (!isPushGroupConversation()) {
|
|
|
|
inflater.inflate(R.menu.conversation_mms_group_options, menu);
|
2021-03-02 02:24:09 +01:00
|
|
|
if (distributionType == DistributionTypes.BROADCAST) {
|
2014-02-18 05:25:40 +01:00
|
|
|
menu.findItem(R.id.menu_distribution_broadcast).setChecked(true);
|
|
|
|
} else {
|
|
|
|
menu.findItem(R.id.menu_distribution_conversation).setChecked(true);
|
|
|
|
}
|
2014-02-22 19:54:43 +01:00
|
|
|
} else if (isActiveGroup()) {
|
2014-02-21 00:41:52 +01:00
|
|
|
inflater.inflate(R.menu.conversation_push_group_options, menu);
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
2012-08-08 04:03:28 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
inflater.inflate(R.menu.conversation, menu);
|
2014-06-04 01:24:44 +02:00
|
|
|
|
2020-12-13 22:55:06 +01:00
|
|
|
// if (isSingleConversation()) {
|
|
|
|
// inflater.inflate(R.menu.conversation_secure, menu);
|
|
|
|
// }
|
2015-09-29 23:26:37 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
if (recipient != null && recipient.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
|
|
|
|
else inflater.inflate(R.menu.conversation_unmuted, menu);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2019-07-19 07:59:51 +02:00
|
|
|
/*
|
2017-08-01 17:56:00 +02: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 05:26:54 +02:00
|
|
|
|
2014-06-04 01:24:44 +02:00
|
|
|
|
2019-01-14 08:30:54 +01: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 20:45:06 +01:00
|
|
|
MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications);
|
|
|
|
|
|
|
|
if (muteItem != null) {
|
|
|
|
muteItem.setVisible(false);
|
|
|
|
}
|
2019-01-14 08:30:54 +01:00
|
|
|
}
|
2019-10-24 05:26:54 +02:00
|
|
|
*/
|
2019-01-14 08:30:54 +01:00
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
searchViewItem = menu.findItem(R.id.menu_search);
|
|
|
|
|
2020-01-16 01:37:52 +01:00
|
|
|
SearchView searchView = (SearchView)searchViewItem.getActionView();
|
2019-02-01 18:06:59 +01: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);
|
2020-12-07 07:40:47 +01:00
|
|
|
updateInputUI(recipient);
|
2019-02-01 18:06:59 +01:00
|
|
|
fragment.onSearchQueryUpdated(null);
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
super.onPrepareOptionsMenu(menu);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
super.onOptionsItemSelected(item);
|
|
|
|
switch (item.getItemId()) {
|
2020-08-24 04:52:50 +02:00
|
|
|
// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
|
|
|
|
// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
|
2020-07-16 03:20:39 +02:00
|
|
|
case R.id.menu_unblock: handleUnblock(); return true;
|
|
|
|
case R.id.menu_block: handleBlock(); return true;
|
2020-09-02 08:03:23 +02:00
|
|
|
case R.id.menu_copy_session_id: handleCopySessionID(); return true;
|
2019-10-18 03:40:41 +02: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 18:06:59 +01:00
|
|
|
case R.id.menu_search: handleSearch(); return true;
|
2020-08-24 04:52:50 +02:00
|
|
|
// case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
|
2020-12-13 22:55:06 +01:00
|
|
|
// case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
|
2020-08-24 04:52:50 +02:00
|
|
|
// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
|
2013-04-26 03:59:49 +02:00
|
|
|
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
|
|
|
|
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
|
2020-02-05 00:36:26 +01:00
|
|
|
case R.id.menu_edit_group: handleEditPushGroup(); return true;
|
2014-02-21 00:41:52 +01:00
|
|
|
case R.id.menu_leave: handleLeavePushGroup(); return true;
|
2015-06-09 16:37:20 +02:00
|
|
|
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
|
|
|
|
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
|
2020-05-14 02:44:13 +02:00
|
|
|
// case R.id.menu_conversation_settings: handleConversationSettings(); return true;
|
2016-08-16 05:23:56 +02:00
|
|
|
case R.id.menu_expiring_messages_off:
|
|
|
|
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
|
2013-04-26 03:59:49 +02:00
|
|
|
case android.R.id.home: handleReturnToConversationList(); return true;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-28 05:57:27 +02:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.d(TAG, "onBackPressed()");
|
2015-07-24 22:22:28 +02:00
|
|
|
if (container.isInputOpen()) container.hideCurrentInput(composeText);
|
|
|
|
else super.onBackPressed();
|
2013-06-28 05:57:27 +02:00
|
|
|
}
|
|
|
|
|
2015-07-03 01:47:03 +02:00
|
|
|
@Override
|
|
|
|
public void onKeyboardShown() {
|
2015-11-22 19:44:53 +01:00
|
|
|
inputPanel.onKeyboardShown();
|
2015-07-03 01:47:03 +02:00
|
|
|
}
|
|
|
|
|
2017-11-25 07:00:30 +01:00
|
|
|
@Override
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
2020-12-01 11:36:41 +01:00
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
2017-11-25 07:00:30 +01:00
|
|
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
//////// Event Handlers
|
|
|
|
|
2013-02-17 20:42:30 +01:00
|
|
|
private void handleReturnToConversationList() {
|
2019-12-19 12:04:38 +01:00
|
|
|
Intent intent = new Intent(this, HomeActivity.class);
|
2013-02-17 20:42:30 +01:00
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
startActivity(intent);
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
2016-08-16 05:23:56 +02:00
|
|
|
private void handleSelectMessageExpiration() {
|
2017-01-03 22:36:34 +01:00
|
|
|
if (isPushGroupConversation() && !isActiveGroup()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:52:03 +01: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);
|
2021-04-14 08:37:04 +02:00
|
|
|
ExpirationTimerUpdate message = new ExpirationTimerUpdate(expirationTime);
|
|
|
|
message.setRecipient(recipient.getAddress().serialize()); // we need the recipient in ExpiringMessageManager.insertOutgoingExpirationTimerMessage
|
2021-03-04 07:14:12 +01:00
|
|
|
message.setSentTimestamp(System.currentTimeMillis());
|
2021-04-14 08:37:04 +02:00
|
|
|
ExpiringMessageManager expiringMessageManager = ApplicationContext.getInstance(getApplicationContext()).getExpiringMessageManager();
|
|
|
|
expiringMessageManager.setExpirationTimer(message);
|
|
|
|
MessageSender.send(message, recipient.getAddress());
|
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
return null;
|
|
|
|
}
|
2017-02-14 07:35:47 +01:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Void result) {
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
if (fragment != null) fragment.setLastSeen(0);
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2016-08-16 05:23:56 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-06-09 16:37:20 +02:00
|
|
|
private void handleMuteNotifications() {
|
2017-12-05 20:52:03 +01:00
|
|
|
MuteDialog.show(this, until -> {
|
|
|
|
recipient.setMuted(until);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
|
|
|
.setMuted(recipient, until);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2015-06-09 16:37:20 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleUnmuteNotifications() {
|
2017-08-01 17:56:00 +02:00
|
|
|
recipient.setMuted(0);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
2017-08-22 03:47:37 +02:00
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
2017-08-01 17:56:00 +02:00
|
|
|
.setMuted(recipient, 0);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2015-06-09 16:37:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void handleUnblock() {
|
2018-09-10 17:40:00 +02: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;
|
|
|
|
|
2015-05-20 23:36:30 +02:00
|
|
|
new AlertDialog.Builder(this)
|
2018-09-10 17:40:00 +02: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 20:52:03 +01:00
|
|
|
.setBlocked(recipient, false);
|
2016-08-27 01:53:23 +02:00
|
|
|
|
2018-09-10 17:40:00 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
|
|
}).show();
|
2015-06-09 16:37:20 +02:00
|
|
|
}
|
|
|
|
|
2016-11-21 00:56:47 +01: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 20:01:13 +01:00
|
|
|
startActivityForResult(intent, SMS_DEFAULT);
|
2016-11-21 00:56:47 +01:00
|
|
|
}
|
|
|
|
|
2020-07-16 03:20:39 +02:00
|
|
|
private void handleBlock() {
|
|
|
|
int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question;
|
|
|
|
int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact;
|
|
|
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
.setTitle(titleRes)
|
|
|
|
.setMessage(bodyRes)
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
.setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
|
|
|
.setBlocked(recipient, true);
|
|
|
|
|
|
|
|
Util.runOnMain(() -> finish());
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
|
|
}).show();
|
|
|
|
}
|
|
|
|
|
2020-09-02 08:03:23 +02:00
|
|
|
private void handleCopySessionID() {
|
|
|
|
if (recipient.isGroupRecipient()) { return; }
|
2021-01-15 05:36:30 +01:00
|
|
|
String sessionID = recipient.getAddress().toString();
|
2020-09-02 08:03:23 +02:00
|
|
|
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
|
|
|
|
ClipData clip = ClipData.newPlainText("Session ID", sessionID);
|
|
|
|
clipboard.setPrimaryClip(clip);
|
|
|
|
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
|
2015-01-19 03:11:30 +01:00
|
|
|
private void handleViewMedia() {
|
|
|
|
Intent intent = new Intent(this, MediaOverviewActivity.class);
|
2017-08-01 17:56:00 +02:00
|
|
|
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
|
2015-01-19 03:11:30 +01: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 20:42:22 +02:00
|
|
|
new AsyncTask<Void, Void, IconCompat>() {
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2018-08-06 20:42:22 +02:00
|
|
|
@Override
|
|
|
|
protected IconCompat doInBackground(Void... voids) {
|
|
|
|
Context context = getApplicationContext();
|
|
|
|
IconCompat icon = null;
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2018-08-06 20:42:22 +02:00
|
|
|
if (recipient.getContactPhoto() != null) {
|
|
|
|
try {
|
2018-08-25 19:33:14 +02:00
|
|
|
Bitmap bitmap = BitmapFactory.decodeStream(recipient.getContactPhoto().openInputStream(context));
|
|
|
|
bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300);
|
|
|
|
icon = IconCompat.createWithAdaptiveBitmap(bitmap);
|
2018-08-06 20:42:22 +02: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 20:42:22 +02: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 20:42:22 +02:00
|
|
|
return icon;
|
|
|
|
}
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2018-08-06 20:42:22 +02: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 18:06:59 +01:00
|
|
|
private void handleSearch() {
|
|
|
|
searchViewModel.onSearchOpened();
|
|
|
|
}
|
|
|
|
|
2014-02-22 19:54:43 +01:00
|
|
|
private void handleLeavePushGroup() {
|
2017-08-01 17:56:00 +02:00
|
|
|
if (getRecipient() == null) {
|
2014-02-22 19:54:43 +01: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 19:54:43 +01: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 19:54:43 +01:00
|
|
|
builder.setCancelable(true);
|
2021-01-13 06:13:49 +01:00
|
|
|
|
|
|
|
GroupRecord group = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()).orNull();
|
|
|
|
List<Address> admins = group.getAdmins();
|
|
|
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
|
|
|
String message = getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group);
|
|
|
|
for (Address admin : admins) {
|
2021-01-15 05:36:30 +01:00
|
|
|
if (admin.toString().equals(userPublicKey)) {
|
2021-01-13 06:13:49 +01:00
|
|
|
message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.setMessage(message);
|
2017-12-05 20:52:03 +01:00
|
|
|
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
|
2020-08-11 04:20:17 +02:00
|
|
|
Recipient groupRecipient = getRecipient();
|
2020-08-12 06:06:18 +02:00
|
|
|
String groupPublicKey;
|
2021-02-18 04:14:05 +01:00
|
|
|
boolean isClosedGroup;
|
2020-08-12 06:06:18 +02:00
|
|
|
try {
|
2021-02-17 06:09:36 +01:00
|
|
|
groupPublicKey = HexEncodingKt.toHexString(GroupUtil.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
|
2021-02-18 04:14:05 +01:00
|
|
|
isClosedGroup = DatabaseFactory.getLokiAPIDatabase(this).isClosedGroup(groupPublicKey);
|
2020-08-12 06:06:18 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
groupPublicKey = null;
|
2021-02-18 04:14:05 +01:00
|
|
|
isClosedGroup = false;
|
2020-08-12 06:06:18 +02:00
|
|
|
}
|
2020-08-11 04:20:17 +02:00
|
|
|
try {
|
2021-02-18 04:14:05 +01:00
|
|
|
if (isClosedGroup) {
|
2021-03-26 05:46:37 +01:00
|
|
|
MessageSender.explicitLeave(groupPublicKey, true);
|
2020-08-11 04:20:17 +02:00
|
|
|
initializeEnabledCheck();
|
|
|
|
} else {
|
|
|
|
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
2018-09-10 17:40:00 +02:00
|
|
|
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
2014-02-22 19:54:43 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
builder.setNegativeButton(R.string.no, null);
|
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleEditPushGroup() {
|
2020-06-09 05:58:01 +02:00
|
|
|
Intent intent = new Intent(this, EditClosedGroupActivity.class);
|
2020-06-10 08:04:07 +02:00
|
|
|
String groupID = this.recipient.getAddress().toGroupString();
|
2020-08-18 01:34:22 +02:00
|
|
|
intent.putExtra(EditClosedGroupActivity.Companion.getGroupIDKey(), groupID);
|
2020-06-09 05:58:01 +02:00
|
|
|
startActivity(intent);
|
2014-02-22 19:54:43 +01:00
|
|
|
}
|
|
|
|
|
2013-04-26 03:59:49 +02:00
|
|
|
private void handleDistributionBroadcastEnabled(MenuItem item) {
|
2021-03-02 02:24:09 +01:00
|
|
|
distributionType = DistributionTypes.BROADCAST;
|
2013-04-26 03:59:49 +02:00
|
|
|
item.setChecked(true);
|
|
|
|
|
|
|
|
if (threadId != -1) {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
2021-03-02 02:24:09 +01:00
|
|
|
.setDistributionType(threadId, DistributionTypes.BROADCAST);
|
2013-04-26 03:59:49 +02:00
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleDistributionConversationEnabled(MenuItem item) {
|
2021-03-02 02:24:09 +01:00
|
|
|
distributionType = DistributionTypes.CONVERSATION;
|
2013-04-26 03:59:49 +02:00
|
|
|
item.setChecked(true);
|
|
|
|
|
|
|
|
if (threadId != -1) {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
2021-03-02 02:24:09 +01:00
|
|
|
.setDistributionType(threadId, DistributionTypes.CONVERSATION);
|
2013-04-26 03:59:49 +02:00
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private void handleAddAttachment() {
|
2021-02-22 06:46:09 +01:00
|
|
|
if (attachmentTypeSelector == null) {
|
|
|
|
attachmentTypeSelector = new AttachmentTypeSelector(
|
|
|
|
this,
|
|
|
|
LoaderManager.getInstance(this),
|
|
|
|
new AttachmentTypeListener(),
|
|
|
|
keyboardHeight);
|
2013-03-05 02:43:04 +01:00
|
|
|
}
|
2021-02-22 06:46:09 +01:00
|
|
|
attachmentTypeSelector.show(this, attachButton);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2017-03-14 21:24:24 +01:00
|
|
|
private void handleSecurityChange(boolean isSecureText, boolean isDefaultSms) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "handleSecurityChange(" + isSecureText + ", " + isDefaultSms + ")");
|
2020-12-07 07:40:47 +01:00
|
|
|
if (isSecurityInitialized && isSecureText == true && isDefaultSms == this.isDefaultSms) {
|
2017-09-07 02:54:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.isDefaultSms = isDefaultSms;
|
|
|
|
this.isSecurityInitialized = true;
|
2015-09-29 23:26:37 +02:00
|
|
|
|
2020-07-20 01:20:25 +02:00
|
|
|
if (recipient == null || attachmentManager == null) { return; }
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent();
|
2015-09-24 00:47:48 +02:00
|
|
|
|
2019-12-15 23:17:31 +01:00
|
|
|
/* Loki - We don't support SMS
|
2017-12-26 00:57:33 +01:00
|
|
|
if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE);
|
|
|
|
if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS);
|
2015-09-24 00:47:48 +02:00
|
|
|
|
2019-05-10 17:13:59 +02:00
|
|
|
if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) {
|
2019-04-12 21:22:38 +02:00
|
|
|
sendButton.setDefaultTransport(Type.SMS);
|
|
|
|
} else {
|
|
|
|
if (isSecureText || isPushGroupConversation()) sendButton.setDefaultTransport(Type.TEXTSECURE);
|
|
|
|
else sendButton.setDefaultTransport(Type.SMS);
|
|
|
|
}
|
2019-12-15 23:17:31 +01:00
|
|
|
*/
|
2015-09-24 00:47:48 +02:00
|
|
|
|
|
|
|
supportInvalidateOptionsMenu();
|
2020-12-07 07:40:47 +01:00
|
|
|
updateInputUI(recipient);
|
2015-09-24 00:47:48 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
///// Initializers
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private ListenableFuture<Boolean> initializeDraft() {
|
|
|
|
final SettableFuture<Boolean> result = new SettableFuture<>();
|
|
|
|
|
2019-04-17 16:21:30 +02: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);
|
2018-11-20 18:59:23 +01:00
|
|
|
|
|
|
|
if (!Util.isEmpty(mediaList)) {
|
2020-12-07 07:40:47 +01:00
|
|
|
Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient, draftText);
|
2018-11-20 18:59:23 +01:00
|
|
|
startActivityForResult(sendIntent, MEDIA_SENDER);
|
|
|
|
return new SettableFuture<>(false);
|
|
|
|
}
|
2018-07-25 17:30:48 +02:00
|
|
|
|
|
|
|
if (draftText != null) {
|
2019-04-17 16:21:30 +02:00
|
|
|
composeText.setText("");
|
|
|
|
composeText.append(draftText);
|
2018-07-25 17:30:48 +02:00
|
|
|
result.set(true);
|
|
|
|
}
|
2018-11-20 18:59:23 +01:00
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
if (draftMedia != null && draftMediaType != null) {
|
|
|
|
return setMedia(draftMedia, draftMediaType);
|
|
|
|
}
|
2015-09-05 02:33:22 +02:00
|
|
|
|
2015-11-18 21:54:40 +01:00
|
|
|
if (draftText == null && draftMedia == null && draftMediaType == null) {
|
2018-07-25 17:30:48 +02:00
|
|
|
return initializeDraftFromDatabase();
|
2015-05-18 19:26:32 +02:00
|
|
|
} else {
|
|
|
|
updateToggleButtonState();
|
2018-07-25 17:30:48 +02:00
|
|
|
result.set(false);
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
2018-07-25 17:30:48 +02:00
|
|
|
|
|
|
|
return result;
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-22 19:54:43 +01:00
|
|
|
private void initializeEnabledCheck() {
|
2014-02-23 23:37:41 +01:00
|
|
|
boolean enabled = !(isPushGroupConversation() && !isActiveGroup());
|
2015-11-22 19:44:57 +01:00
|
|
|
inputPanel.setEnabled(enabled);
|
2014-02-22 19:54:43 +01:00
|
|
|
sendButton.setEnabled(enabled);
|
2015-11-22 19:44:57 +01:00
|
|
|
attachButton.setEnabled(enabled);
|
2014-02-22 19:54:43 +01:00
|
|
|
}
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private ListenableFuture<Boolean> initializeDraftFromDatabase() {
|
|
|
|
SettableFuture<Boolean> future = new SettableFuture<>();
|
|
|
|
|
2013-02-04 09:13:07 +01:00
|
|
|
new AsyncTask<Void, Void, List<Draft>>() {
|
|
|
|
@Override
|
|
|
|
protected List<Draft> doInBackground(Void... params) {
|
|
|
|
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
|
2018-01-25 04:17:44 +01:00
|
|
|
List<Draft> results = draftDatabase.getDrafts(threadId);
|
2013-02-04 09:13:07 +01:00
|
|
|
|
|
|
|
draftDatabase.clearDrafts(threadId);
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(List<Draft> drafts) {
|
2018-10-29 23:14:31 +01:00
|
|
|
if (drafts.isEmpty()) {
|
|
|
|
future.set(false);
|
|
|
|
updateToggleButtonState();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-25 17:30:48 +02: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 09:13:07 +01:00
|
|
|
for (Draft draft : drafts) {
|
2021-02-22 03:30:48 +01:00
|
|
|
switch (draft.getType()) {
|
|
|
|
case Draft.TEXT:
|
|
|
|
composeText.setText(draft.getValue());
|
|
|
|
listener.onSuccess(true);
|
|
|
|
break;
|
|
|
|
case Draft.IMAGE:
|
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE).addListener(listener);
|
|
|
|
break;
|
|
|
|
case Draft.AUDIO:
|
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO).addListener(listener);
|
|
|
|
break;
|
|
|
|
case Draft.VIDEO:
|
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO).addListener(listener);
|
|
|
|
break;
|
|
|
|
case Draft.QUOTE:
|
|
|
|
SettableFuture<Boolean> quoteResult = new SettableFuture<>();
|
|
|
|
new QuoteRestorationTask(draft.getValue(), quoteResult).execute();
|
|
|
|
quoteResult.addListener(listener);
|
|
|
|
break;
|
2014-05-22 23:00:53 +02:00
|
|
|
}
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
2015-05-18 19:26:32 +02:00
|
|
|
|
|
|
|
updateToggleButtonState();
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2018-07-25 17:30:48 +02:00
|
|
|
|
|
|
|
return future;
|
2013-02-03 05:37:40 +01:00
|
|
|
}
|
|
|
|
|
2015-10-02 02:46:47 +02:00
|
|
|
private ListenableFuture<Boolean> initializeSecurity(final boolean currentSecureText,
|
2017-01-18 20:01:13 +01:00
|
|
|
final boolean currentIsDefaultSms)
|
2015-09-29 23:26:37 +02:00
|
|
|
{
|
2015-10-02 02:46:47 +02:00
|
|
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
|
|
|
|
2017-03-14 21:24:24 +01:00
|
|
|
handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms);
|
2015-09-22 02:41:27 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
new AsyncTask<Recipient, Void, boolean[]>() {
|
2015-09-22 02:41:27 +02:00
|
|
|
@Override
|
2017-08-01 17:56:00 +02:00
|
|
|
protected boolean[] doInBackground(Recipient... params) {
|
2019-06-07 05:54:50 +02:00
|
|
|
// Loki - Override the flag below
|
|
|
|
boolean signalEnabled = true; // TextSecurePreferences.isPushRegistered(context);
|
2015-09-29 23:26:37 +02:00
|
|
|
|
2017-02-01 06:46:20 +01:00
|
|
|
|
2020-03-12 23:35:47 +01:00
|
|
|
return new boolean[] { signalEnabled, false};
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-01-18 20:01:13 +01:00
|
|
|
protected void onPostExecute(boolean[] result) {
|
2017-03-14 21:24:24 +01:00
|
|
|
if (result[0] != currentSecureText || result[1] != currentIsDefaultSms) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onPostExecute() handleSecurityChange: " + result[0] + " , " + result[1]);
|
2017-03-14 21:24:24 +01:00
|
|
|
handleSecurityChange(result[0], result[1]);
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
2015-10-02 02:46:47 +02:00
|
|
|
future.set(true);
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
|
2015-10-02 02:46:47 +02:00
|
|
|
|
|
|
|
return future;
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
|
|
|
|
2014-12-25 03:32:51 +01:00
|
|
|
private void initializeViews() {
|
2020-09-07 08:13:34 +02:00
|
|
|
profilePictureView = findViewById(R.id.profilePictureView);
|
2020-05-14 02:44:13 +02:00
|
|
|
titleTextView = findViewById(R.id.titleTextView);
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
container = ViewUtil.findById(this, R.id.layout_container);
|
|
|
|
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);
|
|
|
|
searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer);
|
2020-05-14 02:44:13 +02:00
|
|
|
mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
|
|
|
|
messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
|
|
|
|
muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
|
|
|
|
subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
|
2020-09-17 08:56:26 +02:00
|
|
|
homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer);
|
2018-08-10 18:18:02 +02:00
|
|
|
|
|
|
|
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
|
|
|
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
|
2015-04-16 07:38:33 +02:00
|
|
|
|
2015-07-25 16:58:51 +02:00
|
|
|
container.addOnKeyboardShownListener(this);
|
2017-01-19 20:31:41 +01:00
|
|
|
inputPanel.setListener(this);
|
2016-12-27 00:14:23 +01:00
|
|
|
inputPanel.setMediaListener(this);
|
2015-07-25 16:58:51 +02:00
|
|
|
|
2017-01-19 03:46:40 +01:00
|
|
|
attachmentTypeSelector = null;
|
2015-10-28 17:47:09 +01:00
|
|
|
attachmentManager = new AttachmentManager(this, this);
|
2018-01-25 04:17:44 +01:00
|
|
|
audioRecorder = new AudioRecorder(this);
|
2018-10-29 23:14:31 +01:00
|
|
|
typingTextWatcher = new TypingStatusTextWatcher();
|
2019-10-10 05:44:08 +02:00
|
|
|
mentionTextWatcher = new MentionTextWatcher();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2013-06-28 05:57:27 +02:00
|
|
|
SendButtonListener sendButtonListener = new SendButtonListener();
|
|
|
|
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2015-07-21 04:25:54 +02:00
|
|
|
composeText.setOnEditorActionListener(sendButtonListener);
|
2019-02-14 22:55:48 +01:00
|
|
|
composeText.setCursorPositionChangedListener(this);
|
2015-05-18 19:26:32 +02:00
|
|
|
attachButton.setOnClickListener(new AttachButtonListener());
|
2015-07-26 23:49:43 +02:00
|
|
|
attachButton.setOnLongClickListener(new AttachButtonLongClickListener());
|
2012-07-19 23:22:03 +02:00
|
|
|
sendButton.setOnClickListener(sendButtonListener);
|
2013-03-14 01:45:32 +01:00
|
|
|
sendButton.setEnabled(true);
|
2015-02-16 11:38:09 +01:00
|
|
|
|
2017-09-13 07:48:30 +02:00
|
|
|
unblockButton.setOnClickListener(v -> handleUnblock());
|
|
|
|
makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms());
|
2016-11-21 00:56:47 +01:00
|
|
|
|
2013-06-28 05:57:27 +02:00
|
|
|
composeText.setOnKeyListener(composeKeyPressedListener);
|
|
|
|
composeText.addTextChangedListener(composeKeyPressedListener);
|
2012-07-19 23:22:03 +02:00
|
|
|
composeText.setOnEditorActionListener(sendButtonListener);
|
2013-06-28 05:57:27 +02:00
|
|
|
composeText.setOnClickListener(composeKeyPressedListener);
|
2014-05-29 05:53:34 +02:00
|
|
|
composeText.setOnFocusChangeListener(composeKeyPressedListener);
|
2015-04-16 07:38:33 +02:00
|
|
|
|
2019-02-26 00:21:37 +01:00
|
|
|
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) && Camera.getNumberOfCameras() > 0) {
|
|
|
|
quickCameraToggle.setVisibility(View.VISIBLE);
|
2015-11-18 23:52:26 +01:00
|
|
|
quickCameraToggle.setOnClickListener(new QuickCameraToggleListener());
|
2015-04-16 07:38:33 +02:00
|
|
|
} else {
|
2015-11-18 23:52:26 +01:00
|
|
|
quickCameraToggle.setVisibility(View.GONE);
|
2015-04-16 07:38:33 +02:00
|
|
|
}
|
2018-08-10 18:18:02 +02:00
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
searchNav.setEventListener(this);
|
|
|
|
|
2018-08-10 18:18:02 +02:00
|
|
|
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
|
2020-09-17 08:56:26 +02:00
|
|
|
|
|
|
|
homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp());
|
2014-12-25 03:32:51 +01:00
|
|
|
}
|
|
|
|
|
2015-06-22 17:46:43 +02:00
|
|
|
protected void initializeActionBar() {
|
2019-05-24 19:31:48 +02:00
|
|
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
|
|
setSupportActionBar(toolbar);
|
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
ActionBar supportActionBar = getSupportActionBar();
|
|
|
|
if (supportActionBar == null) throw new AssertionError();
|
|
|
|
|
2020-01-10 01:35:16 +01:00
|
|
|
// supportActionBar.setDisplayHomeAsUpEnabled(true);
|
2017-12-05 20:52:03 +01:00
|
|
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
2015-06-09 16:37:20 +02:00
|
|
|
}
|
|
|
|
|
2014-12-25 03:32:51 +01:00
|
|
|
private void initializeResources() {
|
2017-08-01 17:56:00 +02:00
|
|
|
if (recipient != null) recipient.removeListener(this);
|
2018-08-06 20:42:22 +02:00
|
|
|
|
2020-06-23 04:17:49 +02:00
|
|
|
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
|
|
|
if (address == null) { finish(); return; }
|
|
|
|
recipient = Recipient.from(this, address, true);
|
2014-12-25 03:32:51 +01:00
|
|
|
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
|
2021-03-02 02:24:09 +01:00
|
|
|
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT);
|
2017-10-16 22:11:42 +02:00
|
|
|
glideRequests = GlideApp.with(this);
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
recipient.addListener(this);
|
2015-02-10 12:15:50 +01:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
private void initializeLinkPreviewObserver() {
|
2019-04-17 16:21:30 +02:00
|
|
|
linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository(this))).get(LinkPreviewViewModel.class);
|
2019-01-15 09:41:05 +01: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 18:06:59 +01: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());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSearchMoveUpPressed() {
|
|
|
|
searchViewModel.onMoveUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSearchMoveDownPressed() {
|
|
|
|
searchViewModel.onMoveDown();
|
|
|
|
}
|
2019-01-15 09:41:05 +01:00
|
|
|
|
2015-02-10 12:15:50 +01:00
|
|
|
@Override
|
2017-08-01 17:56:00 +02:00
|
|
|
public void onModified(final Recipient recipient) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
|
2017-10-04 23:56:31 +02:00
|
|
|
Util.runOnMain(() -> {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
|
2020-02-12 23:28:00 +01:00
|
|
|
updateTitleTextView(recipient);
|
2020-09-07 08:13:34 +02:00
|
|
|
updateProfilePicture();
|
2020-01-17 00:37:06 +01:00
|
|
|
updateSubtitleTextView();
|
2020-12-07 07:40:47 +01:00
|
|
|
updateInputUI(recipient);
|
|
|
|
initializeSecurity(true, isDefaultSms);
|
2019-02-01 18:06:59 +01:00
|
|
|
|
2019-02-16 04:24:23 +01:00
|
|
|
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
|
2019-02-01 18:06:59 +01:00
|
|
|
invalidateOptionsMenu();
|
|
|
|
}
|
2015-06-11 21:53:38 +02:00
|
|
|
});
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2014-02-16 03:40:08 +01:00
|
|
|
|
2020-11-20 08:59:13 +01:00
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
|
|
public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
|
2021-04-27 06:26:26 +02:00
|
|
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
2020-11-20 08:59:13 +01:00
|
|
|
if (publicChat != null &&
|
|
|
|
publicChat.getChannel() == event.getChannel() &&
|
|
|
|
publicChat.getServer().equals(event.getUrl())) {
|
|
|
|
this.updateSubtitleTextView();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
//////// Helper Methods
|
|
|
|
|
|
|
|
private void addAttachment(int type) {
|
2019-01-15 09:41:05 +01:00
|
|
|
linkPreviewViewModel.onUserCancel();
|
|
|
|
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Selected: " + type);
|
2012-07-19 23:22:03 +02:00
|
|
|
switch (type) {
|
2017-04-19 01:33:03 +02:00
|
|
|
case AttachmentTypeSelector.ADD_GALLERY:
|
2020-12-07 07:40:47 +01:00
|
|
|
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break;
|
2017-04-19 01:33:03 +02:00
|
|
|
case AttachmentTypeSelector.ADD_DOCUMENT:
|
|
|
|
AttachmentManager.selectDocument(this, PICK_DOCUMENT); break;
|
|
|
|
case AttachmentTypeSelector.ADD_SOUND:
|
2012-07-19 23:22:03 +02:00
|
|
|
AttachmentManager.selectAudio(this, PICK_AUDIO); break;
|
2017-04-19 01:33:03 +02:00
|
|
|
case AttachmentTypeSelector.ADD_CONTACT_INFO:
|
2018-04-27 02:03:54 +02:00
|
|
|
AttachmentManager.selectContactInfo(this, PICK_CONTACT); break;
|
2015-12-18 23:37:11 +01:00
|
|
|
case AttachmentTypeSelector.ADD_LOCATION:
|
|
|
|
AttachmentManager.selectLocation(this, PICK_LOCATION); break;
|
2017-04-19 01:33:03 +02:00
|
|
|
case AttachmentTypeSelector.TAKE_PHOTO:
|
2015-11-24 02:56:41 +01:00
|
|
|
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
|
2016-10-17 04:05:07 +02:00
|
|
|
case AttachmentTypeSelector.ADD_GIF:
|
2020-05-19 01:21:39 +02:00
|
|
|
boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this);
|
|
|
|
if (!hasSeenGIFMetaDataWarning) {
|
|
|
|
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) -> {
|
2020-12-07 07:40:47 +01:00
|
|
|
AttachmentManager.selectGif(this, PICK_GIF);
|
2020-05-19 01:21:39 +02:00
|
|
|
dialog.dismiss();
|
|
|
|
});
|
|
|
|
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
|
|
|
|
builder.create().show();
|
|
|
|
TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
|
|
|
|
} else {
|
2020-12-07 07:40:47 +01:00
|
|
|
AttachmentManager.selectGif(this, PICK_GIF);
|
2020-05-19 01:21:39 +02:00
|
|
|
}
|
2020-02-01 05:12:06 +01:00
|
|
|
break;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
|
|
|
|
return setMedia(uri, mediaType, 0, 0);
|
2018-03-20 19:27:11 +01:00
|
|
|
}
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height) {
|
|
|
|
if (uri == null) {
|
|
|
|
return new SettableFuture<>(false);
|
|
|
|
}
|
2018-05-17 08:40:14 +02:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
if (MediaType.VCARD.equals(mediaType)) {
|
2018-07-25 17:30:48 +02:00
|
|
|
return new SettableFuture<>(false);
|
2018-11-20 18:59:23 +01:00
|
|
|
} else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) {
|
2019-02-12 00:05:37 +01:00
|
|
|
Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, Optional.absent(), Optional.absent());
|
2020-12-07 07:40:47 +01:00
|
|
|
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed()), MEDIA_SENDER);
|
2018-11-20 18:59:23 +01:00
|
|
|
return new SettableFuture<>(false);
|
2018-06-29 20:08:13 +02:00
|
|
|
} else {
|
2020-12-07 07:40:47 +01:00
|
|
|
return attachmentManager.setMedia(glideRequests, uri, mediaType, MediaConstraints.getPushMediaConstraints(), width, height);
|
2018-06-29 20:08:13 +02:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void selectContactInfo(ContactData contactData) {
|
2014-12-13 03:31:20 +01: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-13 03:31:20 +01:00
|
|
|
|
2013-10-17 02:28:36 +02:00
|
|
|
for (int i = 0; i < contactData.numbers.size(); i++) {
|
2014-12-13 03:31:20 +01: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 20:52:03 +01:00
|
|
|
builder.setItems(numberItems, (dialog, which) -> composeText.append(numbers[which]));
|
2013-10-17 02:28:36 +02:00
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
|
2014-12-12 02:13:01 +01:00
|
|
|
private Drafts getDraftsForCurrentState() {
|
|
|
|
Drafts drafts = new Drafts();
|
2013-02-04 09:13:07 +01:00
|
|
|
|
2021-02-02 05:10:23 +01:00
|
|
|
if (!org.thoughtcrime.securesms.util.Util.isEmpty(composeText)) {
|
2017-07-04 19:09:36 +02:00
|
|
|
drafts.add(new Draft(Draft.TEXT, composeText.getTextTrimmed()));
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
|
|
|
|
2015-10-15 23:40:45 +02:00
|
|
|
for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) {
|
2016-12-11 22:37:27 +01: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.hasImage() && slide.getUri() != null) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 20:09:54 +02: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 09:13:07 +01:00
|
|
|
return drafts;
|
|
|
|
}
|
|
|
|
|
2015-06-22 17:46:43 +02:00
|
|
|
protected ListenableFuture<Long> saveDraft() {
|
|
|
|
final SettableFuture<Long> future = new SettableFuture<>();
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
if (this.recipient == null) {
|
2015-06-22 17:46:43 +02:00
|
|
|
future.set(threadId);
|
|
|
|
return future;
|
|
|
|
}
|
2013-02-04 09:13:07 +01: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 09:13:07 +01:00
|
|
|
|
2015-06-22 17:46:43 +02:00
|
|
|
new AsyncTask<Long, Void, Long>() {
|
2013-02-04 09:13:07 +01:00
|
|
|
@Override
|
2015-06-22 17:46:43 +02:00
|
|
|
protected Long doInBackground(Long... params) {
|
2014-12-12 02:13:01 +01: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-12 02:13:01 +01:00
|
|
|
if (drafts.size() > 0) {
|
2020-11-20 08:59:13 +01:00
|
|
|
if (threadId == -1) threadId = threadDatabase.getOrCreateThreadIdFor(getRecipient(), thisDistributionType);
|
2015-01-04 00:25:35 +01:00
|
|
|
|
2018-01-25 04:17:44 +01:00
|
|
|
draftDatabase.insertDrafts(threadId, drafts);
|
2015-10-16 22:59:40 +02:00
|
|
|
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
|
2018-01-25 04:17:44 +01:00
|
|
|
drafts.getUriSnippet(),
|
2015-11-24 00:07:41 +01:00
|
|
|
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
|
2015-01-04 00:25:35 +01:00
|
|
|
} else if (threadId > 0) {
|
2015-11-24 00:07:41 +01:00
|
|
|
threadDatabase.update(threadId, false);
|
2014-12-12 02:13:01 +01:00
|
|
|
}
|
2015-06-22 17:46:43 +02:00
|
|
|
|
|
|
|
return threadId;
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
2015-06-22 17:46:43 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Long result) {
|
|
|
|
future.set(result);
|
|
|
|
}
|
|
|
|
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, thisThreadId);
|
2015-06-22 17:46:43 +02:00
|
|
|
|
|
|
|
return future;
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
private void updateInputUI(Recipient recipient) {
|
2020-04-09 05:23:51 +02:00
|
|
|
if (recipient.isGroupRecipient() && !isActiveGroup()) {
|
2019-08-28 08:07:20 +02:00
|
|
|
unblockButton.setVisibility(View.GONE);
|
2020-04-09 05:23:51 +02:00
|
|
|
inputPanel.setVisibility(View.GONE);
|
2019-08-28 08:07:20 +02:00
|
|
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
|
|
|
} else if (recipient.isBlocked()) {
|
2015-06-09 16:37:20 +02:00
|
|
|
unblockButton.setVisibility(View.VISIBLE);
|
2020-04-09 05:23:51 +02:00
|
|
|
inputPanel.setVisibility(View.GONE);
|
2016-11-21 00:56:47 +01:00
|
|
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
2015-06-09 16:37:20 +02:00
|
|
|
} else {
|
2020-04-09 05:23:51 +02:00
|
|
|
inputPanel.setVisibility(View.VISIBLE);
|
2015-06-09 16:37:20 +02:00
|
|
|
unblockButton.setVisibility(View.GONE);
|
2016-11-21 00:56:47 +01:00
|
|
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
2015-06-09 16:37:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 00:06:40 +01:00
|
|
|
private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard) {
|
2019-04-17 16:21:30 +02:00
|
|
|
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
|
2021-02-22 00:06:40 +01:00
|
|
|
if (!isSystemEmojiPreferred) {
|
2019-04-17 16:21:30 +02:00
|
|
|
mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-02 02:39:36 +02:00
|
|
|
private boolean isSingleConversation() {
|
2017-08-01 17:56:00 +02:00
|
|
|
return getRecipient() != null && !getRecipient().isGroupRecipient();
|
2012-10-21 23:34:09 +02:00
|
|
|
}
|
|
|
|
|
2014-02-22 19:54:43 +01:00
|
|
|
private boolean isActiveGroup() {
|
2021-01-15 05:36:30 +01:00
|
|
|
if (!isGroupConversation()) return false;
|
2014-02-22 19:54:43 +01:00
|
|
|
|
2017-08-08 01:47:38 +02:00
|
|
|
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
|
|
|
|
return record.isPresent() && record.get().isActive();
|
2014-02-22 19:54:43 +01:00
|
|
|
}
|
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
@SuppressWarnings("SimplifiableIfStatement")
|
2015-10-01 20:14:00 +02:00
|
|
|
private boolean isSelfConversation() {
|
2017-08-01 17:56:00 +02:00
|
|
|
if (!TextSecurePreferences.isPushRegistered(this)) return false;
|
2017-12-05 20:52:03 +01:00
|
|
|
if (recipient.isGroupRecipient()) return false;
|
2015-10-01 20:14:00 +02:00
|
|
|
|
2021-02-04 00:28:51 +01:00
|
|
|
return Util.isOwnNumber(this, recipient.getAddress().serialize());
|
2015-10-01 20:14:00 +02:00
|
|
|
}
|
|
|
|
|
2012-10-21 23:34:09 +02:00
|
|
|
private boolean isGroupConversation() {
|
2017-08-01 17:56:00 +02:00
|
|
|
return getRecipient() != null && getRecipient().isGroupRecipient();
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2014-02-18 05:25:40 +01:00
|
|
|
private boolean isPushGroupConversation() {
|
2017-08-01 17:56:00 +02:00
|
|
|
return getRecipient() != null && getRecipient().isPushGroupRecipient();
|
2014-02-18 05:25:40 +01:00
|
|
|
}
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
protected Recipient getRecipient() {
|
|
|
|
return this.recipient;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2015-06-22 17:46:43 +02:00
|
|
|
protected long getThreadId() {
|
|
|
|
return this.threadId;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private String getMessage() throws InvalidMessageException {
|
2019-10-11 02:13:34 +02:00
|
|
|
String result = composeText.getTextTrimmed();
|
|
|
|
if (result.length() < 1 && !attachmentManager.isAttachmentPresent()) throw new InvalidMessageException();
|
|
|
|
for (Mention mention : mentions) {
|
2019-10-11 05:59:13 +02:00
|
|
|
try {
|
2019-10-11 07:37:28 +02:00
|
|
|
int startIndex = result.indexOf("@" + mention.getDisplayName());
|
2019-10-11 05:59:13 +02:00
|
|
|
int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @
|
2020-07-15 04:24:43 +02:00
|
|
|
result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex);
|
2019-10-11 05:59:13 +02:00
|
|
|
} catch (Exception exception) {
|
2019-10-11 07:37:28 +02:00
|
|
|
Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + ".");
|
2019-10-11 05:59:13 +02:00
|
|
|
}
|
2019-10-11 02:13:34 +02:00
|
|
|
}
|
|
|
|
return result;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2019-02-27 04:29:52 +01:00
|
|
|
private Pair<String, Optional<Slide>> getSplitMessage(String rawText, int maxPrimaryMessageSize) {
|
|
|
|
String bodyText = rawText;
|
2019-03-02 03:14:47 +01:00
|
|
|
Optional<Slide> textSlide = Optional.absent();
|
2019-02-27 04:29:52 +01:00
|
|
|
|
|
|
|
if (bodyText.length() > maxPrimaryMessageSize) {
|
|
|
|
bodyText = rawText.substring(0, maxPrimaryMessageSize);
|
|
|
|
|
2019-03-02 03:14:47 +01:00
|
|
|
byte[] textData = rawText.getBytes();
|
2019-02-27 04:29:52 +01:00
|
|
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date());
|
|
|
|
String filename = String.format("signal-%s.txt", timestamp);
|
2019-02-26 02:47:30 +01:00
|
|
|
Uri textUri = BlobProvider.getInstance()
|
|
|
|
.forData(textData)
|
2021-02-01 07:00:32 +01:00
|
|
|
.withMimeType(MediaTypes.LONG_TEXT)
|
2019-02-26 02:47:30 +01:00
|
|
|
.withFileName(filename)
|
|
|
|
.createForSingleSessionInMemory();
|
2019-02-27 04:29:52 +01:00
|
|
|
|
2019-03-02 03:14:47 +01:00
|
|
|
textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length));
|
2019-02-27 04:29:52 +01:00
|
|
|
}
|
|
|
|
|
2019-03-02 03:14:47 +01:00
|
|
|
return new Pair<>(bodyText, textSlide);
|
2019-02-27 04:29:52 +01:00
|
|
|
}
|
|
|
|
|
2013-02-04 03:41:34 +01:00
|
|
|
private void markThreadAsRead() {
|
2020-08-12 06:06:18 +02:00
|
|
|
Recipient recipient = this.recipient;
|
2013-02-04 03:41:34 +01:00
|
|
|
new AsyncTask<Long, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Long... params) {
|
2016-10-10 20:13:37 +02:00
|
|
|
Context context = ConversationActivity.this;
|
2017-02-23 00:05:35 +01:00
|
|
|
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false);
|
2016-02-20 02:07:41 +01:00
|
|
|
|
2021-03-05 00:17:34 +01:00
|
|
|
if (!SessionMetaProtocol.shouldSendReadReceipt(recipient.getAddress())) {
|
2020-08-12 06:06:18 +02:00
|
|
|
for (MarkedMessageInfo messageInfo : messageIds) {
|
|
|
|
MarkReadReceiver.scheduleDeletion(context, messageInfo.getExpirationInfo());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MarkReadReceiver.process(context, messageIds);
|
|
|
|
}
|
2020-09-25 02:55:13 +02:00
|
|
|
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
2013-02-04 03:41:34 +01:00
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
|
2013-02-04 03:41:34 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 07:35:47 +01: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 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
|
2017-02-14 07:35:47 +01:00
|
|
|
}
|
|
|
|
|
2015-06-22 17:46:43 +02:00
|
|
|
protected void sendComplete(long threadId) {
|
2014-11-08 20:35:58 +01:00
|
|
|
boolean refreshFragment = (threadId != this.threadId);
|
|
|
|
this.threadId = threadId;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2015-04-09 02:43:14 +02:00
|
|
|
if (fragment == null || !fragment.isVisible() || isFinishing()) {
|
2014-11-25 07:48:50 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-14 07:35:47 +01:00
|
|
|
fragment.setLastSeen(0);
|
|
|
|
|
2014-02-18 00:47:58 +01:00
|
|
|
if (refreshFragment) {
|
2017-08-01 17:56:00 +02:00
|
|
|
fragment.reload(recipient, threadId);
|
2020-06-26 08:18:19 +02:00
|
|
|
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2014-11-08 20:35:58 +01:00
|
|
|
|
2014-03-26 23:11:56 +01:00
|
|
|
fragment.scrollToBottom();
|
2015-05-18 19:26:32 +02:00
|
|
|
attachmentManager.cleanup();
|
2019-01-15 09:41:05 +01:00
|
|
|
|
|
|
|
updateLinkPreviewState();
|
2019-06-25 03:48:39 +02:00
|
|
|
}
|
|
|
|
|
2014-07-19 04:31:03 +02:00
|
|
|
private void sendMessage() {
|
2019-03-28 19:04:38 +01:00
|
|
|
if (inputPanel.isRecordingInLockedMode()) {
|
|
|
|
inputPanel.releaseRecordingLock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
try {
|
2017-08-01 17:56:00 +02:00
|
|
|
Recipient recipient = getRecipient();
|
2016-11-28 18:14:44 +01:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
if (recipient == null) {
|
2016-11-28 18:14:44 +01:00
|
|
|
throw new RecipientFormattingException("Badly formatted");
|
|
|
|
}
|
|
|
|
|
2019-03-06 17:12:00 +01:00
|
|
|
String message = getMessage();
|
|
|
|
boolean initiating = threadId == -1;
|
2020-12-07 07:40:47 +01:00
|
|
|
boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize;
|
2019-09-17 03:46:47 +02:00
|
|
|
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
|
|
|
recipient.isGroupRecipient() ||
|
|
|
|
inputPanel.getQuote().isPresent() ||
|
|
|
|
linkPreviewViewModel.hasLinkPreview() ||
|
2020-11-06 04:49:23 +01:00
|
|
|
LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages
|
2019-03-06 17:12:00 +01:00
|
|
|
needsSplit;
|
2015-03-11 22:23:45 +01:00
|
|
|
|
2020-12-14 02:16:30 +01:00
|
|
|
if (isMediaMessage) {
|
2021-03-02 02:24:09 +01:00
|
|
|
sendMediaMessage(initiating);
|
2012-07-19 23:22:03 +02:00
|
|
|
} else {
|
2021-03-02 02:24:09 +01:00
|
|
|
sendTextMessage(initiating);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
} catch (RecipientFormattingException ex) {
|
2014-02-18 05:25:40 +01:00
|
|
|
Log.w(TAG, ex);
|
2012-07-19 23:22:03 +02:00
|
|
|
} catch (InvalidMessageException ex) {
|
2014-02-18 05:25:40 +01:00
|
|
|
Log.w(TAG, ex);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2020-03-17 01:02:05 +01:00
|
|
|
|
2021-02-19 01:03:58 +01:00
|
|
|
if (messageStatus == null && !isGroupConversation() && !(TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize()))) {
|
2020-03-17 01:02:05 +01:00
|
|
|
messageStatus = "calculatingPoW";
|
|
|
|
updateSubtitleTextView();
|
|
|
|
updateMessageStatusProgressBar();
|
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
private void sendMediaMessage(boolean initiating)
|
2014-11-08 20:35:58 +01:00
|
|
|
throws InvalidMessageException
|
2014-06-12 00:31:59 +02:00
|
|
|
{
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Sending media message...");
|
2021-03-02 02:24:09 +01:00
|
|
|
sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating);
|
2015-11-18 23:52:26 +01:00
|
|
|
}
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
private ListenableFuture<Void> sendMediaMessage(String body,
|
2019-01-15 09:41:05 +01:00
|
|
|
SlideDeck slideDeck,
|
2019-04-17 16:21:30 +02:00
|
|
|
QuoteModel quote,
|
2021-03-02 02:24:09 +01:00
|
|
|
Optional<LinkPreview> linkPreview,
|
|
|
|
final boolean initiating)
|
2019-01-15 09:41:05 +01:00
|
|
|
{
|
2018-12-13 03:15:09 +01:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
Pair<String, Optional<Slide>> splitMessage = getSplitMessage(body, characterCalculator.calculateCharacters(body).maxPrimaryMessageSize);
|
|
|
|
body = splitMessage.first;
|
2019-02-27 04:29:52 +01:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
if (splitMessage.second.isPresent()) {
|
|
|
|
slideDeck.addSlide(splitMessage.second.get());
|
2019-02-27 04:29:52 +01:00
|
|
|
}
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
List<Attachment> attachments = slideDeck.asAttachments();
|
|
|
|
|
|
|
|
VisibleMessage message = new VisibleMessage();
|
|
|
|
message.setSentTimestamp(System.currentTimeMillis());
|
|
|
|
message.setText(body);
|
|
|
|
OutgoingMediaMessage outgoingMessageCandidate = OutgoingMediaMessage.from(message, recipient, attachments, quote, linkPreview.orNull());
|
2014-06-12 00:31:59 +02:00
|
|
|
|
2019-08-07 07:24:05 +02:00
|
|
|
final SettableFuture<Void> future = new SettableFuture<>();
|
2017-12-05 20:35:15 +01:00
|
|
|
final Context context = getApplicationContext();
|
2014-11-08 20:35:58 +01:00
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
final OutgoingMediaMessage outgoingMessage;
|
2017-08-17 06:49:41 +02:00
|
|
|
|
2020-12-07 07:40:47 +01:00
|
|
|
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate);
|
|
|
|
ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
|
2014-11-08 20:35:58 +01:00
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
inputPanel.clearQuote();
|
|
|
|
attachmentManager.clear(glideRequests, false);
|
|
|
|
silentlySetComposeText("");
|
2020-03-04 23:27:39 +01:00
|
|
|
|
|
|
|
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
|
|
|
|
2020-09-29 02:57:43 +02:00
|
|
|
if (initiating) {
|
|
|
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
|
|
|
}
|
2020-08-12 03:21:41 +02:00
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
try {
|
2021-03-04 07:14:12 +01:00
|
|
|
long allocatedThreadId = getAllocatedThreadId(context);
|
2021-03-02 07:22:56 +01:00
|
|
|
message.setId(DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMessage, allocatedThreadId, false, ()->fragment.releaseOutgoingMessage(id)));
|
2021-03-02 02:24:09 +01:00
|
|
|
MessageSender.send(message, recipient.getAddress(), attachments, quote, linkPreview.orNull());
|
|
|
|
sendComplete(allocatedThreadId);
|
|
|
|
} catch (MmsException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
sendComplete(threadId);
|
|
|
|
}
|
2020-09-29 02:57:43 +02:00
|
|
|
future.set(null);
|
2015-11-18 23:52:26 +01:00
|
|
|
|
|
|
|
return future;
|
2014-06-12 00:31:59 +02:00
|
|
|
}
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
private void sendTextMessage(final boolean initiating)
|
2014-06-12 00:31:59 +02:00
|
|
|
throws InvalidMessageException
|
|
|
|
{
|
2017-12-05 20:35:15 +01:00
|
|
|
final Context context = getApplicationContext();
|
|
|
|
final String messageBody = getMessage();
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
VisibleMessage message = new VisibleMessage();
|
|
|
|
message.setSentTimestamp(System.currentTimeMillis());
|
|
|
|
message.setText(messageBody);
|
|
|
|
OutgoingTextMessage outgoingTextMessage = OutgoingTextMessage.from(message, recipient);
|
2020-12-07 07:40:47 +01:00
|
|
|
ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
|
2014-06-12 00:31:59 +02:00
|
|
|
|
2020-03-04 23:27:39 +01:00
|
|
|
silentlySetComposeText("");
|
2021-03-02 02:24:09 +01:00
|
|
|
final long id = fragment.stageOutgoingMessage(outgoingTextMessage);
|
2020-03-04 23:27:39 +01:00
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
if (initiating) {
|
2020-09-29 02:57:43 +02:00
|
|
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
|
|
|
}
|
2020-08-12 03:21:41 +02:00
|
|
|
|
2021-03-04 07:14:12 +01:00
|
|
|
long allocatedThreadId = getAllocatedThreadId(context);
|
2021-03-02 07:22:56 +01:00
|
|
|
message.setId(DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(allocatedThreadId, outgoingTextMessage, false, message.getSentTimestamp(), ()->fragment.releaseOutgoingMessage(id)));
|
2021-03-02 02:24:09 +01:00
|
|
|
MessageSender.send(message, recipient.getAddress());
|
2014-06-12 00:31:59 +02:00
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
sendComplete(allocatedThreadId);
|
2018-12-13 03:15:09 +01:00
|
|
|
}
|
|
|
|
|
2015-05-18 19:26:32 +02:00
|
|
|
private void updateToggleButtonState() {
|
2019-03-28 19:04:38 +01:00
|
|
|
if (inputPanel.isRecordingInLockedMode()) {
|
|
|
|
buttonToggle.display(sendButton);
|
|
|
|
quickAttachmentToggle.show();
|
|
|
|
inlineAttachmentToggle.hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-20 01:53:12 +01:00
|
|
|
if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
|
2019-10-18 03:40:41 +02:00
|
|
|
buttonToggle.display(attachButton);
|
2015-06-08 20:07:46 +02:00
|
|
|
quickAttachmentToggle.show();
|
2018-08-10 18:18:02 +02:00
|
|
|
inlineAttachmentToggle.hide();
|
2015-05-18 19:26:32 +02:00
|
|
|
} else {
|
|
|
|
buttonToggle.display(sendButton);
|
2015-06-08 20:07:46 +02:00
|
|
|
quickAttachmentToggle.hide();
|
2018-08-15 21:35:41 +02:00
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreview()) {
|
2018-08-15 21:35:41 +02:00
|
|
|
inlineAttachmentToggle.show();
|
|
|
|
} else {
|
|
|
|
inlineAttachmentToggle.hide();
|
|
|
|
}
|
2015-05-18 19:26:32 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-12 00:31:59 +02:00
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
private void updateLinkPreviewState() {
|
2020-12-07 07:40:47 +01:00
|
|
|
if (TextSecurePreferences.isLinkPreviewsEnabled(this) && !attachmentManager.isAttachmentPresent()) {
|
2019-01-15 09:41:05 +01:00
|
|
|
linkPreviewViewModel.onEnabled();
|
2019-02-14 22:55:48 +01:00
|
|
|
linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), composeText.getSelectionStart(), composeText.getSelectionEnd());
|
2019-01-15 09:41:05 +01:00
|
|
|
} else {
|
|
|
|
linkPreviewViewModel.onUserCancel();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-25 07:00:30 +01:00
|
|
|
@Override
|
|
|
|
public void onRecorderPermissionRequired() {
|
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.RECORD_AUDIO)
|
2020-08-24 06:43:24 +02:00
|
|
|
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
|
2017-11-25 07:00:30 +01:00
|
|
|
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
2015-11-18 23:52:26 +01:00
|
|
|
@Override
|
|
|
|
public void onRecorderStarted() {
|
2017-12-05 20:52:03 +01:00
|
|
|
Vibrator vibrator = ServiceUtil.getVibrator(this);
|
2015-11-22 19:44:44 +01:00
|
|
|
vibrator.vibrate(20);
|
2017-12-05 20:52:03 +01:00
|
|
|
|
2017-02-16 21:28:06 +01:00
|
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
2015-11-18 23:52:26 +01:00
|
|
|
|
2015-11-22 19:44:44 +01:00
|
|
|
audioRecorder.startRecording();
|
2020-11-04 04:49:28 +01:00
|
|
|
|
|
|
|
audioHandler = new Handler();
|
|
|
|
stopRecordingTask = () -> inputPanel.onRecordReleased();
|
|
|
|
audioHandler.postDelayed(stopRecordingTask, 60000);
|
2015-11-18 23:52:26 +01:00
|
|
|
}
|
|
|
|
|
2019-03-28 19:04:38 +01:00
|
|
|
@Override
|
|
|
|
public void onRecorderLocked() {
|
|
|
|
updateToggleButtonState();
|
|
|
|
}
|
|
|
|
|
2015-11-18 23:52:26 +01:00
|
|
|
@Override
|
|
|
|
public void onRecorderFinished() {
|
2020-11-04 04:49:28 +01:00
|
|
|
if (audioHandler != null && stopRecordingTask != null) {
|
|
|
|
audioHandler.removeCallbacks(stopRecordingTask);
|
|
|
|
}
|
2019-03-28 19:04:38 +01:00
|
|
|
updateToggleButtonState();
|
2017-12-05 20:52:03 +01:00
|
|
|
Vibrator vibrator = ServiceUtil.getVibrator(this);
|
2015-11-18 23:52:26 +01:00
|
|
|
vibrator.vibrate(20);
|
2017-12-05 20:52:03 +01:00
|
|
|
|
2017-02-16 21:28:06 +01:00
|
|
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
2015-11-18 23:52:26 +01: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) {
|
2020-12-07 07:40:47 +01:00
|
|
|
int subscriptionId = -1;
|
2018-01-12 12:39:10 +01:00
|
|
|
long expiresIn = recipient.getExpireMessages() * 1000L;
|
2017-12-05 20:35:15 +01:00
|
|
|
boolean initiating = threadId == -1;
|
2021-02-01 07:00:32 +01:00
|
|
|
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaTypes.AUDIO_AAC, true);
|
2017-12-05 20:35:15 +01:00
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
|
|
|
slideDeck.addSlide(audioSlide);
|
|
|
|
|
2021-03-02 02:24:09 +01:00
|
|
|
sendMediaMessage("", slideDeck, inputPanel.getQuote().orNull(), Optional.absent(), initiating).addListener(new AssertedSuccessListener<Void>() {
|
2017-12-05 20:35:15 +01:00
|
|
|
@Override
|
|
|
|
public void onSuccess(Void nothing) {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
2019-02-26 02:47:30 +01:00
|
|
|
BlobProvider.getInstance().delete(ConversationActivity.this, result.first);
|
2017-12-05 20:35:15 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
|
|
}
|
|
|
|
});
|
2015-11-18 23:52:26 +01: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() {
|
2020-11-04 04:49:28 +01:00
|
|
|
if (audioHandler != null && stopRecordingTask != null) {
|
|
|
|
audioHandler.removeCallbacks(stopRecordingTask);
|
|
|
|
}
|
2019-03-28 19:04:38 +01:00
|
|
|
updateToggleButtonState();
|
2017-12-05 20:52:03 +01:00
|
|
|
Vibrator vibrator = ServiceUtil.getVibrator(this);
|
2015-11-18 23:52:26 +01:00
|
|
|
vibrator.vibrate(50);
|
2017-12-05 20:52:03 +01:00
|
|
|
|
2017-02-16 21:28:06 +01:00
|
|
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
2015-11-18 23:52:26 +01: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-26 02:47:30 +01:00
|
|
|
BlobProvider.getInstance().delete(ConversationActivity.this, result.first);
|
2015-11-18 23:52:26 +01:00
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2015-11-18 23:52:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFailure(ExecutionException e) {}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-11-22 19:44:53 +01:00
|
|
|
@Override
|
|
|
|
public void onEmojiToggle() {
|
2017-01-19 20:31:41 +01:00
|
|
|
if (!emojiDrawerStub.resolved()) {
|
2021-02-22 00:06:40 +01:00
|
|
|
initializeMediaKeyboardProviders(emojiDrawerStub.get());
|
2019-04-17 16:21:30 +02:00
|
|
|
|
|
|
|
inputPanel.setMediaKeyboard(emojiDrawerStub.get());
|
2017-01-19 20:31:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (container.getCurrentInput() == emojiDrawerStub.get()) {
|
|
|
|
container.showSoftkey(composeText);
|
|
|
|
} else {
|
|
|
|
container.show(composeText, emojiDrawerStub.get());
|
|
|
|
}
|
2015-11-22 19:44:53 +01:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
@Override
|
|
|
|
public void onLinkPreviewCanceled() {
|
|
|
|
linkPreviewViewModel.onUserCancel();
|
|
|
|
}
|
|
|
|
|
2016-12-27 00:14:23 +01: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-09 00:32:59 +02:00
|
|
|
} else if (MediaUtil.isImageType(contentType)) {
|
2016-12-27 00:14:23 +01:00
|
|
|
setMedia(uri, MediaType.IMAGE);
|
2017-05-09 00:32:59 +02:00
|
|
|
} else if (MediaUtil.isVideoType(contentType)) {
|
2016-12-27 00:14:23 +01:00
|
|
|
setMedia(uri, MediaType.VIDEO);
|
2017-05-09 00:32:59 +02:00
|
|
|
} else if (MediaUtil.isAudioType(contentType)) {
|
2016-12-27 00:14:23 +01:00
|
|
|
setMedia(uri, MediaType.AUDIO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 22:55:48 +01:00
|
|
|
@Override
|
|
|
|
public void onCursorPositionChanged(int start, int end) {
|
|
|
|
linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed(), start, end);
|
|
|
|
}
|
|
|
|
|
2018-10-29 23:14:31 +01:00
|
|
|
private void silentlySetComposeText(String text) {
|
|
|
|
typingTextWatcher.setEnabled(false);
|
|
|
|
composeText.setText(text);
|
2020-05-14 02:44:13 +02:00
|
|
|
if (text.isEmpty()) { resetMentions(); }
|
2018-10-29 23:14:31 +01:00
|
|
|
typingTextWatcher.setEnabled(true);
|
|
|
|
}
|
2016-12-27 00:14:23 +01:00
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
// Listeners
|
|
|
|
|
2015-10-28 17:47:09 +01:00
|
|
|
private class AttachmentTypeListener implements AttachmentTypeSelector.AttachmentClickedListener {
|
2013-02-03 05:37:40 +01:00
|
|
|
@Override
|
2015-10-28 17:47:09 +01:00
|
|
|
public void onClick(int type) {
|
|
|
|
addAttachment(type);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2016-11-26 07:37:23 +01:00
|
|
|
|
|
|
|
@Override
|
2019-03-15 01:01:23 +01:00
|
|
|
public void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
|
2019-01-15 09:41:05 +01:00
|
|
|
linkPreviewViewModel.onUserCancel();
|
2019-03-15 01:01:23 +01:00
|
|
|
Media media = new Media(uri, mimeType, dateTaken, width, height, size, Optional.of(Media.ALL_MEDIA_BUCKET_ID), Optional.absent());
|
2020-12-07 07:40:47 +01:00
|
|
|
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient, composeText.getTextTrimmed()), MEDIA_SENDER);
|
2016-11-26 07:37:23 +01:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2015-11-18 23:52:26 +01:00
|
|
|
private class QuickCameraToggleListener implements OnClickListener {
|
2015-04-16 07:38:33 +02:00
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
2018-09-20 22:27:18 +02:00
|
|
|
Permissions.with(ConversationActivity.this)
|
|
|
|
.request(Manifest.permission.CAMERA)
|
2020-08-24 06:43:24 +02:00
|
|
|
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48)
|
2018-09-20 22:27:18 +02:00
|
|
|
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
|
|
|
.onAllGranted(() -> {
|
|
|
|
composeText.clearFocus();
|
2020-12-07 07:40:47 +01:00
|
|
|
startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER);
|
2018-09-20 22:27:18 +02: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 07:38:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener {
|
2013-02-03 05:37:40 +01:00
|
|
|
@Override
|
2012-07-19 23:22:03 +02:00
|
|
|
public void onClick(View v) {
|
2014-07-19 04:31:03 +02:00
|
|
|
sendMessage();
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2013-02-03 05:37:40 +01:00
|
|
|
@Override
|
2012-07-19 23:22:03 +02:00
|
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
|
|
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
|
|
|
sendButton.performClick();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2015-05-18 19:26:32 +02:00
|
|
|
private class AttachButtonListener implements OnClickListener {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
handleAddAttachment();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 23:49:43 +02:00
|
|
|
private class AttachButtonLongClickListener implements View.OnLongClickListener {
|
|
|
|
@Override
|
|
|
|
public boolean onLongClick(View v) {
|
|
|
|
return sendButton.performLongClick();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-29 05:53:34 +02:00
|
|
|
private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener {
|
2015-05-18 19:26:32 +02:00
|
|
|
|
|
|
|
int beforeLength;
|
|
|
|
|
2013-06-28 05:57:27 +02:00
|
|
|
@Override
|
|
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
2013-07-10 03:26:18 +02:00
|
|
|
if (TextSecurePreferences.isEnterSendsEnabled(ConversationActivity.this)) {
|
2013-06-28 05:57:27 +02: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 22:22:28 +02:00
|
|
|
container.showSoftkey(composeText);
|
2013-06-28 05:57:27 +02:00
|
|
|
}
|
|
|
|
|
2015-05-18 19:26:32 +02: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 19:26:32 +02:00
|
|
|
}
|
|
|
|
|
2013-02-03 05:37:40 +01:00
|
|
|
@Override
|
2012-07-19 23:22:03 +02:00
|
|
|
public void afterTextChanged(Editable s) {
|
2019-02-14 22:55:48 +01:00
|
|
|
if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) {
|
2017-12-05 20:52:03 +01:00
|
|
|
composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50);
|
2015-05-18 19:26:32 +02:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2015-05-18 19:26:32 +02:00
|
|
|
|
2013-02-03 05:37:40 +01:00
|
|
|
@Override
|
2012-07-19 23:22:03 +02:00
|
|
|
public void onTextChanged(CharSequence s, int start, int before,int count) {}
|
2014-05-29 05:53:34 +02:00
|
|
|
|
|
|
|
@Override
|
2015-07-24 22:22:28 +02:00
|
|
|
public void onFocusChange(View v, boolean hasFocus) {}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2012-08-04 02:34:09 +02:00
|
|
|
|
2018-10-29 23:14:31 +01:00
|
|
|
private class TypingStatusTextWatcher extends SimpleTextWatcher {
|
|
|
|
private boolean enabled = true;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTextChanged(String text) {
|
2020-12-07 07:40:47 +01:00
|
|
|
if (enabled && threadId > 0) {
|
2018-10-29 23:14:31 +01:00
|
|
|
ApplicationContext.getInstance(ConversationActivity.this).getTypingStatusSender().onTypingStarted(threadId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEnabled(boolean enabled) {
|
|
|
|
this.enabled = enabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-10 05:44:08 +02:00
|
|
|
private class MentionTextWatcher extends SimpleTextWatcher {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTextChanged(String text) {
|
2019-10-11 02:13:34 +02:00
|
|
|
boolean isBackspace = text.length() < oldText.length();
|
|
|
|
if (isBackspace) {
|
|
|
|
currentMentionStartIndex = -1;
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.hide();
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionViewContainer.setVisibility(View.GONE);
|
2019-10-15 07:19:00 +02:00
|
|
|
ArrayList<Mention> mentionsToRemove = new ArrayList<>();
|
|
|
|
for (Mention mention : mentions) {
|
|
|
|
if (!text.contains(mention.getDisplayName())) {
|
|
|
|
mentionsToRemove.add(mention);
|
2019-10-11 02:13:34 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-15 07:19:00 +02:00
|
|
|
mentions.removeAll(mentionsToRemove);
|
2019-10-15 07:07:34 +02:00
|
|
|
}
|
|
|
|
if (text.length() > 0) {
|
2019-10-11 02:42:01 +02:00
|
|
|
if (currentMentionStartIndex > text.length()) {
|
2019-10-11 07:37:28 +02:00
|
|
|
resetMentions(); // Should never occur
|
2019-10-11 02:42:01 +02:00
|
|
|
}
|
2019-10-11 07:37:28 +02:00
|
|
|
int lastCharacterIndex = text.length() - 1;
|
|
|
|
char lastCharacter = text.charAt(lastCharacterIndex);
|
2019-11-19 23:17:31 +01:00
|
|
|
char secondToLastCharacter = ' ';
|
2019-11-18 07:10:28 +01:00
|
|
|
if (lastCharacterIndex > 0) {
|
2019-11-19 23:17:31 +01:00
|
|
|
secondToLastCharacter = text.charAt(lastCharacterIndex - 1);
|
2019-11-18 07:10:28 +01:00
|
|
|
}
|
2019-10-15 04:51:18 +02:00
|
|
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(ConversationActivity.this);
|
|
|
|
LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this);
|
2019-10-11 02:13:34 +02:00
|
|
|
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
|
2019-11-19 23:17:31 +01:00
|
|
|
if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
|
2020-05-14 03:19:20 +02:00
|
|
|
List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId);
|
2019-10-11 07:37:28 +02:00
|
|
|
currentMentionStartIndex = lastCharacterIndex;
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.show(mentionCandidates, threadId);
|
2019-10-11 02:13:34 +02:00
|
|
|
} else if (Character.isWhitespace(lastCharacter)) {
|
|
|
|
currentMentionStartIndex = -1;
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.hide();
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionViewContainer.setVisibility(View.GONE);
|
2019-10-11 02:13:34 +02:00
|
|
|
} else {
|
|
|
|
if (currentMentionStartIndex != -1) {
|
|
|
|
String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
|
2020-05-14 03:19:20 +02:00
|
|
|
List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId);
|
2020-01-16 05:15:08 +01:00
|
|
|
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
|
2019-10-11 07:37:28 +02:00
|
|
|
mentionCandidateSelectionView.show(mentionCandidates, threadId);
|
2019-10-11 02:13:34 +02:00
|
|
|
}
|
2019-10-10 05:44:08 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-11 07:37:28 +02:00
|
|
|
ConversationActivity.this.oldText = text;
|
2019-10-10 05:44:08 +02:00
|
|
|
}
|
2019-10-11 02:13:34 +02:00
|
|
|
}
|
2019-10-10 05:44:08 +02:00
|
|
|
|
2019-10-11 07:37:28 +02:00
|
|
|
private void resetMentions() {
|
2019-10-11 02:13:34 +02:00
|
|
|
oldText = "";
|
|
|
|
currentMentionStartIndex = -1;
|
|
|
|
mentions.clear();
|
2019-10-10 05:44:08 +02:00
|
|
|
}
|
|
|
|
|
2015-03-31 22:36:04 +02:00
|
|
|
@Override
|
|
|
|
public void setThreadId(long threadId) {
|
|
|
|
this.threadId = threadId;
|
|
|
|
}
|
|
|
|
|
2018-02-07 23:01:37 +01:00
|
|
|
@Override
|
|
|
|
public void handleReplyMessage(MessageRecord messageRecord) {
|
2020-04-09 05:23:51 +02:00
|
|
|
if (recipient.isGroupRecipient() && !isActiveGroup()) { return; }
|
|
|
|
|
2018-02-07 23:01:37 +01:00
|
|
|
Recipient author;
|
|
|
|
|
|
|
|
if (messageRecord.isOutgoing()) {
|
2021-02-17 06:09:36 +01:00
|
|
|
author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true);
|
2018-02-07 23:01:37 +01:00
|
|
|
} else {
|
|
|
|
author = messageRecord.getIndividualRecipient();
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:03:54 +02: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),
|
2020-05-22 01:53:04 +02:00
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
|
|
|
body,
|
|
|
|
slideDeck,
|
|
|
|
recipient,
|
|
|
|
threadId);
|
2019-01-15 09:41:05 +01: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),
|
2020-05-22 01:53:04 +02:00
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
|
|
|
messageRecord.getBody(),
|
|
|
|
slideDeck,
|
|
|
|
recipient,
|
|
|
|
threadId);
|
2018-04-27 02:03:54 +02:00
|
|
|
} else {
|
|
|
|
inputPanel.setQuote(GlideApp.with(this),
|
2020-05-22 01:53:04 +02:00
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
|
|
|
messageRecord.getBody(),
|
|
|
|
messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(),
|
|
|
|
recipient,
|
|
|
|
threadId);
|
2018-04-27 02:03:54 +02:00
|
|
|
}
|
2018-02-07 23:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
@Override
|
|
|
|
public void onMessageActionToolbarOpened() {
|
2020-01-15 07:13:46 +01:00
|
|
|
searchViewItem.collapseActionView();
|
2019-02-01 18:06:59 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 15:35:47 +02:00
|
|
|
@Override
|
|
|
|
public void onForwardClicked() {
|
|
|
|
inputPanel.clearQuote();
|
|
|
|
}
|
|
|
|
|
2014-04-15 12:43:14 +02:00
|
|
|
@Override
|
|
|
|
public void onAttachmentChanged() {
|
2020-12-07 07:40:47 +01:00
|
|
|
handleSecurityChange(true, isDefaultSms);
|
2015-05-18 19:26:32 +02:00
|
|
|
updateToggleButtonState();
|
2019-01-15 09:41:05 +01:00
|
|
|
updateLinkPreviewState();
|
2014-04-15 12:43:14 +02:00
|
|
|
}
|
2015-09-05 02:33:22 +02:00
|
|
|
|
2018-04-24 20:09:54 +02:00
|
|
|
private class QuoteRestorationTask extends AsyncTask<Void, Void, MessageRecord> {
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private final String serialized;
|
|
|
|
private final SettableFuture<Boolean> future;
|
2018-04-24 20:09:54 +02:00
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
QuoteRestorationTask(@NonNull String serialized, @NonNull SettableFuture<Boolean> future) {
|
2018-04-24 20:09:54 +02:00
|
|
|
this.serialized = serialized;
|
2018-07-25 17:30:48 +02:00
|
|
|
this.future = future;
|
2018-04-24 20:09:54 +02: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 17:30:48 +02:00
|
|
|
future.set(true);
|
2018-04-24 20:09:54 +02:00
|
|
|
} else {
|
|
|
|
Log.e(TAG, "Failed to restore a quote from a draft. No matching message record.");
|
2018-07-25 17:30:48 +02:00
|
|
|
future.set(false);
|
2018-04-24 20:09:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-28 04:32:23 +02:00
|
|
|
|
|
|
|
// region Loki
|
2021-03-04 07:14:12 +01:00
|
|
|
private long getAllocatedThreadId(Context context) {
|
|
|
|
long allocatedThreadId;
|
|
|
|
if (threadId == -1) {
|
|
|
|
allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
|
|
|
} else {
|
|
|
|
allocatedThreadId = threadId;
|
|
|
|
}
|
|
|
|
return allocatedThreadId;
|
|
|
|
}
|
|
|
|
|
2020-02-12 23:28:00 +01:00
|
|
|
private void updateTitleTextView(Recipient recipient) {
|
2020-05-14 02:44:13 +02:00
|
|
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
2020-01-17 00:37:06 +01:00
|
|
|
if (recipient == null) {
|
|
|
|
titleTextView.setText("Compose");
|
2021-02-22 03:36:37 +01:00
|
|
|
} else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) {
|
2021-02-19 00:05:24 +01:00
|
|
|
titleTextView.setText(getResources().getString(R.string.note_to_self));
|
2020-01-17 00:37:06 +01:00
|
|
|
} else {
|
2020-02-12 23:28:00 +01:00
|
|
|
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty());
|
|
|
|
titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString());
|
2020-01-17 00:37:06 +01:00
|
|
|
}
|
2020-01-10 01:35:16 +01:00
|
|
|
}
|
|
|
|
|
2020-09-07 08:13:34 +02:00
|
|
|
private void updateProfilePicture() {
|
2020-10-02 07:40:55 +02:00
|
|
|
try {
|
|
|
|
profilePictureView.glide = GlideApp.with(this);
|
|
|
|
profilePictureView.update(recipient, threadId);
|
|
|
|
} catch (Exception exception) {
|
|
|
|
// Do nothing
|
|
|
|
}
|
2020-09-07 08:13:34 +02:00
|
|
|
}
|
|
|
|
|
2020-01-16 04:35:51 +01:00
|
|
|
private void updateSubtitleTextView() {
|
2020-01-17 00:37:06 +01:00
|
|
|
muteIndicatorImageView.setVisibility(View.GONE);
|
2020-02-12 23:28:00 +01:00
|
|
|
subtitleTextView.setVisibility(View.VISIBLE);
|
2020-09-04 03:32:43 +02:00
|
|
|
if (recipient.isMuted()) {
|
2020-01-17 00:37:06 +01:00
|
|
|
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
2020-02-12 23:28:00 +01:00
|
|
|
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
2020-02-04 03:07:54 +01:00
|
|
|
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
2021-04-27 06:26:26 +02:00
|
|
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
2020-01-22 00:46:04 +01:00
|
|
|
if (publicChat != null) {
|
|
|
|
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
|
|
|
if (userCount == null) { userCount = 0; }
|
2020-05-27 05:51:19 +02:00
|
|
|
subtitleTextView.setText(userCount + " members");
|
2020-01-22 05:28:39 +01:00
|
|
|
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
2020-02-12 23:28:00 +01:00
|
|
|
subtitleTextView.setText(recipient.getAddress().toString());
|
2020-01-22 00:46:04 +01:00
|
|
|
} else {
|
2020-02-12 23:28:00 +01:00
|
|
|
subtitleTextView.setVisibility(View.GONE);
|
2020-01-22 00:46:04 +01:00
|
|
|
}
|
2020-01-17 00:37:06 +01:00
|
|
|
} else {
|
2020-02-12 23:28:00 +01:00
|
|
|
subtitleTextView.setVisibility(View.GONE);
|
2020-01-16 04:35:51 +01:00
|
|
|
}
|
2020-02-12 23:28:00 +01:00
|
|
|
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
|
2020-01-16 04:35:51 +01: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) {
|
2021-02-19 01:03:58 +01:00
|
|
|
if (timestamp == 0 || (TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize())) ) { return; }
|
2020-01-16 04:35:51 +01:00
|
|
|
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();
|
|
|
|
}
|
2020-02-12 23:28:00 +01:00
|
|
|
// endregion
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|