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;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2015-07-07 23:25:41 +02:00
|
|
|
import android.content.res.Configuration;
|
2015-07-01 02:45:39 +02:00
|
|
|
import android.content.res.TypedArray;
|
2018-08-25 19:33:14 +02:00
|
|
|
import android.graphics.Bitmap;
|
2015-07-01 02:45:39 +02:00
|
|
|
import android.graphics.Color;
|
2019-10-07 20:43:36 +02:00
|
|
|
import android.graphics.PorterDuff;
|
2015-06-24 00:10:50 +02:00
|
|
|
import android.graphics.drawable.ColorDrawable;
|
2020-03-25 16:30:15 +01:00
|
|
|
import android.graphics.drawable.Drawable;
|
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;
|
2015-11-18 23:52:26 +01:00
|
|
|
import android.os.Vibrator;
|
2016-03-30 00:52:54 +02:00
|
|
|
import android.provider.Browser;
|
2013-10-17 02:28:36 +02:00
|
|
|
import android.provider.ContactsContract;
|
2016-11-21 00:56:47 +01:00
|
|
|
import android.provider.Telephony;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.text.Editable;
|
2020-08-05 22:45:52 +02:00
|
|
|
import android.text.Spannable;
|
|
|
|
import android.text.SpannableString;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.text.TextWatcher;
|
2020-05-28 18:24:04 +02:00
|
|
|
import android.view.Gravity;
|
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;
|
2019-12-03 22:57:21 +01:00
|
|
|
import android.view.MotionEvent;
|
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;
|
2013-03-06 04:32:15 +01:00
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2019-11-12 15:18:57 +01:00
|
|
|
import androidx.annotation.IdRes;
|
2019-08-30 23:45:35 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
2020-07-08 17:54:47 +02:00
|
|
|
import androidx.annotation.WorkerThread;
|
2019-08-30 23:45:35 +02:00
|
|
|
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.lifecycle.ViewModelProviders;
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
import com.annimon.stream.Collectors;
|
2019-02-25 22:58:10 +01:00
|
|
|
import com.annimon.stream.Stream;
|
2020-07-08 17:54:47 +02:00
|
|
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
2020-03-25 16:30:15 +01:00
|
|
|
import com.bumptech.glide.request.target.CustomTarget;
|
|
|
|
import com.bumptech.glide.request.transition.Transition;
|
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;
|
2019-02-01 04:28:40 +01:00
|
|
|
import org.thoughtcrime.securesms.ApplicationContext;
|
2020-04-16 17:30:51 +02:00
|
|
|
import org.thoughtcrime.securesms.BlockUnblockDialog;
|
2019-02-01 04:28:40 +01:00
|
|
|
import org.thoughtcrime.securesms.ExpirationDialog;
|
|
|
|
import org.thoughtcrime.securesms.GroupMembersDialog;
|
2019-11-14 20:35:08 +01:00
|
|
|
import org.thoughtcrime.securesms.MainActivity;
|
2019-02-01 04:28:40 +01:00
|
|
|
import org.thoughtcrime.securesms.MuteDialog;
|
2020-06-23 02:01:40 +02:00
|
|
|
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
2019-02-01 04:28:40 +01:00
|
|
|
import org.thoughtcrime.securesms.PromptMmsActivity;
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
|
|
import org.thoughtcrime.securesms.ShortcutLauncherActivity;
|
|
|
|
import org.thoughtcrime.securesms.TransportOption;
|
|
|
|
import org.thoughtcrime.securesms.VerifyIdentityActivity;
|
2019-06-11 08:18:45 +02:00
|
|
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
|
|
|
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
|
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-06-30 18:16:05 +02:00
|
|
|
import org.thoughtcrime.securesms.color.MaterialColor;
|
2015-05-18 19:26:32 +02:00
|
|
|
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
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;
|
2015-06-09 16:37:20 +02:00
|
|
|
import org.thoughtcrime.securesms.components.SendButton;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.components.TooltipPopup;
|
|
|
|
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;
|
2017-06-07 03:03:09 +02:00
|
|
|
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView;
|
2016-01-04 22:02:22 +01:00
|
|
|
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
2020-08-05 22:45:52 +02:00
|
|
|
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
|
2017-11-16 01:29:00 +01:00
|
|
|
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
|
2019-11-12 15:18:57 +01:00
|
|
|
import org.thoughtcrime.securesms.components.reminder.Reminder;
|
2015-11-09 23:51:53 +01:00
|
|
|
import org.thoughtcrime.securesms.components.reminder.ReminderView;
|
2018-06-11 18:37:01 +02:00
|
|
|
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder;
|
2017-11-16 01:29:00 +01:00
|
|
|
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder;
|
2013-10-17 02:28:36 +02:00
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
2019-11-12 15:18:57 +01:00
|
|
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
2018-04-27 02:03:54 +02:00
|
|
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
|
|
|
import org.thoughtcrime.securesms.contactshare.ContactShareEditActivity;
|
|
|
|
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
2018-10-29 23:14:31 +01:00
|
|
|
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
2020-06-07 01:26:21 +02:00
|
|
|
import org.thoughtcrime.securesms.conversation.ConversationGroupViewModel.GroupActiveState;
|
2020-08-05 22:45:52 +02:00
|
|
|
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
|
2020-06-26 17:10:54 +02:00
|
|
|
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
|
2020-07-27 15:58:58 +02:00
|
|
|
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
|
2019-12-03 17:05:03 +01:00
|
|
|
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
|
2014-11-04 00:16:04 +01:00
|
|
|
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
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;
|
2020-03-27 19:55:44 +01:00
|
|
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
2017-06-07 03:03:09 +02:00
|
|
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
|
|
|
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
2020-08-05 22:45:52 +02:00
|
|
|
import org.thoughtcrime.securesms.database.MentionUtil;
|
|
|
|
import org.thoughtcrime.securesms.database.MentionUtil.UpdatedBodyAndMentions;
|
2014-12-12 02:13:01 +01:00
|
|
|
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
2019-04-12 21:22:38 +02:00
|
|
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
2017-08-22 19:44:04 +02:00
|
|
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
2013-04-26 03:59:49 +02:00
|
|
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
2017-06-07 03:03:09 +02:00
|
|
|
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
|
2020-08-05 22:45:52 +02:00
|
|
|
import org.thoughtcrime.securesms.database.model.Mention;
|
2018-02-07 23:01:37 +01:00
|
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
|
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
2019-12-03 22:57:21 +01:00
|
|
|
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
2019-10-15 21:47:54 +02:00
|
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
2017-11-16 01:29:00 +01:00
|
|
|
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
2018-03-20 19:27:11 +01:00
|
|
|
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
2020-07-20 20:20:56 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.GroupChangeException;
|
2020-07-24 21:40:06 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.GroupId;
|
2020-04-27 21:27:31 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
2020-05-12 20:09:47 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
2020-07-20 20:20:56 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeResult;
|
2020-05-12 20:09:47 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
2020-03-31 17:01:43 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
|
2020-04-27 21:27:31 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
2019-11-12 15:18:57 +01:00
|
|
|
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
|
|
|
import org.thoughtcrime.securesms.invites.InviteReminderModel;
|
|
|
|
import org.thoughtcrime.securesms.invites.InviteReminderRepository;
|
2020-07-24 21:40:06 +02:00
|
|
|
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
2020-05-07 21:51:54 +02:00
|
|
|
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
2017-05-20 03:01:40 +02:00
|
|
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
2018-06-11 18:37:01 +02:00
|
|
|
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
2020-05-28 18:24:04 +02:00
|
|
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
|
|
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
|
2018-05-22 11:13:10 +02:00
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
2019-06-27 18:14:54 +02:00
|
|
|
import org.thoughtcrime.securesms.maps.PlacePickerActivity;
|
2019-12-03 17:05:03 +01:00
|
|
|
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.mediasend.Media;
|
|
|
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
|
2020-01-08 21:56:51 +01:00
|
|
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult;
|
2020-06-26 17:10:54 +02:00
|
|
|
import org.thoughtcrime.securesms.messagedetails.MessageDetailsActivity;
|
2020-02-19 23:08:34 +01:00
|
|
|
import org.thoughtcrime.securesms.messagerequests.MessageRequestViewModel;
|
|
|
|
import org.thoughtcrime.securesms.messagerequests.MessageRequestsBottomView;
|
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;
|
2020-07-10 15:30:00 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
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;
|
2016-01-04 22:02:22 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.LocationSlide;
|
2015-07-28 22:17:01 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
2016-08-16 05:23:56 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
2014-02-24 09:19:54 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
2018-04-24 20:09:54 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.QuoteId;
|
|
|
|
import org.thoughtcrime.securesms.mms.QuoteModel;
|
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 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-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.StickerSlide;
|
2018-11-20 18:59:23 +01:00
|
|
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
2018-08-16 18:47:43 +02:00
|
|
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
2017-11-25 07:00:30 +01:00
|
|
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
2017-08-22 20:51:01 +02:00
|
|
|
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
|
2019-02-26 02:47:30 +01:00
|
|
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
2020-07-29 18:44:23 +02:00
|
|
|
import org.thoughtcrime.securesms.reactions.ReactionsBottomSheetDialogFragment;
|
2020-05-05 19:53:57 +02:00
|
|
|
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
|
2019-08-07 20:22:51 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
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.recipients.Recipient;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 21:22:04 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
2019-10-01 14:17:25 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
2019-11-19 17:01:07 +01:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
2020-06-17 17:46:59 +02:00
|
|
|
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
|
2019-10-17 00:37:08 +02:00
|
|
|
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
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.service.KeyCachingService;
|
|
|
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
|
|
|
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
|
2014-02-19 22:46:49 +01:00
|
|
|
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
|
Major storage layer refactoring to set the stage for clean GCM.
1) We now try to hand out cursors at a minimum. There has always been
a fairly clean insertion layer that handles encrypting message bodies,
but the process of decrypting message bodies has always been less than
ideal. Here we introduce a "Reader" interface that will decrypt message
bodies when appropriate and return objects that encapsulate record state.
No more MessageDisplayHelper. The MmsSmsDatabase interface is also more
sane.
2) We finally rid ourselves of the technical debt associated with TextSecure's
initial usage of the default SMS DB. In that world, we weren't able to use
anything other than the default "Inbox, Outbox, Sent" types to describe a
message, and had to overload the message content itself with a set of
local "prefixes" to describe what it was (encrypted, asymetric encrypted,
remote encrypted, a key exchange, procssed key exchange), and so on.
This includes a major schema update that transforms the "type" field into
a bitmask that describes everything that used to be encoded in a prefix,
and prefixes have been completely eliminated from the system.
No more Prefix.java
3) Refactoring of the MultipartMessageHandler code. It's less of a mess, and
hopefully more clear as to what's going on.
The next step is to remove what we can from SmsTransportDetails and genericize
that interface for a GCM equivalent.
2013-04-20 21:22:04 +02:00
|
|
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
|
|
|
|
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
|
|
|
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
|
|
|
|
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent;
|
|
|
|
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
|
2020-08-26 17:51:25 +02:00
|
|
|
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
2020-08-05 22:45:52 +02:00
|
|
|
import org.thoughtcrime.securesms.util.Base64;
|
2018-08-25 19:33:14 +02:00
|
|
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
2015-02-16 11:38:09 +01:00
|
|
|
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
|
2018-04-27 02:03:54 +02:00
|
|
|
import org.thoughtcrime.securesms.util.CommunicationActions;
|
2020-03-25 16:30:15 +01:00
|
|
|
import org.thoughtcrime.securesms.util.DrawableUtil;
|
2019-10-07 20:43:36 +02:00
|
|
|
import org.thoughtcrime.securesms.util.DynamicDarkToolbarTheme;
|
2013-06-30 03:03:55 +02:00
|
|
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
2019-10-07 20:43:36 +02:00
|
|
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
2020-07-27 15:58:58 +02:00
|
|
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
2017-06-07 03:03:09 +02:00
|
|
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
2015-09-05 02:33:22 +02:00
|
|
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
2020-01-08 21:56:51 +01:00
|
|
|
import org.thoughtcrime.securesms.util.MessageUtil;
|
2020-09-09 16:22:22 +02:00
|
|
|
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
2017-12-05 20:52:03 +01:00
|
|
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
2013-07-10 03:26:18 +02:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
2019-04-17 16:21:30 +02:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode;
|
2014-11-12 20:15:05 +01:00
|
|
|
import org.thoughtcrime.securesms.util.Util;
|
2015-10-14 06:44:01 +02:00
|
|
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
2015-11-09 23:51:53 +01:00
|
|
|
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
2015-06-22 17:46:43 +02:00
|
|
|
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
|
|
|
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
2019-08-30 23:45:35 +02:00
|
|
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
2019-11-19 17:01:07 +01:00
|
|
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
2017-01-19 03:46:40 +01:00
|
|
|
import org.thoughtcrime.securesms.util.views.Stub;
|
2016-03-23 18:34:41 +01:00
|
|
|
import org.whispersystems.libsignal.InvalidMessageException;
|
2020-01-08 21:56:51 +01:00
|
|
|
import org.whispersystems.libsignal.util.Pair;
|
2016-03-23 18:34:41 +01:00
|
|
|
import org.whispersystems.libsignal.util.guava.Optional;
|
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;
|
2020-08-05 22:45:52 +02:00
|
|
|
import java.util.ArrayList;
|
2018-04-27 02:03:54 +02:00
|
|
|
import java.util.Collections;
|
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;
|
2020-06-07 23:22:35 +02:00
|
|
|
import java.util.Locale;
|
2020-07-08 17:54:47 +02:00
|
|
|
import java.util.Objects;
|
2020-08-05 22:45:52 +02:00
|
|
|
import java.util.Set;
|
2015-11-18 23:52:26 +01:00
|
|
|
import java.util.concurrent.ExecutionException;
|
2020-07-08 17:54:47 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.concurrent.TimeoutException;
|
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
|
|
|
|
2015-03-11 22:23:45 +01:00
|
|
|
import static org.thoughtcrime.securesms.TransportOption.Type;
|
2014-02-22 19:54:43 +01:00
|
|
|
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
2017-06-07 03:03:09 +02:00
|
|
|
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
2014-02-22 19:54:43 +01:00
|
|
|
|
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")
|
2020-06-23 02:01:40 +02:00
|
|
|
public class ConversationActivity extends PassphraseRequiredActivity
|
2014-04-15 12:43:14 +02:00
|
|
|
implements ConversationFragment.ConversationFragmentListener,
|
2015-02-10 12:15:50 +01:00
|
|
|
AttachmentManager.AttachmentListener,
|
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,
|
2019-04-17 16:21:30 +02:00
|
|
|
ConversationSearchBottomBar.EventListener,
|
2020-01-30 04:13:44 +01:00
|
|
|
StickerKeyboardProvider.StickerEventListener,
|
2020-05-05 19:53:57 +02:00
|
|
|
AttachmentKeyboard.Callback,
|
|
|
|
ConversationReactionOverlay.OnReactionSelectedListener,
|
2020-06-26 17:10:54 +02:00
|
|
|
ReactWithAnyEmojiBottomSheetDialogFragment.Callback,
|
2020-07-29 18:44:23 +02:00
|
|
|
SafetyNumberChangeDialog.Callback,
|
|
|
|
ReactionsBottomSheetDialogFragment.Callback
|
2013-04-26 03:59:49 +02:00
|
|
|
{
|
2020-03-25 16:30:15 +01:00
|
|
|
|
|
|
|
private static final int SHORTCUT_ICON_SIZE = Build.VERSION.SDK_INT >= 26 ? ViewUtil.dpToPx(72) : ViewUtil.dpToPx(48 + 16 * 2);
|
|
|
|
|
2014-02-19 01:28:54 +01:00
|
|
|
private static final String TAG = ConversationActivity.class.getSimpleName();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-08-21 21:06:47 +02:00
|
|
|
private static final String STATE_REACT_WITH_ANY_PAGE = "STATE_REACT_WITH_ANY_PAGE";
|
|
|
|
|
2020-06-06 20:15:52 +02:00
|
|
|
public static final String RECIPIENT_EXTRA = "recipient_id";
|
|
|
|
public static final String THREAD_ID_EXTRA = "thread_id";
|
|
|
|
public static final String TEXT_EXTRA = "draft_text";
|
|
|
|
public static final String MEDIA_EXTRA = "media_list";
|
|
|
|
public static final String STICKER_EXTRA = "sticker_extra";
|
2020-07-10 15:30:00 +02:00
|
|
|
public static final String BORDERLESS_EXTRA = "borderless_extra";
|
2020-06-06 20:15:52 +02:00
|
|
|
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
|
|
|
|
public static final String STARTING_POSITION_EXTRA = "starting_position";
|
2013-02-03 05:37:40 +01:00
|
|
|
|
2018-04-27 02:03:54 +02:00
|
|
|
private static final int PICK_GALLERY = 1;
|
|
|
|
private static final int PICK_DOCUMENT = 2;
|
|
|
|
private static final int PICK_AUDIO = 3;
|
|
|
|
private static final int PICK_CONTACT = 4;
|
|
|
|
private static final int GET_CONTACT_DETAILS = 5;
|
|
|
|
private static final int GROUP_EDIT = 6;
|
|
|
|
private static final int TAKE_PHOTO = 7;
|
|
|
|
private static final int ADD_CONTACT = 8;
|
|
|
|
private static final int PICK_LOCATION = 9;
|
|
|
|
private static final int PICK_GIF = 10;
|
|
|
|
private static final int SMS_DEFAULT = 11;
|
2019-03-14 00:05:25 +01:00
|
|
|
private static final int MEDIA_SENDER = 12;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2019-02-01 04:28:40 +01:00
|
|
|
private GlideRequests glideRequests;
|
|
|
|
protected ComposeText composeText;
|
|
|
|
private AnimatingToggle buttonToggle;
|
|
|
|
private SendButton sendButton;
|
|
|
|
private ImageButton attachButton;
|
|
|
|
protected ConversationTitleView titleView;
|
|
|
|
private TextView charactersLeft;
|
|
|
|
private ConversationFragment fragment;
|
|
|
|
private Button unblockButton;
|
|
|
|
private Button makeDefaultSmsButton;
|
|
|
|
private Button registerButton;
|
|
|
|
private InputAwareLayout container;
|
|
|
|
protected Stub<ReminderView> reminderView;
|
|
|
|
private Stub<UnverifiedBannerView> unverifiedBannerView;
|
2017-08-22 20:51:01 +02:00
|
|
|
private Stub<GroupShareProfileView> groupShareProfileView;
|
2018-10-29 23:14:31 +01:00
|
|
|
private TypingStatusTextWatcher typingTextWatcher;
|
2019-02-01 18:06:59 +01:00
|
|
|
private ConversationSearchBottomBar searchNav;
|
|
|
|
private MenuItem searchViewItem;
|
2020-02-19 23:08:34 +01:00
|
|
|
private MessageRequestsBottomView messageRequestBottomView;
|
2019-12-03 22:57:21 +01:00
|
|
|
private ConversationReactionOverlay reactionOverlay;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-01-30 04:13:44 +01:00
|
|
|
private AttachmentManager attachmentManager;
|
|
|
|
private AudioRecorder audioRecorder;
|
|
|
|
private BroadcastReceiver securityUpdateReceiver;
|
|
|
|
private Stub<MediaKeyboard> emojiDrawerStub;
|
|
|
|
private Stub<AttachmentKeyboard> attachmentKeyboardStub;
|
|
|
|
protected HidingLinearLayout quickAttachmentToggle;
|
|
|
|
protected HidingLinearLayout inlineAttachmentToggle;
|
|
|
|
private InputPanel inputPanel;
|
2020-02-19 23:08:34 +01:00
|
|
|
private View panelParent;
|
2020-07-17 20:32:53 +02:00
|
|
|
private View noLongerMemberBanner;
|
2020-08-26 17:51:25 +02:00
|
|
|
private View requestingMemberBanner;
|
|
|
|
private View cancelJoinRequest;
|
2020-07-27 15:58:58 +02:00
|
|
|
private Stub<View> mentionsSuggestions;
|
2019-02-01 18:06:59 +01:00
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
private LinkPreviewViewModel linkPreviewViewModel;
|
|
|
|
private ConversationSearchViewModel searchViewModel;
|
|
|
|
private ConversationStickerViewModel stickerViewModel;
|
2020-01-30 04:13:44 +01:00
|
|
|
private ConversationViewModel viewModel;
|
2019-11-12 15:18:57 +01:00
|
|
|
private InviteReminderModel inviteReminderModel;
|
2020-06-07 01:26:21 +02:00
|
|
|
private ConversationGroupViewModel groupViewModel;
|
2020-10-13 16:57:00 +02:00
|
|
|
private MentionsPickerViewModel mentionsViewModel;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
private LiveRecipient recipient;
|
|
|
|
private long threadId;
|
|
|
|
private int distributionType;
|
2020-08-21 21:06:47 +02:00
|
|
|
private int reactWithAnyEmojiStartPage;
|
2019-08-07 20:22:51 +02:00
|
|
|
private boolean isSecureText;
|
2020-02-19 23:08:34 +01:00
|
|
|
private boolean isDefaultSms = true;
|
|
|
|
private boolean isMmsEnabled = true;
|
|
|
|
private boolean isSecurityInitialized = false;
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2020-07-21 17:53:25 +02:00
|
|
|
private IdentityRecordList identityRecords = new IdentityRecordList(Collections.emptyList());
|
2019-10-07 20:43:36 +02:00
|
|
|
private final DynamicTheme dynamicTheme = new DynamicDarkToolbarTheme();
|
|
|
|
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2019-11-14 20:35:08 +01:00
|
|
|
public static @NonNull Intent buildIntent(@NonNull Context context,
|
|
|
|
@NonNull RecipientId recipientId,
|
|
|
|
long threadId,
|
|
|
|
int distributionType,
|
2020-06-07 21:25:21 +02:00
|
|
|
int startingPosition)
|
2020-08-26 17:51:25 +02:00
|
|
|
{
|
|
|
|
Intent intent = buildIntent(context, recipientId, threadId);
|
|
|
|
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
|
|
|
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition);
|
|
|
|
|
|
|
|
return intent;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NonNull Intent buildIntent(@NonNull Context context,
|
|
|
|
@NonNull RecipientId recipientId,
|
|
|
|
long threadId)
|
2019-11-14 20:35:08 +01:00
|
|
|
{
|
|
|
|
Intent intent = new Intent(context, ConversationActivity.class);
|
|
|
|
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipientId);
|
|
|
|
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
|
|
|
|
|
|
|
return intent;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
@Override
|
2014-12-15 21:25:55 +01:00
|
|
|
protected void onPreCreate() {
|
2013-06-21 20:56:59 +02:00
|
|
|
dynamicTheme.onCreate(this);
|
2013-06-30 03:03:55 +02:00
|
|
|
dynamicLanguage.onCreate(this);
|
2014-12-15 21:25:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-02-02 04:22:48 +01:00
|
|
|
protected void onCreate(Bundle state, boolean ready) {
|
2019-10-01 14:17:25 +02:00
|
|
|
RecipientId recipientId = getIntent().getParcelableExtra(RECIPIENT_EXTRA);
|
|
|
|
|
|
|
|
if (recipientId == null) {
|
|
|
|
Log.w(TAG, "[onCreate] Missing recipientId!");
|
2019-11-14 20:35:08 +01:00
|
|
|
// TODO [greyson] Navigation
|
|
|
|
startActivity(new Intent(this, MainActivity.class));
|
2019-10-01 14:17:25 +02:00
|
|
|
finish();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
setContentView(R.layout.conversation_activity);
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2017-11-14 03:01:05 +01:00
|
|
|
TypedArray typedArray = obtainStyledAttributes(new int[] {R.attr.conversation_background});
|
|
|
|
int color = typedArray.getColor(0, Color.WHITE);
|
|
|
|
typedArray.recycle();
|
|
|
|
|
|
|
|
getWindow().getDecorView().setBackgroundColor(color);
|
|
|
|
|
2018-02-02 04:22:48 +01:00
|
|
|
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale());
|
2012-07-19 23:22:03 +02:00
|
|
|
|
|
|
|
initializeReceivers();
|
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();
|
2019-04-17 16:21:30 +02:00
|
|
|
initializeStickerObserver();
|
2020-01-30 04:13:44 +01:00
|
|
|
initializeViewModel();
|
2020-06-07 01:26:21 +02:00
|
|
|
initializeGroupViewModel();
|
2020-07-27 15:58:58 +02:00
|
|
|
if (FeatureFlags.mentions()) initializeMentionsViewModel();
|
2020-06-07 01:26:21 +02:00
|
|
|
initializeEnabledCheck();
|
2019-10-17 15:04:37 +02:00
|
|
|
initializeSecurity(recipient.get().isRegistered(), isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
|
2015-10-02 02:46:47 +02:00
|
|
|
@Override
|
|
|
|
public void onSuccess(Boolean result) {
|
2017-06-14 18:53:22 +02:00
|
|
|
initializeProfiles();
|
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());
|
2018-07-25 17:30:48 +02:00
|
|
|
}
|
|
|
|
});
|
2015-10-02 02:46:47 +02:00
|
|
|
}
|
|
|
|
});
|
2019-11-12 15:18:57 +01:00
|
|
|
initializeInsightObserver();
|
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) {
|
2019-07-23 15:24:55 +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
|
|
|
|
2020-08-21 21:06:47 +02:00
|
|
|
reactWithAnyEmojiStartPage = 0;
|
2020-03-19 14:10:16 +01:00
|
|
|
if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent() || inputPanel.getQuote().isPresent()) {
|
2014-12-25 03:32:51 +01:00
|
|
|
saveDraft();
|
2017-10-16 22:11:42 +02:00
|
|
|
attachmentManager.clear(glideRequests, false);
|
2020-03-19 14:10:16 +01:00
|
|
|
inputPanel.clearQuote();
|
2018-10-29 23:14:31 +01:00
|
|
|
silentlySetComposeText("");
|
2014-12-25 03:32:51 +01:00
|
|
|
}
|
|
|
|
|
2019-10-01 14:17:25 +02:00
|
|
|
RecipientId recipientId = intent.getParcelableExtra(RECIPIENT_EXTRA);
|
|
|
|
|
|
|
|
if (recipientId == null) {
|
|
|
|
Log.w(TAG, "[onNewIntent] Missing recipientId!");
|
2019-11-14 20:35:08 +01:00
|
|
|
// TODO [greyson] Navigation
|
|
|
|
startActivity(new Intent(this, MainActivity.class));
|
2019-10-01 14:17:25 +02:00
|
|
|
finish();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-25 03:32:51 +01:00
|
|
|
setIntent(intent);
|
|
|
|
initializeResources();
|
2019-10-17 15:04:37 +02:00
|
|
|
initializeSecurity(recipient.get().isRegistered(), 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
|
|
|
dynamicTheme.onResume(this);
|
2013-06-30 03:03:55 +02:00
|
|
|
dynamicLanguage.onResume(this);
|
2013-06-21 20:56:59 +02:00
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
EventBus.getDefault().register(this);
|
2013-03-05 02:43:04 +01:00
|
|
|
initializeMmsEnabledCheck();
|
2017-06-07 03:03:09 +02:00
|
|
|
initializeIdentityRecords();
|
2015-07-21 04:25:54 +02:00
|
|
|
composeText.setTransport(sendButton.getSelectedTransport());
|
2015-06-09 16:37:20 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
Recipient recipientSnapshot = recipient.get();
|
|
|
|
|
|
|
|
titleView.setTitle(glideRequests, recipientSnapshot);
|
|
|
|
setActionBarColor(recipientSnapshot.getColor());
|
|
|
|
setBlockedUserState(recipientSnapshot, isSecureText, isDefaultSms);
|
2012-07-19 23:22:03 +02:00
|
|
|
calculateCharactersRemaining();
|
2013-02-04 03:41:34 +01:00
|
|
|
|
2020-05-07 21:51:54 +02:00
|
|
|
if (recipientSnapshot.getGroupId().isPresent() && recipientSnapshot.getGroupId().get().isV2()) {
|
2020-07-24 21:40:06 +02:00
|
|
|
GroupId.V2 groupId = recipientSnapshot.getGroupId().get().requireV2();
|
|
|
|
|
|
|
|
ApplicationDependencies.getJobManager()
|
|
|
|
.startChain(new RequestGroupV2InfoJob(groupId))
|
|
|
|
.then(new GroupV2UpdateSelfProfileKeyJob(groupId))
|
|
|
|
.enqueue();
|
2020-05-07 21:51:54 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 21:04:45 +02:00
|
|
|
ApplicationDependencies.getMessageNotifier().setVisibleThread(threadId);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-03-05 02:43:04 +01:00
|
|
|
protected void onPause() {
|
|
|
|
super.onPause();
|
2020-05-27 21:04:45 +02:00
|
|
|
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
|
2019-08-14 17:56:45 +02:00
|
|
|
if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_end);
|
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);
|
2015-07-21 04:25:54 +02:00
|
|
|
composeText.setTransport(sendButton.getSelectedTransport());
|
2017-01-19 20:31:41 +01:00
|
|
|
|
|
|
|
if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) {
|
|
|
|
container.hideAttachedInput(true);
|
|
|
|
}
|
2020-02-06 17:06:41 +01:00
|
|
|
|
|
|
|
if (reactionOverlay != null && reactionOverlay.isShowing()) {
|
|
|
|
reactionOverlay.hide();
|
|
|
|
}
|
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();
|
2016-08-16 05:23:56 +02:00
|
|
|
if (securityUpdateReceiver != null) unregisterReceiver(securityUpdateReceiver);
|
2012-07-19 23:22:03 +02:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
2019-12-03 22:57:21 +01:00
|
|
|
@Override
|
|
|
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
|
|
|
return reactionOverlay.applyTouchEvent(ev) || super.dispatchTouchEvent(ev);
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
@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;
|
2018-04-27 02:03:54 +02:00
|
|
|
case PICK_CONTACT:
|
2018-06-29 20:08:13 +02:00
|
|
|
if (isSecureText && !isSmsForced()) {
|
|
|
|
openContactShareEditor(data.getData());
|
|
|
|
} else {
|
|
|
|
addAttachmentContactInfo(data.getData());
|
|
|
|
}
|
2018-04-27 02:03:54 +02:00
|
|
|
break;
|
|
|
|
case GET_CONTACT_DETAILS:
|
|
|
|
sendSharedContact(data.getParcelableArrayListExtra(ContactShareEditActivity.KEY_CONTACTS));
|
2013-10-17 02:28:36 +02:00
|
|
|
break;
|
2014-02-21 00:41:52 +01:00
|
|
|
case GROUP_EDIT:
|
2019-08-07 20:22:51 +02:00
|
|
|
Recipient recipientSnapshot = recipient.get();
|
|
|
|
|
|
|
|
onRecipientChanged(recipientSnapshot);
|
|
|
|
titleView.setTitle(glideRequests, recipientSnapshot);
|
|
|
|
NotificationChannels.updateContactChannelName(this, recipientSnapshot);
|
|
|
|
setBlockedUserState(recipientSnapshot, isSecureText, isDefaultSms);
|
2015-06-09 16:37:20 +02:00
|
|
|
supportInvalidateOptionsMenu();
|
2014-02-21 00:41:52 +01:00
|
|
|
break;
|
2015-07-11 03:45:55 +02:00
|
|
|
case TAKE_PHOTO:
|
2019-10-15 18:51:16 +02:00
|
|
|
handleImageFromDeviceCameraApp();
|
2015-07-11 03:45:55 +02:00
|
|
|
break;
|
2015-10-16 15:39:27 +02:00
|
|
|
case ADD_CONTACT:
|
2020-06-08 19:20:07 +02:00
|
|
|
SimpleTask.run(() -> {
|
|
|
|
try {
|
|
|
|
DirectoryHelper.refreshDirectoryFor(this, recipient.get(), false);
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, "Failed to refresh user after adding to contacts.");
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}, nothing -> onRecipientChanged(recipient.get()));
|
2015-10-16 15:39:27 +02:00
|
|
|
break;
|
2015-12-18 23:37:11 +01:00
|
|
|
case PICK_LOCATION:
|
2019-06-27 18:14:54 +02:00
|
|
|
SignalPlace place = new SignalPlace(PlacePickerActivity.addressFromData(data));
|
2018-01-25 04:17:44 +01:00
|
|
|
attachmentManager.setLocation(place, getCurrentMediaConstraints());
|
2015-12-18 23:37:11 +01:00
|
|
|
break;
|
2016-10-17 04:05:07 +02:00
|
|
|
case PICK_GIF:
|
2018-03-20 19:27:11 +01:00
|
|
|
setMedia(data.getData(),
|
|
|
|
MediaType.GIF,
|
|
|
|
data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
|
2020-07-06 23:13:08 +02:00
|
|
|
data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0),
|
|
|
|
data.getBooleanExtra(GiphyActivity.EXTRA_BORDERLESS, false));
|
2016-10-17 04:05:07 +02:00
|
|
|
break;
|
2017-01-18 20:01:13 +01:00
|
|
|
case SMS_DEFAULT:
|
2017-03-14 21:24:24 +01:00
|
|
|
initializeSecurity(isSecureText, isDefaultSms);
|
2017-01-18 20:01:13 +01:00
|
|
|
break;
|
2019-03-14 00:05:25 +01:00
|
|
|
case MEDIA_SENDER:
|
2020-01-08 21:56:51 +01:00
|
|
|
MediaSendActivityResult result = data.getParcelableExtra(MediaSendActivity.EXTRA_RESULT);
|
|
|
|
sendButton.setTransport(result.getTransport());
|
2018-11-20 18:59:23 +01:00
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
if (result.isPushPreUpload()) {
|
|
|
|
sendMediaMessage(result);
|
|
|
|
return;
|
|
|
|
}
|
2018-11-20 18:59:23 +01:00
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
|
|
|
boolean initiating = threadId == -1;
|
|
|
|
QuoteModel quote = result.isViewOnce() ? null : inputPanel.getQuote().orNull();
|
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
2020-08-05 22:45:52 +02:00
|
|
|
List<Mention> mentions = new ArrayList<>(result.getMentions());
|
2018-11-20 18:59:23 +01:00
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
for (Media mediaItem : result.getNonUploadedMedia()) {
|
2018-11-20 18:59:23 +01:00
|
|
|
if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
|
2020-09-15 15:27:34 +02:00
|
|
|
slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull(), mediaItem.getTransformProperties().orNull()));
|
2018-11-20 18:59:23 +01:00
|
|
|
} else if (MediaUtil.isGif(mediaItem.getMimeType())) {
|
2020-09-15 15:27:34 +02:00
|
|
|
slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull()));
|
2018-11-20 18:59:23 +01:00
|
|
|
} else if (MediaUtil.isImageType(mediaItem.getMimeType())) {
|
2020-09-15 15:27:34 +02:00
|
|
|
slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), mediaItem.getMimeType(), mediaItem.getSize(), mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull(), null));
|
2018-11-20 18:59:23 +01:00
|
|
|
} 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();
|
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
sendMediaMessage(result.getTransport().isSms(),
|
|
|
|
result.getBody(),
|
2019-04-17 16:21:30 +02:00
|
|
|
slideDeck,
|
2019-06-11 08:18:45 +02:00
|
|
|
quote,
|
2019-04-17 16:21:30 +02:00
|
|
|
Collections.emptyList(),
|
|
|
|
Collections.emptyList(),
|
2020-08-05 22:45:52 +02:00
|
|
|
mentions,
|
2019-04-17 16:21:30 +02:00
|
|
|
expiresIn,
|
2020-01-08 21:56:51 +01:00
|
|
|
result.isViewOnce(),
|
2019-04-17 16:21:30 +02:00
|
|
|
subscriptionId,
|
|
|
|
initiating,
|
|
|
|
true).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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-21 21:06:47 +02:00
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
|
|
|
|
outState.putInt(STATE_REACT_WITH_ANY_PAGE, reactWithAnyEmojiStartPage);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
|
|
|
|
|
|
reactWithAnyEmojiStartPage = savedInstanceState.getInt(STATE_REACT_WITH_ANY_PAGE, 0);
|
|
|
|
}
|
|
|
|
|
2019-10-15 18:51:16 +02:00
|
|
|
private void handleImageFromDeviceCameraApp() {
|
|
|
|
if (attachmentManager.getCaptureUri() == null) {
|
|
|
|
Log.w(TAG, "No image available.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Uri mediaUri = BlobProvider.getInstance()
|
|
|
|
.forData(getContentResolver().openInputStream(attachmentManager.getCaptureUri()), 0L)
|
|
|
|
.withMimeType(MediaUtil.IMAGE_JPEG)
|
|
|
|
.createForSingleSessionOnDisk(this);
|
|
|
|
|
|
|
|
getContentResolver().delete(attachmentManager.getCaptureUri(), null, null);
|
|
|
|
|
|
|
|
setMedia(mediaUri, MediaType.IMAGE);
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
Log.w(TAG, "Could not handle public image", ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
2020-08-29 16:45:11 +02:00
|
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
2014-06-29 05:40:57 +02:00
|
|
|
MenuInflater inflater = this.getMenuInflater();
|
2012-07-19 23:22:03 +02:00
|
|
|
menu.clear();
|
|
|
|
|
2020-06-07 01:26:21 +02:00
|
|
|
GroupActiveState groupActiveState = groupViewModel.getGroupActiveState().getValue();
|
|
|
|
boolean isActiveGroup = groupActiveState != null && groupActiveState.isActiveGroup();
|
|
|
|
boolean isActiveV2Group = groupActiveState != null && groupActiveState.isActiveV2Group();
|
2020-06-08 17:31:55 +02:00
|
|
|
boolean isInActiveGroup = groupActiveState != null && !groupActiveState.isActiveGroup();
|
2020-06-07 01:26:21 +02:00
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
if (isInMessageRequest()) {
|
2020-06-07 01:26:21 +02:00
|
|
|
if (isActiveGroup) {
|
2020-02-19 23:08:34 +01:00
|
|
|
inflater.inflate(R.menu.conversation_message_requests_group, menu);
|
|
|
|
}
|
|
|
|
|
|
|
|
inflater.inflate(R.menu.conversation_message_requests, menu);
|
|
|
|
|
|
|
|
if (recipient != null && recipient.get().isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
|
|
|
|
else inflater.inflate(R.menu.conversation_unmuted, menu);
|
|
|
|
|
2020-08-29 16:45:11 +02:00
|
|
|
super.onCreateOptionsMenu(menu);
|
2020-02-19 23:08:34 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-26 05:04:13 +02:00
|
|
|
if (isSecureText) {
|
2019-08-07 20:22:51 +02:00
|
|
|
if (recipient.get().getExpireMessages() > 0) {
|
2020-06-08 17:31:55 +02:00
|
|
|
if (!isInActiveGroup) {
|
|
|
|
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
|
|
|
}
|
2019-12-03 21:26:05 +01:00
|
|
|
titleView.showExpiring(recipient);
|
2016-09-26 05:04:13 +02:00
|
|
|
} else {
|
2020-06-08 17:31:55 +02:00
|
|
|
if (!isInActiveGroup) {
|
|
|
|
inflater.inflate(R.menu.conversation_expiring_off, menu);
|
|
|
|
}
|
2019-12-03 21:26:05 +01:00
|
|
|
titleView.clearExpiring();
|
2016-09-26 05:04:13 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-16 05:23:56 +02:00
|
|
|
|
2012-08-08 04:03:28 +02:00
|
|
|
if (isSingleConversation()) {
|
2016-11-09 18:37:40 +01:00
|
|
|
if (isSecureText) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
|
|
|
else inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
2012-10-21 23:34:09 +02:00
|
|
|
} else if (isGroupConversation()) {
|
|
|
|
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);
|
|
|
|
if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
|
|
|
|
menu.findItem(R.id.menu_distribution_broadcast).setChecked(true);
|
|
|
|
} else {
|
|
|
|
menu.findItem(R.id.menu_distribution_conversation).setChecked(true);
|
|
|
|
}
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
2020-10-05 18:59:00 +02:00
|
|
|
|
|
|
|
inflater.inflate(R.menu.conversation_active_group_options, menu);
|
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
|
|
|
|
2015-09-29 23:26:37 +02:00
|
|
|
if (isSingleConversation() && isSecureText) {
|
|
|
|
inflater.inflate(R.menu.conversation_secure, menu);
|
|
|
|
} else if (isSingleConversation()) {
|
|
|
|
inflater.inflate(R.menu.conversation_insecure, menu);
|
|
|
|
}
|
|
|
|
|
2020-06-08 01:28:19 +02:00
|
|
|
if (recipient != null && recipient.get().isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
|
|
|
|
else inflater.inflate(R.menu.conversation_unmuted, menu);
|
2015-06-09 16:37:20 +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-08-07 20:22:51 +02:00
|
|
|
if (recipient != null && recipient.get().isLocalNumber()) {
|
2019-12-03 21:26:05 +01:00
|
|
|
if (isSecureText) {
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_call_secure);
|
|
|
|
hideMenuItem(menu, R.id.menu_video_secure);
|
2019-12-03 21:26:05 +01:00
|
|
|
} else {
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_call_insecure);
|
2019-12-03 21:26:05 +01:00
|
|
|
}
|
2019-01-14 08:30:54 +01:00
|
|
|
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_mute_notifications);
|
2019-01-14 08:30:54 +01:00
|
|
|
}
|
|
|
|
|
2020-04-22 17:12:08 +02:00
|
|
|
if (recipient != null && recipient.get().isBlocked()) {
|
|
|
|
if (isSecureText) {
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_call_secure);
|
|
|
|
hideMenuItem(menu, R.id.menu_video_secure);
|
|
|
|
hideMenuItem(menu, R.id.menu_expiring_messages);
|
|
|
|
hideMenuItem(menu, R.id.menu_expiring_messages_off);
|
2020-04-22 17:12:08 +02:00
|
|
|
} else {
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_call_insecure);
|
2020-04-22 17:12:08 +02:00
|
|
|
}
|
2020-04-23 17:06:52 +02:00
|
|
|
|
2020-04-27 17:42:34 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_mute_notifications);
|
2020-04-22 17:12:08 +02:00
|
|
|
}
|
|
|
|
|
2020-06-17 17:46:59 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_group_recipients);
|
2020-06-07 14:31:18 +02:00
|
|
|
|
2020-06-07 01:26:21 +02:00
|
|
|
if (isActiveV2Group) {
|
2020-05-26 19:19:05 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_mute_notifications);
|
|
|
|
hideMenuItem(menu, R.id.menu_conversation_settings);
|
2020-10-05 18:59:00 +02:00
|
|
|
} else if (isGroupConversation()) {
|
2020-06-06 03:30:46 +02:00
|
|
|
hideMenuItem(menu, R.id.menu_conversation_settings);
|
2020-05-26 19:19:05 +02:00
|
|
|
}
|
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
searchViewItem = menu.findItem(R.id.menu_search);
|
|
|
|
|
|
|
|
SearchView searchView = (SearchView) searchViewItem.getActionView();
|
|
|
|
SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onQueryTextSubmit(String query) {
|
2019-10-21 06:49:06 +02:00
|
|
|
searchViewModel.onQueryUpdated(query, threadId, true);
|
2019-02-01 18:06:59 +01:00
|
|
|
searchNav.showLoading();
|
|
|
|
fragment.onSearchQueryUpdated(query);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onQueryTextChange(String query) {
|
2019-10-21 06:49:06 +02:00
|
|
|
searchViewModel.onQueryUpdated(query, threadId, false);
|
2019-02-01 18:06:59 +01:00
|
|
|
searchNav.showLoading();
|
|
|
|
fragment.onSearchQueryUpdated(query);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
searchViewItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onMenuItemActionExpand(MenuItem item) {
|
|
|
|
searchView.setOnQueryTextListener(queryListener);
|
|
|
|
searchViewModel.onSearchOpened();
|
|
|
|
searchNav.setVisibility(View.VISIBLE);
|
|
|
|
searchNav.setData(0, 0);
|
|
|
|
inputPanel.setVisibility(View.GONE);
|
|
|
|
|
|
|
|
for (int i = 0; i < menu.size(); i++) {
|
|
|
|
if (!menu.getItem(i).equals(searchViewItem)) {
|
|
|
|
menu.getItem(i).setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onMenuItemActionCollapse(MenuItem item) {
|
|
|
|
searchView.setOnQueryTextListener(null);
|
|
|
|
searchViewModel.onSearchClosed();
|
|
|
|
searchNav.setVisibility(View.GONE);
|
|
|
|
inputPanel.setVisibility(View.VISIBLE);
|
|
|
|
fragment.onSearchQueryUpdated(null);
|
2020-04-22 17:12:08 +02:00
|
|
|
setBlockedUserState(recipient.get(), isSecureText, isDefaultSms);
|
2019-02-01 18:06:59 +01:00
|
|
|
invalidateOptionsMenu();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-08-29 16:45:11 +02:00
|
|
|
super.onCreateOptionsMenu(menu);
|
2012-07-19 23:22:03 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
super.onOptionsItemSelected(item);
|
|
|
|
switch (item.getItemId()) {
|
2018-09-27 20:45:56 +02:00
|
|
|
case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
|
2019-12-03 21:26:05 +01:00
|
|
|
case R.id.menu_video_secure: handleVideo(getRecipient()); return true;
|
2018-09-27 20:45:56 +02:00
|
|
|
case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
|
2015-01-19 03:11:30 +01: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;
|
2014-06-04 01:24:44 +02:00
|
|
|
case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
|
2015-10-04 05:13:34 +02:00
|
|
|
case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
|
2013-04-26 03:59:49 +02:00
|
|
|
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
|
|
|
|
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
|
|
|
|
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
|
2020-06-16 19:07:25 +02:00
|
|
|
case R.id.menu_group_settings: handleManageGroup(); return true;
|
2014-02-21 00:41:52 +01:00
|
|
|
case R.id.menu_leave: handleLeavePushGroup(); return true;
|
2014-12-13 03:31:20 +01:00
|
|
|
case R.id.menu_invite: handleInviteLink(); 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;
|
2015-07-08 21:22:51 +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;
|
2020-06-10 16:28:34 +02:00
|
|
|
case android.R.id.home: onNavigateUp(); return true;
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:24:04 +02:00
|
|
|
@Override
|
|
|
|
public boolean onMenuOpened(int featureId, Menu menu) {
|
|
|
|
if (menu == null) {
|
|
|
|
return super.onMenuOpened(featureId, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SignalStore.uiHints().hasSeenGroupSettingsMenuToast()) {
|
|
|
|
MenuItem settingsMenuItem = menu.findItem(R.id.menu_group_settings);
|
|
|
|
|
|
|
|
if (settingsMenuItem != null && settingsMenuItem.isVisible()) {
|
|
|
|
Toast toast = Toast.makeText(this, R.string.ConversationActivity__more_options_now_in_group_settings, Toast.LENGTH_SHORT);
|
|
|
|
|
|
|
|
toast.setGravity(Gravity.CENTER, 0, 0);
|
|
|
|
toast.show();
|
|
|
|
|
|
|
|
SignalStore.uiHints().markHasSeenGroupSettingsMenuToast();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.onMenuOpened(featureId, menu);
|
|
|
|
}
|
|
|
|
|
2013-06-28 05:57:27 +02:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.d(TAG, "onBackPressed()");
|
2020-02-05 16:20:13 +01:00
|
|
|
if (reactionOverlay.isShowing()) reactionOverlay.hide();
|
|
|
|
else 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-16 01:29:00 +01:00
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
|
|
public void onEvent(ReminderUpdateEvent event) {
|
2019-11-12 15:18:57 +01:00
|
|
|
updateReminders();
|
2017-11-16 01:29:00 +01:00
|
|
|
}
|
|
|
|
|
2017-11-25 07:00:30 +01:00
|
|
|
@Override
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
|
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
|
|
|
}
|
|
|
|
|
2020-01-30 04:13:44 +01:00
|
|
|
@Override
|
|
|
|
public void onAttachmentMediaClicked(@NonNull Media media) {
|
|
|
|
linkPreviewViewModel.onUserCancel();
|
|
|
|
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
|
2020-02-04 06:00:06 +01:00
|
|
|
container.hideCurrentInput(composeText);
|
2020-01-30 04:13:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAttachmentSelectorClicked(@NonNull AttachmentKeyboardButton button) {
|
|
|
|
switch (button) {
|
|
|
|
case GALLERY:
|
|
|
|
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport());
|
|
|
|
break;
|
|
|
|
case GIF:
|
|
|
|
AttachmentManager.selectGif(this, PICK_GIF, !isSecureText, recipient.get().getColor().toConversationColor(this));
|
|
|
|
break;
|
|
|
|
case FILE:
|
|
|
|
AttachmentManager.selectDocument(this, PICK_DOCUMENT);
|
|
|
|
break;
|
|
|
|
case CONTACT:
|
|
|
|
AttachmentManager.selectContactInfo(this, PICK_CONTACT);
|
|
|
|
break;
|
|
|
|
case LOCATION:
|
|
|
|
AttachmentManager.selectLocation(this, PICK_LOCATION);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-04 06:00:06 +01:00
|
|
|
|
|
|
|
container.hideCurrentInput(composeText);
|
2020-01-30 04:13:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAttachmentPermissionsRequested() {
|
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
|
|
.onAllGranted(() -> viewModel.onAttachmentKeyboardOpen())
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////// Event Handlers
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2016-08-16 05:23:56 +02:00
|
|
|
private void handleSelectMessageExpiration() {
|
2020-04-27 21:27:31 +02:00
|
|
|
boolean activeGroup = isActiveGroup();
|
|
|
|
|
|
|
|
if (isPushGroupConversation() && !activeGroup) {
|
2017-01-03 22:36:34 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-27 21:27:31 +02:00
|
|
|
ExpirationDialog.show(this, recipient.get().getExpireMessages(),
|
|
|
|
expirationTime ->
|
|
|
|
SimpleTask.run(
|
|
|
|
getLifecycle(),
|
|
|
|
() -> {
|
|
|
|
if (activeGroup) {
|
|
|
|
try {
|
|
|
|
GroupManager.updateGroupTimer(ConversationActivity.this, getRecipient().requireGroupId().requirePush(), expirationTime);
|
2020-07-20 20:20:56 +02:00
|
|
|
} catch (GroupChangeException | IOException e) {
|
2020-04-27 21:27:31 +02:00
|
|
|
Log.w(TAG, e);
|
2020-07-20 20:20:56 +02:00
|
|
|
return GroupChangeResult.failure(GroupChangeFailureReason.fromException(e));
|
2020-04-27 21:27:31 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient.getId(), expirationTime);
|
|
|
|
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000L);
|
|
|
|
MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null);
|
|
|
|
}
|
2020-07-20 20:20:56 +02:00
|
|
|
return GroupChangeResult.SUCCESS;
|
2020-04-27 21:27:31 +02:00
|
|
|
},
|
2020-07-20 20:20:56 +02:00
|
|
|
(changeResult) -> {
|
2020-07-22 20:34:29 +02:00
|
|
|
if (!changeResult.isSuccess()) {
|
2020-07-20 20:20:56 +02:00
|
|
|
Toast.makeText(ConversationActivity.this, GroupErrors.getUserDisplayMessage(changeResult.getFailureReason()), Toast.LENGTH_SHORT).show();
|
2020-04-27 21:27:31 +02:00
|
|
|
} else {
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
if (fragment != null) fragment.setLastSeen(0);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
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 -> {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
2019-08-07 20:22:51 +02:00
|
|
|
.setMuted(recipient.getId(), 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
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-08 21:22:51 +02:00
|
|
|
private void handleConversationSettings() {
|
2020-06-17 17:46:59 +02:00
|
|
|
if (isGroupConversation()) {
|
2020-06-16 19:07:25 +02:00
|
|
|
handleManageGroup();
|
2020-06-02 21:09:48 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
if (isInMessageRequest()) return;
|
|
|
|
|
2020-06-18 19:00:06 +02:00
|
|
|
Intent intent = ManageRecipientActivity.newIntentFromConversation(this, recipient.getId());
|
2017-10-04 01:27:12 +02:00
|
|
|
startActivitySceneTransition(intent, titleView.findViewById(R.id.contact_photo_image), "avatar");
|
2015-07-08 21:22:51 +02:00
|
|
|
}
|
|
|
|
|
2015-06-09 16:37:20 +02:00
|
|
|
private void handleUnmuteNotifications() {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
2017-08-22 03:47:37 +02:00
|
|
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
2019-08-07 20:22:51 +02:00
|
|
|
.setMuted(recipient.getId(), 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() {
|
2020-04-23 17:06:52 +02:00
|
|
|
BlockUnblockDialog.showUnblockFor(this, getLifecycle(), recipient.get(), () -> {
|
|
|
|
SignalExecutors.BOUNDED.execute(() -> {
|
|
|
|
RecipientUtil.unblock(ConversationActivity.this, recipient.get());
|
|
|
|
});
|
|
|
|
});
|
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
|
|
|
}
|
|
|
|
|
2017-12-26 00:57:33 +01:00
|
|
|
private void handleRegisterForSignal() {
|
2019-10-17 00:37:08 +02:00
|
|
|
startActivity(RegistrationNavigationActivity.newIntentForReRegistration(this));
|
2017-12-26 00:57:33 +01:00
|
|
|
}
|
|
|
|
|
2014-12-13 03:31:20 +01:00
|
|
|
private void handleInviteLink() {
|
2019-05-23 13:02:15 +02:00
|
|
|
String inviteText = getString(R.string.ConversationActivity_lets_switch_to_signal, getString(R.string.install_url));
|
2019-03-15 23:31:52 +01:00
|
|
|
|
|
|
|
if (isDefaultSms) {
|
|
|
|
composeText.appendInvite(inviteText);
|
|
|
|
} else {
|
|
|
|
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
2019-09-07 05:40:06 +02:00
|
|
|
intent.setData(Uri.parse("smsto:" + recipient.get().requireSmsAddress()));
|
2019-03-15 23:31:52 +01:00
|
|
|
intent.putExtra("sms_body", inviteText);
|
|
|
|
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
|
|
|
|
startActivity(intent);
|
2014-12-13 03:31:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-04 05:13:34 +02:00
|
|
|
private void handleResetSecureSession() {
|
2015-05-20 23:36:30 +02:00
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
2015-11-19 17:05:31 +01:00
|
|
|
builder.setTitle(R.string.ConversationActivity_reset_secure_session_question);
|
2015-03-24 13:44:22 +01:00
|
|
|
builder.setIconAttribute(R.attr.dialog_alert_icon);
|
2012-07-19 23:22:03 +02:00
|
|
|
builder.setCancelable(true);
|
2015-11-19 17:05:31 +01:00
|
|
|
builder.setMessage(R.string.ConversationActivity_this_may_help_if_youre_having_encryption_problems);
|
2017-12-05 20:52:03 +01:00
|
|
|
builder.setPositiveButton(R.string.ConversationActivity_reset, (dialog, which) -> {
|
|
|
|
if (isSingleConversation()) {
|
|
|
|
final Context context = getApplicationContext();
|
2014-04-22 23:33:29 +02:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
OutgoingEndSessionMessage endSessionMessage =
|
|
|
|
new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipient(), "TERMINATE", 0, -1));
|
2014-04-22 23:33:29 +02:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
|
|
|
|
@Override
|
|
|
|
protected Long doInBackground(OutgoingEndSessionMessage... messages) {
|
2018-01-25 04:17:44 +01:00
|
|
|
return MessageSender.send(context, messages[0], threadId, false, null);
|
2017-12-05 20:52:03 +01:00
|
|
|
}
|
2014-11-08 20:35:58 +01:00
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Long result) {
|
|
|
|
sendComplete(result);
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, endSessionMessage);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
});
|
2015-11-19 17:05:31 +01:00
|
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
2012-07-19 23:22:03 +02:00
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
|
2015-01-19 03:11:30 +01:00
|
|
|
private void handleViewMedia() {
|
2019-12-03 17:05:03 +01:00
|
|
|
startActivity(MediaOverviewActivity.forThread(this, threadId));
|
2015-01-19 03:11:30 +01:00
|
|
|
}
|
|
|
|
|
2018-04-08 12:55:30 +02:00
|
|
|
private void handleAddShortcut() {
|
2019-09-07 05:40:06 +02:00
|
|
|
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().getId());
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2020-03-25 16:30:15 +01:00
|
|
|
final Context context = getApplicationContext();
|
|
|
|
final Recipient recipient = this.recipient.get();
|
|
|
|
|
|
|
|
GlideApp.with(this)
|
|
|
|
.asBitmap()
|
|
|
|
.load(recipient.getContactPhoto())
|
|
|
|
.error(recipient.getFallbackContactPhoto().asDrawable(this, recipient.getColor().toAvatarColor(this), false))
|
|
|
|
.into(new CustomTarget<Bitmap>() {
|
|
|
|
@Override
|
|
|
|
public void onLoadFailed(@Nullable Drawable errorDrawable) {
|
|
|
|
if (errorDrawable == null) {
|
|
|
|
throw new AssertionError();
|
|
|
|
}
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2020-03-25 16:30:15 +01:00
|
|
|
Log.w(TAG, "Utilizing fallback photo for shortcut for recipient " + recipient.getId());
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2020-03-25 16:30:15 +01:00
|
|
|
SimpleTask.run(() -> DrawableUtil.toBitmap(errorDrawable, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE),
|
|
|
|
bitmap -> addIconToHomeScreen(context, bitmap, recipient));
|
|
|
|
}
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2020-03-25 16:30:15 +01:00
|
|
|
@Override
|
|
|
|
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
|
|
|
SimpleTask.run(() -> BitmapUtil.createScaledBitmap(resource, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE),
|
|
|
|
bitmap -> addIconToHomeScreen(context, bitmap, recipient));
|
|
|
|
}
|
2018-04-08 12:55:30 +02:00
|
|
|
|
2020-03-25 16:30:15 +01:00
|
|
|
@Override
|
|
|
|
public void onLoadCleared(@Nullable Drawable placeholder) {
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void addIconToHomeScreen(@NonNull Context context,
|
|
|
|
@NonNull Bitmap bitmap,
|
|
|
|
@NonNull Recipient recipient)
|
|
|
|
{
|
|
|
|
IconCompat icon = IconCompat.createWithAdaptiveBitmap(bitmap);
|
|
|
|
String name = recipient.isLocalNumber() ? context.getString(R.string.note_to_self)
|
|
|
|
: recipient.getDisplayName(context);
|
|
|
|
|
|
|
|
ShortcutInfoCompat shortcutInfoCompat = new ShortcutInfoCompat.Builder(context, recipient.getId().serialize() + '-' + System.currentTimeMillis())
|
|
|
|
.setShortLabel(name)
|
|
|
|
.setIcon(icon)
|
|
|
|
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getId()))
|
|
|
|
.build();
|
|
|
|
|
|
|
|
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null)) {
|
|
|
|
Toast.makeText(context, context.getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap.recycle();
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-16 21:52:24 +02:00
|
|
|
LeaveGroupDialog.handleLeavePushGroup(this, getRecipient().requireGroupId().requirePush(), this::finish);
|
2014-02-22 19:54:43 +01:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:07:25 +02:00
|
|
|
private void handleManageGroup() {
|
|
|
|
startActivityForResult(ManageGroupActivity.newIntent(ConversationActivity.this, recipient.get().requireGroupId()),
|
2020-06-07 19:44:30 +02:00
|
|
|
GROUP_EDIT,
|
|
|
|
ManageGroupActivity.createTransitionBundle(this, titleView.findViewById(R.id.contact_photo_image)));
|
2020-04-27 21:27:31 +02:00
|
|
|
}
|
|
|
|
|
2013-04-26 03:59:49 +02:00
|
|
|
private void handleDistributionBroadcastEnabled(MenuItem item) {
|
|
|
|
distributionType = ThreadDatabase.DistributionTypes.BROADCAST;
|
|
|
|
item.setChecked(true);
|
|
|
|
|
|
|
|
if (threadId != -1) {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
|
|
|
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST);
|
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleDistributionConversationEnabled(MenuItem item) {
|
|
|
|
distributionType = ThreadDatabase.DistributionTypes.CONVERSATION;
|
|
|
|
item.setChecked(true);
|
|
|
|
|
|
|
|
if (threadId != -1) {
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
|
|
|
|
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION);
|
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2013-04-26 03:59:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-27 20:45:56 +02:00
|
|
|
private void handleDial(final Recipient recipient, boolean isSecure) {
|
2015-09-22 02:41:27 +02:00
|
|
|
if (recipient == null) return;
|
|
|
|
|
2018-09-27 20:45:56 +02:00
|
|
|
if (isSecure) {
|
2018-04-27 02:03:54 +02:00
|
|
|
CommunicationActions.startVoiceCall(this, recipient);
|
2015-09-22 02:41:27 +02:00
|
|
|
} else {
|
2020-03-05 17:38:05 +01:00
|
|
|
CommunicationActions.startInsecureCall(this, recipient);
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2019-12-03 21:26:05 +01:00
|
|
|
private void handleVideo(final Recipient recipient) {
|
|
|
|
if (recipient == null) return;
|
|
|
|
|
|
|
|
CommunicationActions.startVideoCall(this, recipient);
|
|
|
|
}
|
|
|
|
|
2012-10-21 23:34:09 +02:00
|
|
|
private void handleDisplayGroupRecipients() {
|
2020-04-27 21:27:31 +02:00
|
|
|
new GroupMembersDialog(this, getRecipient()).display();
|
2012-10-21 23:34:09 +02:00
|
|
|
}
|
|
|
|
|
2014-06-04 01:24:44 +02:00
|
|
|
private void handleAddToContacts() {
|
2019-09-07 05:40:06 +02:00
|
|
|
if (recipient.get().isGroup()) return;
|
2017-07-26 18:59:15 +02:00
|
|
|
|
2015-12-12 07:57:18 +01:00
|
|
|
try {
|
2019-08-07 20:22:51 +02:00
|
|
|
startActivityForResult(RecipientExporter.export(recipient.get()).asAddContactIntent(), ADD_CONTACT);
|
2015-12-12 07:57:18 +01:00
|
|
|
} catch (ActivityNotFoundException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
2013-10-17 02:28:36 +02:00
|
|
|
}
|
|
|
|
|
2017-10-05 20:07:44 +02:00
|
|
|
private boolean handleDisplayQuickContact() {
|
2020-02-19 23:08:34 +01:00
|
|
|
if (isInMessageRequest() || recipient.get().isGroup()) return false;
|
2017-10-05 20:07:44 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (recipient.get().getContactUri() != null) {
|
|
|
|
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.get().getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
|
2017-10-05 20:07:44 +02:00
|
|
|
} else {
|
|
|
|
handleAddToContacts();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private void handleAddAttachment() {
|
2015-09-29 23:26:37 +02:00
|
|
|
if (this.isMmsEnabled || isSecureText) {
|
2020-01-30 04:13:44 +01:00
|
|
|
viewModel.getRecentMedia().removeObservers(this);
|
|
|
|
|
|
|
|
if (attachmentKeyboardStub.resolved() && container.isInputOpen() && container.getCurrentInput() == attachmentKeyboardStub.get()) {
|
|
|
|
container.showSoftkey(composeText);
|
|
|
|
} else {
|
|
|
|
viewModel.getRecentMedia().observe(this, media -> attachmentKeyboardStub.get().onMediaChanged(media));
|
|
|
|
attachmentKeyboardStub.get().setCallback(this);
|
|
|
|
container.show(composeText, attachmentKeyboardStub.get());
|
|
|
|
|
|
|
|
viewModel.onAttachmentKeyboardOpen();
|
2017-01-19 03:46:40 +01:00
|
|
|
}
|
2013-03-05 02:43:04 +01:00
|
|
|
} else {
|
2013-09-16 09:55:01 +02:00
|
|
|
handleManualMmsRequired();
|
2013-03-05 02:43:04 +01:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2013-09-16 09:55:01 +02:00
|
|
|
private void handleManualMmsRequired() {
|
|
|
|
Toast.makeText(this, R.string.MmsDownloader_error_reading_mms_settings, Toast.LENGTH_LONG).show();
|
|
|
|
|
2017-12-05 20:52:03 +01:00
|
|
|
Bundle extras = getIntent().getExtras();
|
2013-09-16 09:55:01 +02:00
|
|
|
Intent intent = new Intent(this, PromptMmsActivity.class);
|
2017-12-05 20:52:03 +01:00
|
|
|
if (extras != null) intent.putExtras(extras);
|
2013-09-16 09:55:01 +02:00
|
|
|
startActivity(intent);
|
|
|
|
}
|
|
|
|
|
2020-06-26 17:10:54 +02:00
|
|
|
private void handleRecentSafetyNumberChange() {
|
|
|
|
List<IdentityRecord> records = identityRecords.getUnverifiedRecords();
|
|
|
|
records.addAll(identityRecords.getUntrustedRecords());
|
2020-08-27 22:34:26 +02:00
|
|
|
SafetyNumberChangeDialog.show(getSupportFragmentManager(), records);
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
|
|
|
|
2020-06-26 17:10:54 +02:00
|
|
|
@Override
|
|
|
|
public void onSendAnywayAfterSafetyNumberChange() {
|
|
|
|
initializeIdentityRecords().addListener(new AssertedSuccessListener<Boolean>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(Boolean result) {
|
|
|
|
sendMessage();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-06-07 03:03:09 +02:00
|
|
|
|
2020-06-26 17:10:54 +02:00
|
|
|
@Override
|
|
|
|
public void onMessageResentAfterSafetyNumberChange() {
|
|
|
|
initializeIdentityRecords().addListener(new AssertedSuccessListener<Boolean>() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess(Boolean result) { }
|
|
|
|
});
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
|
|
|
|
2020-08-27 22:34:26 +02:00
|
|
|
@Override
|
|
|
|
public void onCanceled() { }
|
|
|
|
|
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 + ")");
|
2017-09-07 02:54:32 +02:00
|
|
|
|
|
|
|
this.isSecureText = isSecureText;
|
|
|
|
this.isDefaultSms = isDefaultSms;
|
|
|
|
this.isSecurityInitialized = true;
|
2015-09-29 23:26:37 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
boolean isMediaMessage = recipient.get().isMmsGroup() || attachmentManager.isAttachmentPresent();
|
2015-09-24 00:47:48 +02:00
|
|
|
|
|
|
|
sendButton.resetAvailableTransports(isMediaMessage);
|
|
|
|
|
2017-12-26 00:57:33 +01:00
|
|
|
if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE);
|
2019-08-07 20:22:51 +02:00
|
|
|
if (recipient.get().isPushGroup()) sendButton.disableTransport(Type.SMS);
|
2015-09-24 00:47:48 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (!recipient.get().isPushGroup() && recipient.get().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);
|
|
|
|
}
|
2015-09-24 00:47:48 +02:00
|
|
|
|
|
|
|
calculateCharactersRemaining();
|
|
|
|
supportInvalidateOptionsMenu();
|
2019-08-07 20:22:51 +02:00
|
|
|
setBlockedUserState(recipient.get(), isSecureText, isDefaultSms);
|
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<>();
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
final CharSequence draftText = getIntent().getCharSequenceExtra(TEXT_EXTRA);
|
2020-07-10 15:30:00 +02:00
|
|
|
final Uri draftMedia = getIntent().getData();
|
|
|
|
final String draftContentType = getIntent().getType();
|
|
|
|
final MediaType draftMediaType = MediaType.from(draftContentType);
|
|
|
|
final List<Media> mediaList = getIntent().getParcelableArrayListExtra(MEDIA_EXTRA);
|
|
|
|
final StickerLocator stickerLocator = getIntent().getParcelableExtra(STICKER_EXTRA);
|
|
|
|
final boolean borderless = getIntent().getBooleanExtra(BORDERLESS_EXTRA, false);
|
2019-04-17 16:21:30 +02:00
|
|
|
|
|
|
|
if (stickerLocator != null && draftMedia != null) {
|
2020-02-05 22:34:54 +01:00
|
|
|
Log.d(TAG, "Handling shared sticker.");
|
2020-09-02 18:46:58 +02:00
|
|
|
sendSticker(stickerLocator, Objects.requireNonNull(draftContentType), draftMedia, 0, true);
|
2019-04-17 16:21:30 +02:00
|
|
|
return new SettableFuture<>(false);
|
|
|
|
}
|
2018-11-20 18:59:23 +01:00
|
|
|
|
2020-07-10 15:30:00 +02:00
|
|
|
if (draftMedia != null && draftContentType != null && borderless) {
|
|
|
|
SimpleTask.run(getLifecycle(),
|
|
|
|
() -> getKeyboardImageDetails(draftMedia),
|
|
|
|
details -> sendKeyboardImage(draftMedia, draftContentType, details));
|
|
|
|
return new SettableFuture<>(false);
|
|
|
|
}
|
|
|
|
|
2018-11-20 18:59:23 +01:00
|
|
|
if (!Util.isEmpty(mediaList)) {
|
2020-02-05 22:34:54 +01:00
|
|
|
Log.d(TAG, "Handling shared Media.");
|
2019-08-07 20:22:51 +02:00
|
|
|
Intent sendIntent = MediaSendActivity.buildEditorIntent(this, mediaList, recipient.get(), draftText, sendButton.getSelectedTransport());
|
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) {
|
2020-02-05 22:34:54 +01:00
|
|
|
Log.d(TAG, "Handling shared Data.");
|
2018-07-25 17:30:48 +02:00
|
|
|
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() {
|
2020-08-26 17:51:25 +02:00
|
|
|
groupViewModel.getSelfMemberLevel().observe(this, selfMemberShip -> {
|
|
|
|
boolean canSendMessages;
|
|
|
|
boolean leftGroup;
|
|
|
|
boolean canCancelRequest;
|
|
|
|
|
|
|
|
if (selfMemberShip == null) {
|
|
|
|
leftGroup = false;
|
|
|
|
canSendMessages = true;
|
|
|
|
canCancelRequest = false;
|
|
|
|
} else {
|
|
|
|
switch (selfMemberShip) {
|
|
|
|
case NOT_A_MEMBER:
|
|
|
|
leftGroup = true;
|
|
|
|
canSendMessages = false;
|
|
|
|
canCancelRequest = false;
|
|
|
|
break;
|
|
|
|
case PENDING_MEMBER:
|
|
|
|
leftGroup = false;
|
|
|
|
canSendMessages = false;
|
|
|
|
canCancelRequest = false;
|
|
|
|
break;
|
|
|
|
case REQUESTING_MEMBER:
|
|
|
|
leftGroup = false;
|
|
|
|
canSendMessages = false;
|
|
|
|
canCancelRequest = true;
|
|
|
|
break;
|
|
|
|
case FULL_MEMBER:
|
|
|
|
case ADMINISTRATOR:
|
|
|
|
leftGroup = false;
|
|
|
|
canSendMessages = true;
|
|
|
|
canCancelRequest = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new AssertionError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
noLongerMemberBanner.setVisibility(leftGroup ? View.VISIBLE : View.GONE);
|
|
|
|
requestingMemberBanner.setVisibility(canCancelRequest ? View.VISIBLE : View.GONE);
|
|
|
|
if (canCancelRequest) {
|
|
|
|
cancelJoinRequest.setOnClickListener(v -> ConversationGroupViewModel.onCancelJoinRequest(getRecipient(), new AsynchronousCallback.MainThread<Void, GroupChangeFailureReason>() {
|
|
|
|
@Override
|
|
|
|
public void onComplete(@Nullable Void result) {
|
|
|
|
Log.d(TAG, "Cancel request complete");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(@Nullable GroupChangeFailureReason error) {
|
|
|
|
Log.d(TAG, "Cancel join request failed " + error);
|
|
|
|
Toast.makeText(ConversationActivity.this, GroupErrors.getUserDisplayMessage(error), Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
}.toWorkerCallback()));
|
|
|
|
}
|
|
|
|
|
|
|
|
inputPanel.setVisibility(canSendMessages ? View.VISIBLE : View.GONE);
|
|
|
|
inputPanel.setEnabled(canSendMessages);
|
|
|
|
sendButton.setEnabled(canSendMessages);
|
|
|
|
attachButton.setEnabled(canSendMessages);
|
2020-06-07 01:26:21 +02:00
|
|
|
});
|
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<>();
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
new AsyncTask<Void, Void, Pair<Drafts, CharSequence>>() {
|
2013-02-04 09:13:07 +01:00
|
|
|
@Override
|
2020-08-05 22:45:52 +02:00
|
|
|
protected Pair<Drafts, CharSequence> doInBackground(Void... params) {
|
|
|
|
Context context = ConversationActivity.this;
|
|
|
|
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(context);
|
|
|
|
Drafts results = draftDatabase.getDrafts(threadId);
|
|
|
|
Draft mentionsDraft = results.getDraftOfType(Draft.MENTION);
|
|
|
|
Spannable updatedText = null;
|
|
|
|
|
|
|
|
if (mentionsDraft != null) {
|
|
|
|
String text = results.getDraftOfType(Draft.TEXT).getValue();
|
|
|
|
List<Mention> mentions = MentionUtil.bodyRangeListToMentions(context, Base64.decodeOrThrow(mentionsDraft.getValue()));
|
|
|
|
UpdatedBodyAndMentions updated = MentionUtil.updateBodyAndMentionsWithDisplayNames(context, text, mentions);
|
|
|
|
|
|
|
|
updatedText = new SpannableString(updated.getBody());
|
|
|
|
MentionAnnotation.setMentionAnnotations(updatedText, updated.getMentions());
|
|
|
|
}
|
2013-02-04 09:13:07 +01:00
|
|
|
|
|
|
|
draftDatabase.clearDrafts(threadId);
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
return new Pair<>(results, updatedText);
|
2013-02-04 09:13:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-08-05 22:45:52 +02:00
|
|
|
protected void onPostExecute(Pair<Drafts, CharSequence> draftsWithUpdatedMentions) {
|
|
|
|
Drafts drafts = Objects.requireNonNull(draftsWithUpdatedMentions.first());
|
|
|
|
CharSequence updatedText = draftsWithUpdatedMentions.second();
|
|
|
|
|
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) {
|
2016-01-04 22:02:22 +01:00
|
|
|
try {
|
2017-12-05 20:52:03 +01:00
|
|
|
switch (draft.getType()) {
|
|
|
|
case Draft.TEXT:
|
2020-08-05 22:45:52 +02:00
|
|
|
composeText.setText(updatedText == null ? draft.getValue() : updatedText);
|
2018-07-25 17:30:48 +02:00
|
|
|
listener.onSuccess(true);
|
2017-12-05 20:52:03 +01:00
|
|
|
break;
|
|
|
|
case Draft.LOCATION:
|
2018-07-25 17:30:48 +02:00
|
|
|
attachmentManager.setLocation(SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints()).addListener(listener);
|
2017-12-05 20:52:03 +01:00
|
|
|
break;
|
|
|
|
case Draft.IMAGE:
|
2018-07-25 17:30:48 +02:00
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE).addListener(listener);
|
2017-12-05 20:52:03 +01:00
|
|
|
break;
|
|
|
|
case Draft.AUDIO:
|
2018-07-25 17:30:48 +02:00
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO).addListener(listener);
|
2017-12-05 20:52:03 +01:00
|
|
|
break;
|
|
|
|
case Draft.VIDEO:
|
2018-07-25 17:30:48 +02:00
|
|
|
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO).addListener(listener);
|
2017-12-05 20:52:03 +01:00
|
|
|
break;
|
2018-04-24 20:09:54 +02:00
|
|
|
case Draft.QUOTE:
|
2018-07-25 17:30:48 +02:00
|
|
|
SettableFuture<Boolean> quoteResult = new SettableFuture<>();
|
|
|
|
new QuoteRestorationTask(draft.getValue(), quoteResult).execute();
|
|
|
|
quoteResult.addListener(listener);
|
2018-04-24 20:09:54 +02:00
|
|
|
break;
|
2016-01-04 22:02:22 +01:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
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) {
|
2017-08-22 19:44:04 +02:00
|
|
|
Context context = ConversationActivity.this;
|
2019-08-07 20:22:51 +02:00
|
|
|
Recipient recipient = params[0].resolve();
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Resolving registered state...");
|
2017-11-25 18:50:36 +01:00
|
|
|
RegisteredState registeredState;
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (recipient.isPushGroup()) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Push group recipient...");
|
2017-11-27 18:44:50 +01:00
|
|
|
registeredState = RegisteredState.REGISTERED;
|
2017-11-25 18:50:36 +01:00
|
|
|
} else {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Checking through resolved recipient");
|
2017-11-25 18:50:36 +01:00
|
|
|
registeredState = recipient.resolve().getRegistered();
|
|
|
|
}
|
|
|
|
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Resolved registered state: " + registeredState);
|
2017-09-15 01:46:12 +02:00
|
|
|
boolean signalEnabled = TextSecurePreferences.isPushRegistered(context);
|
2015-09-29 23:26:37 +02:00
|
|
|
|
2017-08-22 19:44:04 +02:00
|
|
|
if (registeredState == RegisteredState.UNKNOWN) {
|
2017-02-01 06:46:20 +01:00
|
|
|
try {
|
2019-09-07 05:40:06 +02:00
|
|
|
Log.i(TAG, "Refreshing directory for user: " + recipient.getId().serialize());
|
|
|
|
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient, false);
|
2017-02-01 06:46:20 +01:00
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
2015-09-29 23:26:37 +02:00
|
|
|
}
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
2017-02-01 06:46:20 +01:00
|
|
|
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "Returning registered state...");
|
2017-09-15 01:46:12 +02:00
|
|
|
return new boolean[] {registeredState == RegisteredState.REGISTERED && signalEnabled,
|
|
|
|
Util.isDefaultSmsProvider(context)};
|
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-10-14 06:44:01 +02:00
|
|
|
onSecurityUpdated();
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
2019-08-07 20:22:51 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
|
2015-10-02 02:46:47 +02:00
|
|
|
|
|
|
|
return future;
|
2015-09-22 02:41:27 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 06:44:01 +02:00
|
|
|
private void onSecurityUpdated() {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onSecurityUpdated()");
|
2019-11-12 15:18:57 +01:00
|
|
|
updateReminders();
|
2019-08-07 20:22:51 +02:00
|
|
|
updateDefaultSubscriptionId(recipient.get().getDefaultSubscriptionId());
|
2016-02-06 01:10:33 +01:00
|
|
|
}
|
|
|
|
|
2019-11-12 15:18:57 +01:00
|
|
|
private void initializeInsightObserver() {
|
|
|
|
inviteReminderModel = new InviteReminderModel(this, new InviteReminderRepository(this));
|
|
|
|
inviteReminderModel.loadReminder(recipient, this::updateReminders);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void updateReminders() {
|
|
|
|
Optional<Reminder> inviteReminder = inviteReminderModel.getReminder();
|
2017-11-16 01:29:00 +01:00
|
|
|
|
|
|
|
if (UnauthorizedReminder.isEligible(this)) {
|
|
|
|
reminderView.get().showReminder(new UnauthorizedReminder(this));
|
|
|
|
} else if (ExpiredBuildReminder.isEligible()) {
|
|
|
|
reminderView.get().showReminder(new ExpiredBuildReminder(this));
|
2020-09-09 16:22:22 +02:00
|
|
|
reminderView.get().setOnActionClickListener(this::handleReminderAction);
|
2018-06-11 18:37:01 +02:00
|
|
|
} else if (ServiceOutageReminder.isEligible(this)) {
|
2019-10-15 21:47:54 +02:00
|
|
|
ApplicationDependencies.getJobManager().add(new ServiceOutageDetectionJob());
|
2018-06-11 18:37:01 +02:00
|
|
|
reminderView.get().showReminder(new ServiceOutageReminder(this));
|
2017-11-16 01:29:00 +01:00
|
|
|
} else if (TextSecurePreferences.isPushRegistered(this) &&
|
|
|
|
TextSecurePreferences.isShowInviteReminders(this) &&
|
2019-11-12 15:18:57 +01:00
|
|
|
!isSecureText &&
|
|
|
|
inviteReminder.isPresent() &&
|
|
|
|
!recipient.get().isGroup()) {
|
|
|
|
reminderView.get().setOnActionClickListener(this::handleReminderAction);
|
|
|
|
reminderView.get().setOnDismissListener(() -> inviteReminderModel.dismissReminder());
|
|
|
|
reminderView.get().showReminder(inviteReminder.get());
|
2017-01-19 03:46:40 +01:00
|
|
|
} else if (reminderView.resolved()) {
|
|
|
|
reminderView.get().hide();
|
2015-10-14 06:44:01 +02:00
|
|
|
}
|
|
|
|
}
|
2014-04-14 02:55:20 +02:00
|
|
|
|
2019-11-12 15:18:57 +01:00
|
|
|
private void handleReminderAction(@IdRes int reminderActionId) {
|
|
|
|
switch (reminderActionId) {
|
|
|
|
case R.id.reminder_action_invite:
|
|
|
|
handleInviteLink();
|
|
|
|
reminderView.get().requestDismiss();
|
|
|
|
break;
|
|
|
|
case R.id.reminder_action_view_insights:
|
|
|
|
InsightsLauncher.showInsightsDashboard(getSupportFragmentManager());
|
|
|
|
break;
|
2020-09-09 16:22:22 +02:00
|
|
|
case R.id.reminder_action_update_now:
|
|
|
|
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(this);
|
|
|
|
break;
|
2019-11-12 15:18:57 +01:00
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException("Unknown ID: " + reminderActionId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 01:10:33 +01:00
|
|
|
private void updateDefaultSubscriptionId(Optional<Integer> defaultSubscriptionId) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orNull() + ")");
|
2016-02-06 01:10:33 +01:00
|
|
|
sendButton.setDefaultSubscriptionId(defaultSubscriptionId);
|
|
|
|
}
|
|
|
|
|
2013-03-05 02:43:04 +01:00
|
|
|
private void initializeMmsEnabledCheck() {
|
|
|
|
new AsyncTask<Void, Void, Boolean>() {
|
|
|
|
@Override
|
|
|
|
protected Boolean doInBackground(Void... params) {
|
2014-12-29 23:01:02 +01:00
|
|
|
return Util.isMmsCapable(ConversationActivity.this);
|
2013-03-05 02:43:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Boolean isMmsEnabled) {
|
|
|
|
ConversationActivity.this.isMmsEnabled = isMmsEnabled;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2013-03-05 02:43:04 +01:00
|
|
|
}
|
|
|
|
|
2017-06-07 03:03:09 +02:00
|
|
|
private ListenableFuture<Boolean> initializeIdentityRecords() {
|
|
|
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
new AsyncTask<Recipient, Void, Pair<IdentityRecordList, String>>() {
|
2017-06-07 03:03:09 +02:00
|
|
|
@Override
|
2017-08-01 17:56:00 +02:00
|
|
|
protected @NonNull Pair<IdentityRecordList, String> doInBackground(Recipient... params) {
|
2020-06-08 00:52:39 +02:00
|
|
|
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this);
|
|
|
|
List<Recipient> recipients;
|
2017-08-01 17:56:00 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (params[0].isGroup()) {
|
2020-06-08 00:52:39 +02:00
|
|
|
recipients = DatabaseFactory.getGroupDatabase(ConversationActivity.this)
|
|
|
|
.getGroupMembers(params[0].requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
|
2017-08-01 17:56:00 +02:00
|
|
|
} else {
|
2020-06-08 00:52:39 +02:00
|
|
|
recipients = Collections.singletonList(params[0]);
|
2017-08-01 17:56:00 +02:00
|
|
|
}
|
2017-06-07 03:03:09 +02:00
|
|
|
|
2020-06-08 00:52:39 +02:00
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
IdentityRecordList identityRecordList = identityDatabase.getIdentities(recipients);
|
2020-06-07 23:22:35 +02:00
|
|
|
|
2020-06-08 00:52:39 +02:00
|
|
|
Log.i(TAG, String.format(Locale.US, "Loaded %d identities in %d ms", recipients.size(), System.currentTimeMillis() - startTime));
|
2020-06-07 23:22:35 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
String message = null;
|
2017-06-07 03:03:09 +02:00
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
if (identityRecordList.isUnverified()) {
|
2020-06-08 00:52:39 +02:00
|
|
|
message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients());
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
2017-08-01 17:56:00 +02:00
|
|
|
|
|
|
|
return new Pair<>(identityRecordList, message);
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(@NonNull Pair<IdentityRecordList, String> result) {
|
2020-01-08 21:56:51 +01:00
|
|
|
Log.i(TAG, "Got identity records: " + result.first().isUnverified());
|
2020-07-21 17:53:25 +02:00
|
|
|
identityRecords = result.first();
|
2017-06-07 03:03:09 +02:00
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
if (result.second() != null) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.d(TAG, "Replacing banner...");
|
2020-01-08 21:56:51 +01:00
|
|
|
unverifiedBannerView.get().display(result.second(), result.first().getUnverifiedRecords(),
|
2017-06-07 03:03:09 +02:00
|
|
|
new UnverifiedClickedListener(),
|
|
|
|
new UnverifiedDismissedListener());
|
|
|
|
} else if (unverifiedBannerView.resolved()) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.d(TAG, "Clearing banner...");
|
2017-06-07 03:03:09 +02:00
|
|
|
unverifiedBannerView.get().hide();
|
|
|
|
}
|
|
|
|
|
2017-06-22 19:37:26 +02:00
|
|
|
titleView.setVerified(isSecureText && identityRecords.isVerified());
|
2017-06-07 03:03:09 +02:00
|
|
|
|
|
|
|
future.set(true);
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
|
2017-06-07 03:03:09 +02:00
|
|
|
|
|
|
|
return future;
|
|
|
|
}
|
|
|
|
|
2014-12-25 03:32:51 +01:00
|
|
|
private void initializeViews() {
|
2020-02-19 23:08:34 +01:00
|
|
|
titleView = findViewById(R.id.conversation_title_view);
|
|
|
|
buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
|
|
|
|
sendButton = ViewUtil.findById(this, R.id.send_button);
|
|
|
|
attachButton = ViewUtil.findById(this, R.id.attach_button);
|
|
|
|
composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
|
|
|
|
charactersLeft = ViewUtil.findById(this, R.id.space_left);
|
|
|
|
emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
|
|
|
|
attachmentKeyboardStub = ViewUtil.findStubById(this, R.id.attachment_keyboard_stub);
|
|
|
|
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
|
|
|
|
makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
|
|
|
|
registerButton = ViewUtil.findById(this, R.id.register_button);
|
|
|
|
container = ViewUtil.findById(this, R.id.layout_container);
|
|
|
|
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
|
|
|
|
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
|
|
|
|
groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
|
|
|
|
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
|
|
|
|
inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
|
|
|
|
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
|
|
|
|
panelParent = ViewUtil.findById(this, R.id.conversation_activity_panel_parent);
|
|
|
|
searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
|
|
|
|
messageRequestBottomView = ViewUtil.findById(this, R.id.conversation_activity_message_request_bottom_bar);
|
|
|
|
reactionOverlay = ViewUtil.findById(this, R.id.conversation_reaction_scrubber);
|
2020-07-27 15:58:58 +02:00
|
|
|
mentionsSuggestions = ViewUtil.findStubById(this, R.id.conversation_mention_suggestions_stub);
|
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
|
|
|
|
2020-08-26 17:51:25 +02:00
|
|
|
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
|
|
|
|
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
|
|
|
|
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
|
2020-07-17 20:32:53 +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
|
|
|
|
2020-01-30 04:13:44 +01:00
|
|
|
attachmentManager = new AttachmentManager(this, this);
|
|
|
|
audioRecorder = new AudioRecorder(this);
|
|
|
|
typingTextWatcher = new TypingStatusTextWatcher();
|
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);
|
2017-12-05 20:52:03 +01:00
|
|
|
sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> {
|
|
|
|
calculateCharactersRemaining();
|
2019-01-15 09:41:05 +01:00
|
|
|
updateLinkPreviewState();
|
2020-08-27 16:52:47 +02:00
|
|
|
linkPreviewViewModel.onTransportChanged(newTransport.isSms());
|
2017-12-05 20:52:03 +01:00
|
|
|
composeText.setTransport(newTransport);
|
2019-10-07 20:43:36 +02:00
|
|
|
|
|
|
|
buttonToggle.getBackground().setColorFilter(newTransport.getBackgroundColor(), PorterDuff.Mode.MULTIPLY);
|
2017-12-05 20:52:03 +01:00
|
|
|
buttonToggle.getBackground().invalidateSelf();
|
2019-10-07 20:43:36 +02:00
|
|
|
|
2019-04-12 21:22:38 +02:00
|
|
|
if (manuallySelected) recordTransportPreference(newTransport);
|
2015-02-16 11:38:09 +01:00
|
|
|
});
|
|
|
|
|
2017-10-04 01:27:12 +02:00
|
|
|
titleView.setOnClickListener(v -> handleConversationSettings());
|
2017-10-05 20:07:44 +02:00
|
|
|
titleView.setOnLongClickListener(v -> handleDisplayQuickContact());
|
2017-09-13 07:48:30 +02:00
|
|
|
unblockButton.setOnClickListener(v -> handleUnblock());
|
|
|
|
makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms());
|
2017-12-26 00:57:33 +01:00
|
|
|
registerButton.setOnClickListener(v -> handleRegisterForSignal());
|
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
|
|
|
|
2020-04-16 17:30:51 +02:00
|
|
|
if (Camera.getNumberOfCameras() > 0) {
|
2019-02-26 00:21:37 +01:00
|
|
|
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());
|
2019-12-03 22:57:21 +01:00
|
|
|
|
2020-05-05 19:53:57 +02:00
|
|
|
reactionOverlay.setOnReactionSelectedListener(this);
|
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();
|
|
|
|
|
2019-05-24 19:31:48 +02: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() {
|
2019-10-30 05:12:56 +01:00
|
|
|
if (recipient != null) {
|
|
|
|
recipient.removeObservers(this);
|
|
|
|
}
|
|
|
|
|
2019-10-01 14:16:57 +02:00
|
|
|
recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
|
2014-12-25 03:32:51 +01:00
|
|
|
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
|
|
|
|
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
2017-10-16 22:11:42 +02:00
|
|
|
glideRequests = GlideApp.with(this);
|
2012-07-19 23:22:03 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
recipient.observe(this, this::onRecipientChanged);
|
2015-02-10 12:15:50 +01:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
|
|
|
|
private void initializeLinkPreviewObserver() {
|
2019-07-15 17:12:26 +02:00
|
|
|
linkPreviewViewModel = ViewModelProviders.of(this, new LinkPreviewViewModel.Factory(new LinkPreviewRepository())).get(LinkPreviewViewModel.class);
|
2019-01-15 09:41:05 +01:00
|
|
|
|
|
|
|
linkPreviewViewModel.getLinkPreviewState().observe(this, previewState -> {
|
|
|
|
if (previewState == null) return;
|
|
|
|
|
|
|
|
if (previewState.isLoading()) {
|
|
|
|
inputPanel.setLinkPreviewLoading();
|
2020-08-13 19:50:38 +02:00
|
|
|
} else if (previewState.hasLinks() && !previewState.getLinkPreview().isPresent()) {
|
2020-08-18 21:28:37 +02:00
|
|
|
inputPanel.setLinkPreviewNoPreview(previewState.getError());
|
2019-01-15 09:41:05 +01:00
|
|
|
} else {
|
|
|
|
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());
|
2019-08-07 20:22:51 +02:00
|
|
|
fragment.jumpToMessage(messageResult.messageRecipient.getId(), messageResult.receivedTimestampMs, searchViewModel::onMissingResult);
|
2019-02-01 18:06:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
searchNav.setData(result.getPosition(), result.getResults().size());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
private void initializeStickerObserver() {
|
|
|
|
StickerSearchRepository repository = new StickerSearchRepository(this);
|
|
|
|
|
|
|
|
stickerViewModel = ViewModelProviders.of(this, new ConversationStickerViewModel.Factory(getApplication(), repository))
|
|
|
|
.get(ConversationStickerViewModel.class);
|
|
|
|
|
|
|
|
stickerViewModel.getStickerResults().observe(this, stickers -> {
|
|
|
|
if (stickers == null) return;
|
|
|
|
|
|
|
|
inputPanel.setStickerSuggestions(stickers);
|
|
|
|
});
|
|
|
|
|
|
|
|
stickerViewModel.getStickersAvailability().observe(this, stickersAvailable -> {
|
|
|
|
if (stickersAvailable == null) return;
|
|
|
|
|
|
|
|
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
|
|
|
|
MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this);
|
|
|
|
boolean stickerIntro = !TextSecurePreferences.hasSeenStickerIntroTooltip(this);
|
|
|
|
|
|
|
|
if (stickersAvailable) {
|
|
|
|
inputPanel.showMediaKeyboardToggle(true);
|
|
|
|
inputPanel.setMediaKeyboardToggleMode(isSystemEmojiPreferred || keyboardMode == MediaKeyboardMode.STICKER);
|
|
|
|
if (stickerIntro) showStickerIntroductionTooltip();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emojiDrawerStub.resolved()) {
|
|
|
|
initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-30 04:13:44 +01:00
|
|
|
private void initializeViewModel() {
|
|
|
|
this.viewModel = ViewModelProviders.of(this, new ConversationViewModel.Factory()).get(ConversationViewModel.class);
|
|
|
|
}
|
|
|
|
|
2020-06-07 01:26:21 +02:00
|
|
|
private void initializeGroupViewModel() {
|
|
|
|
groupViewModel = ViewModelProviders.of(this, new ConversationGroupViewModel.Factory()).get(ConversationGroupViewModel.class);
|
|
|
|
recipient.observe(this, groupViewModel::onRecipientChange);
|
|
|
|
groupViewModel.getGroupActiveState().observe(this, unused -> invalidateOptionsMenu());
|
|
|
|
}
|
|
|
|
|
2020-07-27 15:58:58 +02:00
|
|
|
private void initializeMentionsViewModel() {
|
2020-10-13 16:57:00 +02:00
|
|
|
mentionsViewModel = ViewModelProviders.of(this, new MentionsPickerViewModel.Factory()).get(MentionsPickerViewModel.class);
|
2020-07-27 15:58:58 +02:00
|
|
|
|
2020-08-19 00:13:45 +02:00
|
|
|
recipient.observe(this, r -> {
|
|
|
|
if (r.isPushV2Group() && !mentionsSuggestions.resolved()) {
|
|
|
|
mentionsSuggestions.get();
|
|
|
|
}
|
|
|
|
mentionsViewModel.onRecipientChange(r);
|
|
|
|
});
|
2020-08-05 22:45:52 +02:00
|
|
|
|
2020-07-27 15:58:58 +02:00
|
|
|
composeText.setMentionQueryChangedListener(query -> {
|
2020-09-25 18:13:18 +02:00
|
|
|
if (getRecipient().isPushV2Group() && getRecipient().isActiveGroup()) {
|
2020-07-27 15:58:58 +02:00
|
|
|
if (!mentionsSuggestions.resolved()) {
|
|
|
|
mentionsSuggestions.get();
|
|
|
|
}
|
|
|
|
mentionsViewModel.onQueryChange(query);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
composeText.setMentionValidator(annotations -> {
|
2020-09-25 18:13:18 +02:00
|
|
|
if (!getRecipient().isPushV2Group() || !getRecipient().isActiveGroup()) {
|
2020-08-05 22:45:52 +02:00
|
|
|
return annotations;
|
|
|
|
}
|
|
|
|
|
|
|
|
Set<String> validRecipientIds = Stream.of(getRecipient().getParticipants())
|
|
|
|
.map(r -> MentionAnnotation.idToMentionAnnotationValue(r.getId()))
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
|
|
return Stream.of(annotations)
|
|
|
|
.filterNot(a -> validRecipientIds.contains(a.getValue()))
|
|
|
|
.toList();
|
|
|
|
});
|
|
|
|
|
2020-07-27 15:58:58 +02:00
|
|
|
mentionsViewModel.getSelectedRecipient().observe(this, recipient -> {
|
2020-08-27 15:49:45 +02:00
|
|
|
composeText.replaceTextWithMention(recipient.getDisplayName(this), recipient.getId());
|
2020-07-27 15:58:58 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
private void showStickerIntroductionTooltip() {
|
|
|
|
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
|
|
|
|
inputPanel.setMediaKeyboardToggleMode(true);
|
|
|
|
|
|
|
|
TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView())
|
2020-03-04 14:14:21 +01:00
|
|
|
.setBackgroundTint(getResources().getColor(R.color.core_ultramarine))
|
2019-04-17 16:21:30 +02:00
|
|
|
.setTextColor(getResources().getColor(R.color.core_white))
|
|
|
|
.setText(R.string.ConversationActivity_new_say_it_with_stickers)
|
|
|
|
.setOnDismissListener(() -> {
|
|
|
|
TextSecurePreferences.setHasSeenStickerIntroTooltip(this, true);
|
|
|
|
EventBus.getDefault().removeStickyEvent(StickerPackInstallEvent.class);
|
|
|
|
})
|
|
|
|
.show(TooltipPopup.POSITION_ABOVE);
|
|
|
|
}
|
|
|
|
|
2020-05-05 19:53:57 +02:00
|
|
|
@Override
|
|
|
|
public void onReactionSelected(MessageRecord messageRecord, String emoji) {
|
2019-12-03 22:57:21 +01:00
|
|
|
final Context context = getApplicationContext();
|
|
|
|
|
2020-05-05 19:53:57 +02:00
|
|
|
reactionOverlay.hide();
|
|
|
|
|
2019-12-03 22:57:21 +01:00
|
|
|
SignalExecutors.BOUNDED.execute(() -> {
|
|
|
|
ReactionRecord oldRecord = Stream.of(messageRecord.getReactions())
|
|
|
|
.filter(record -> record.getAuthor().equals(Recipient.self().getId()))
|
|
|
|
.findFirst()
|
|
|
|
.orElse(null);
|
|
|
|
|
|
|
|
if (oldRecord != null && oldRecord.getEmoji().equals(emoji)) {
|
|
|
|
MessageSender.sendReactionRemoval(context, messageRecord.getId(), messageRecord.isMms(), oldRecord);
|
|
|
|
} else {
|
|
|
|
MessageSender.sendNewReaction(context, messageRecord.getId(), messageRecord.isMms(), emoji);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-05-05 19:53:57 +02:00
|
|
|
@Override
|
|
|
|
public void onCustomReactionSelected(@NonNull MessageRecord messageRecord, boolean hasAddedCustomEmoji) {
|
|
|
|
ReactionRecord oldRecord = Stream.of(messageRecord.getReactions())
|
|
|
|
.filter(record -> record.getAuthor().equals(Recipient.self().getId()))
|
|
|
|
.findFirst()
|
|
|
|
.orElse(null);
|
|
|
|
|
|
|
|
if (oldRecord != null && hasAddedCustomEmoji) {
|
|
|
|
final Context context = getApplicationContext();
|
|
|
|
|
|
|
|
reactionOverlay.hide();
|
|
|
|
|
|
|
|
SignalExecutors.BOUNDED.execute(() -> MessageSender.sendReactionRemoval(context,
|
|
|
|
messageRecord.getId(),
|
|
|
|
messageRecord.isMms(),
|
|
|
|
oldRecord));
|
|
|
|
} else {
|
|
|
|
reactionOverlay.hideAllButMask();
|
|
|
|
|
2020-08-21 21:06:47 +02:00
|
|
|
ReactWithAnyEmojiBottomSheetDialogFragment.createForMessageRecord(messageRecord, reactWithAnyEmojiStartPage)
|
2020-05-05 19:53:57 +02:00
|
|
|
.show(getSupportFragmentManager(), "BOTTOM");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onReactWithAnyEmojiDialogDismissed() {
|
|
|
|
reactionOverlay.hideMask();
|
|
|
|
}
|
|
|
|
|
2020-08-21 21:06:47 +02:00
|
|
|
@Override
|
|
|
|
public void onReactWithAnyEmojiPageChanged(int page) {
|
|
|
|
reactWithAnyEmojiStartPage = page;
|
|
|
|
}
|
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
@Override
|
|
|
|
public void onSearchMoveUpPressed() {
|
|
|
|
searchViewModel.onMoveUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSearchMoveDownPressed() {
|
|
|
|
searchViewModel.onMoveDown();
|
|
|
|
}
|
2019-01-15 09:41:05 +01:00
|
|
|
|
2017-05-20 03:01:40 +02:00
|
|
|
private void initializeProfiles() {
|
2017-06-14 18:53:22 +02:00
|
|
|
if (!isSecureText) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "SMS contact, no profile fetch needed.");
|
2017-06-14 18:53:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-09 01:04:55 +02:00
|
|
|
RetrieveProfileJob.enqueueAsync(recipient.getId());
|
2017-05-20 03:01:40 +02:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
private void onRecipientChanged(@NonNull Recipient recipient) {
|
2019-10-01 21:52:28 +02:00
|
|
|
Log.i(TAG, "onModified(" + recipient.getId() + ") " + recipient.getRegistered());
|
2019-08-07 20:22:51 +02:00
|
|
|
titleView.setTitle(glideRequests, recipient);
|
|
|
|
titleView.setVerified(identityRecords.isVerified());
|
|
|
|
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
|
|
|
setActionBarColor(recipient.getColor());
|
2019-11-12 15:18:57 +01:00
|
|
|
updateReminders();
|
2019-08-07 20:22:51 +02:00
|
|
|
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
|
|
|
|
initializeSecurity(isSecureText, isDefaultSms);
|
2019-02-01 18:06:59 +01:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
}
|
2020-06-07 01:26:21 +02:00
|
|
|
|
|
|
|
if (groupViewModel != null) {
|
|
|
|
groupViewModel.onRecipientChange(recipient);
|
|
|
|
}
|
2020-10-13 16:57:00 +02:00
|
|
|
|
|
|
|
if (mentionsViewModel != null) {
|
|
|
|
mentionsViewModel.onRecipientChange(recipient);
|
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
2014-02-16 03:40:08 +01:00
|
|
|
|
2017-06-07 03:03:09 +02:00
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
|
|
public void onIdentityRecordUpdate(final IdentityRecord event) {
|
|
|
|
initializeIdentityRecords();
|
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
|
|
|
public void onStickerPackInstalled(final StickerPackInstallEvent event) {
|
|
|
|
if (!TextSecurePreferences.hasSeenStickerIntroTooltip(this)) return;
|
|
|
|
|
|
|
|
EventBus.getDefault().removeStickyEvent(event);
|
2019-12-19 23:59:42 +01:00
|
|
|
|
|
|
|
if (!inputPanel.isStickerMode()) {
|
|
|
|
TooltipPopup.forTarget(inputPanel.getMediaKeyboardToggleAnchorView())
|
|
|
|
.setText(R.string.ConversationActivity_sticker_pack_installed)
|
|
|
|
.setIconGlideModel(event.getIconGlideModel())
|
|
|
|
.show(TooltipPopup.POSITION_ABOVE);
|
|
|
|
}
|
2019-04-17 16:21:30 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private void initializeReceivers() {
|
|
|
|
securityUpdateReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
2017-03-14 21:24:24 +01:00
|
|
|
initializeSecurity(isSecureText, isDefaultSms);
|
2015-09-24 00:47:48 +02:00
|
|
|
calculateCharactersRemaining();
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
registerReceiver(securityUpdateReceiver,
|
2014-11-04 00:16:04 +01:00
|
|
|
new IntentFilter(SecurityEvent.SECURITY_UPDATE_EVENT),
|
2012-07-19 23:22:03 +02:00
|
|
|
KeyCachingService.KEY_PERMISSION, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////// Helper Methods
|
|
|
|
|
2018-07-25 17:30:48 +02:00
|
|
|
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
|
2020-07-06 23:13:08 +02:00
|
|
|
return setMedia(uri, mediaType, 0, 0, false);
|
2018-03-20 19:27:11 +01:00
|
|
|
}
|
|
|
|
|
2020-07-06 23:13:08 +02:00
|
|
|
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height, boolean borderless) {
|
2018-07-25 17:30:48 +02:00
|
|
|
if (uri == null) {
|
|
|
|
return new SettableFuture<>(false);
|
|
|
|
}
|
2018-05-17 08:40:14 +02:00
|
|
|
|
2018-06-29 20:08:13 +02:00
|
|
|
if (MediaType.VCARD.equals(mediaType) && isSecureText) {
|
|
|
|
openContactShareEditor(uri);
|
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)) {
|
2020-07-08 17:54:47 +02:00
|
|
|
String mimeType = MediaUtil.getMimeType(this, uri);
|
|
|
|
if (mimeType == null) {
|
|
|
|
mimeType = mediaType.toFallbackMimeType();
|
|
|
|
}
|
|
|
|
|
|
|
|
Media media = new Media(uri, mimeType, 0, width, height, 0, 0, borderless, Optional.absent(), Optional.absent(), Optional.absent());
|
2019-08-07 20:22:51 +02:00
|
|
|
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
|
2018-11-20 18:59:23 +01:00
|
|
|
return new SettableFuture<>(false);
|
2018-06-29 20:08:13 +02:00
|
|
|
} else {
|
2018-07-25 17:30:48 +02:00
|
|
|
return attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints(), width, height);
|
2018-06-29 20:08:13 +02:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2018-04-27 02:03:54 +02:00
|
|
|
private void openContactShareEditor(Uri contactUri) {
|
2018-05-17 08:40:14 +02:00
|
|
|
Intent intent = ContactShareEditActivity.getIntent(this, Collections.singletonList(contactUri));
|
2018-04-27 02:03:54 +02:00
|
|
|
startActivityForResult(intent, GET_CONTACT_DETAILS);
|
|
|
|
}
|
|
|
|
|
2014-06-04 01:24:44 +02:00
|
|
|
private void addAttachmentContactInfo(Uri contactUri) {
|
2013-10-17 02:28:36 +02:00
|
|
|
ContactAccessor contactDataList = ContactAccessor.getInstance();
|
|
|
|
ContactData contactData = contactDataList.getContactData(this, contactUri);
|
|
|
|
|
|
|
|
if (contactData.numbers.size() == 1) composeText.append(contactData.numbers.get(0).number);
|
|
|
|
else if (contactData.numbers.size() > 1) selectContactInfo(contactData);
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:03:54 +02:00
|
|
|
private void sendSharedContact(List<Contact> contacts) {
|
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
2019-08-07 20:22:51 +02:00
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
2018-04-27 02:03:54 +02:00
|
|
|
boolean initiating = threadId == -1;
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
sendMediaMessage(isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, false);
|
2018-04-27 02:03:54 +02:00
|
|
|
}
|
|
|
|
|
2013-10-17 02:28:36 +02:00
|
|
|
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
|
|
|
|
2020-10-05 18:59:00 +02:00
|
|
|
if (recipient.get().isGroup() && !recipient.get().isActiveGroup()) {
|
|
|
|
return drafts;
|
|
|
|
}
|
|
|
|
|
2013-02-04 09:13:07 +01:00
|
|
|
if (!Util.isEmpty(composeText)) {
|
2020-08-05 22:45:52 +02:00
|
|
|
drafts.add(new Draft(Draft.TEXT, composeText.getTextTrimmed().toString()));
|
|
|
|
List<Mention> draftMentions = composeText.getMentions();
|
|
|
|
if (!draftMentions.isEmpty()) {
|
|
|
|
drafts.add(new Draft(Draft.MENTION, Base64.encodeBytes(MentionUtil.mentionsToBodyRangeList(draftMentions).toByteArray())));
|
|
|
|
}
|
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.hasLocation()) drafts.add(new Draft(Draft.LOCATION, ((LocationSlide)slide).getPlace().serialize()));
|
|
|
|
else if (slide.hasImage() && slide.getUri() != null) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
|
2013-02-04 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) {
|
2017-08-01 17:56:00 +02:00
|
|
|
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(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
|
|
|
}
|
|
|
|
|
2015-06-30 18:16:05 +02:00
|
|
|
private void setActionBarColor(MaterialColor color) {
|
2017-12-05 20:52:03 +01:00
|
|
|
ActionBar supportActionBar = getSupportActionBar();
|
|
|
|
if (supportActionBar == null) throw new AssertionError();
|
|
|
|
supportActionBar.setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
|
2015-10-19 20:23:12 +02:00
|
|
|
setStatusBarColor(color.toStatusBarColor(this));
|
2015-06-24 00:10:50 +02:00
|
|
|
}
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
2020-06-16 16:37:27 +02:00
|
|
|
if (!isSecureText && isPushGroupConversation()) {
|
2017-12-26 00:57:33 +01:00
|
|
|
unblockButton.setVisibility(View.GONE);
|
2020-04-22 17:12:08 +02:00
|
|
|
inputPanel.setVisibility(View.GONE);
|
2017-12-26 00:57:33 +01:00
|
|
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
|
|
|
registerButton.setVisibility(View.VISIBLE);
|
2017-01-18 20:01:13 +01:00
|
|
|
} else if (!isSecureText && !isDefaultSms) {
|
2016-11-21 00:56:47 +01:00
|
|
|
unblockButton.setVisibility(View.GONE);
|
2020-04-22 17:12:08 +02:00
|
|
|
inputPanel.setVisibility(View.GONE);
|
2016-11-21 00:56:47 +01:00
|
|
|
makeDefaultSmsButton.setVisibility(View.VISIBLE);
|
2017-12-26 00:57:33 +01:00
|
|
|
registerButton.setVisibility(View.GONE);
|
2015-06-09 16:37:20 +02:00
|
|
|
} else {
|
2020-07-17 20:32:53 +02:00
|
|
|
boolean inactivePushGroup = isPushGroupConversation() && !recipient.isActiveGroup();
|
|
|
|
inputPanel.setVisibility(inactivePushGroup ? View.GONE : 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);
|
2017-12-26 00:57:33 +01:00
|
|
|
registerButton.setVisibility(View.GONE);
|
2015-06-09 16:37:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
private void calculateCharactersRemaining() {
|
2020-08-05 22:45:52 +02:00
|
|
|
String messageBody = composeText.getTextTrimmed().toString();
|
2015-03-07 18:02:10 +01:00
|
|
|
TransportOption transportOption = sendButton.getSelectedTransport();
|
2016-04-17 18:09:31 +02:00
|
|
|
CharacterState characterState = transportOption.calculateCharacters(messageBody);
|
2015-03-07 18:02:10 +01:00
|
|
|
|
2015-03-11 22:23:45 +01:00
|
|
|
if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) {
|
2017-12-05 20:52:03 +01:00
|
|
|
charactersLeft.setText(String.format(dynamicLanguage.getCurrentLocale(),
|
|
|
|
"%d/%d (%d)",
|
|
|
|
characterState.charactersRemaining,
|
2019-02-27 04:29:52 +01:00
|
|
|
characterState.maxTotalMessageSize,
|
2017-12-05 20:52:03 +01:00
|
|
|
characterState.messagesSpent));
|
2015-03-11 22:23:45 +01:00
|
|
|
charactersLeft.setVisibility(View.VISIBLE);
|
|
|
|
} else {
|
|
|
|
charactersLeft.setVisibility(View.GONE);
|
2014-01-08 23:29:05 +01:00
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard, boolean stickersAvailable) {
|
|
|
|
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
|
|
|
|
|
|
|
|
if (stickersAvailable) {
|
|
|
|
if (isSystemEmojiPreferred) {
|
|
|
|
mediaKeyboard.setProviders(0, new StickerKeyboardProvider(this, this));
|
|
|
|
} else {
|
|
|
|
MediaKeyboardMode keyboardMode = TextSecurePreferences.getMediaKeyboardMode(this);
|
|
|
|
int index = keyboardMode == MediaKeyboardMode.STICKER ? 1 : 0;
|
|
|
|
|
|
|
|
mediaKeyboard.setProviders(index,
|
|
|
|
new EmojiKeyboardProvider(this, inputPanel),
|
|
|
|
new StickerKeyboardProvider(this, this));
|
|
|
|
}
|
|
|
|
} else if (!isSystemEmojiPreferred) {
|
|
|
|
mediaKeyboard.setProviders(0, new EmojiKeyboardProvider(this, inputPanel));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
private boolean isInMessageRequest() {
|
|
|
|
return messageRequestBottomView.getVisibility() == View.VISIBLE;
|
|
|
|
}
|
2019-04-17 16:21:30 +02:00
|
|
|
|
2012-08-02 02:39:36 +02:00
|
|
|
private boolean isSingleConversation() {
|
2019-08-07 20:22:51 +02:00
|
|
|
return getRecipient() != null && !getRecipient().isGroup();
|
2012-10-21 23:34:09 +02:00
|
|
|
}
|
|
|
|
|
2014-02-22 19:54:43 +01:00
|
|
|
private boolean isActiveGroup() {
|
|
|
|
if (!isGroupConversation()) return false;
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getId());
|
2017-08-08 01:47:38 +02:00
|
|
|
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;
|
2019-09-07 05:40:06 +02:00
|
|
|
if (recipient.get().isGroup()) return false;
|
2015-10-01 20:14:00 +02:00
|
|
|
|
2019-09-07 05:40:06 +02:00
|
|
|
return recipient.get().isLocalNumber();
|
2015-10-01 20:14:00 +02:00
|
|
|
}
|
|
|
|
|
2012-10-21 23:34:09 +02:00
|
|
|
private boolean isGroupConversation() {
|
2019-08-07 20:22:51 +02:00
|
|
|
return getRecipient() != null && getRecipient().isGroup();
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
|
2014-02-18 05:25:40 +01:00
|
|
|
private boolean isPushGroupConversation() {
|
2019-08-07 20:22:51 +02:00
|
|
|
return getRecipient() != null && getRecipient().isPushGroup();
|
2014-02-18 05:25:40 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 20:09:47 +02:00
|
|
|
private boolean isPushGroupV1Conversation() {
|
|
|
|
return getRecipient() != null && getRecipient().isPushV1Group();
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:03:54 +02:00
|
|
|
private boolean isSmsForced() {
|
|
|
|
return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
|
|
|
}
|
|
|
|
|
2017-08-01 17:56:00 +02:00
|
|
|
protected Recipient getRecipient() {
|
2019-08-07 20:22:51 +02:00
|
|
|
return this.recipient.get();
|
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 {
|
2020-08-05 22:45:52 +02:00
|
|
|
String rawText = composeText.getTextTrimmed().toString();
|
2012-07-19 23:22:03 +02:00
|
|
|
|
|
|
|
if (rawText.length() < 1 && !attachmentManager.isAttachmentPresent())
|
2012-09-20 04:56:04 +02:00
|
|
|
throw new InvalidMessageException(getString(R.string.ConversationActivity_message_is_empty_exclamation));
|
2012-07-19 23:22:03 +02:00
|
|
|
|
|
|
|
return rawText;
|
|
|
|
}
|
|
|
|
|
2015-09-05 02:33:22 +02:00
|
|
|
private MediaConstraints getCurrentMediaConstraints() {
|
|
|
|
return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE
|
2017-05-09 00:32:59 +02:00
|
|
|
? MediaConstraints.getPushMediaConstraints()
|
|
|
|
: MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedTransport().getSimSubscriptionId().or(-1));
|
2015-09-05 02:33:22 +02: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) {
|
2019-08-07 20:22:51 +02:00
|
|
|
fragment.reload(recipient.get(), threadId);
|
2020-05-27 21:04:45 +02:00
|
|
|
ApplicationDependencies.getMessageNotifier().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();
|
2020-08-19 00:34:18 +02:00
|
|
|
linkPreviewViewModel.onSend();
|
2012-07-19 23:22:03 +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();
|
|
|
|
TransportOption transport = sendButton.getSelectedTransport();
|
2019-04-28 20:34:56 +02:00
|
|
|
boolean forceSms = (recipient.isForceSmsSelection() || sendButton.isManualSelection()) && transport.isSms();
|
2019-03-06 17:12:00 +01:00
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
|
|
|
long expiresIn = recipient.getExpireMessages() * 1000L;
|
|
|
|
boolean initiating = threadId == -1;
|
|
|
|
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
|
|
|
|
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
2019-09-07 05:40:06 +02:00
|
|
|
recipient.isGroup() ||
|
|
|
|
recipient.getEmail().isPresent() ||
|
2019-03-06 17:12:00 +01:00
|
|
|
inputPanel.getQuote().isPresent() ||
|
2020-08-05 22:45:52 +02:00
|
|
|
composeText.hasMentions() ||
|
2019-03-06 17:12:00 +01:00
|
|
|
linkPreviewViewModel.hasLinkPreview() ||
|
|
|
|
needsSplit;
|
2015-03-11 22:23:45 +01:00
|
|
|
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection());
|
|
|
|
Log.i(TAG, "forceSms: " + forceSms);
|
2012-08-02 02:39:36 +02:00
|
|
|
|
2019-09-07 05:40:06 +02:00
|
|
|
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !isMmsEnabled) {
|
2013-09-16 09:55:01 +02:00
|
|
|
handleManualMmsRequired();
|
2020-08-12 17:18:14 +02:00
|
|
|
} else if (!forceSms && (identityRecords.isUnverified(true) || identityRecords.isUntrusted(true))) {
|
2020-06-26 17:10:54 +02:00
|
|
|
handleRecentSafetyNumberChange();
|
2019-01-15 09:41:05 +01:00
|
|
|
} else if (isMediaMessage) {
|
2019-08-01 01:33:56 +02:00
|
|
|
sendMediaMessage(forceSms, expiresIn, false, subscriptionId, initiating);
|
2012-07-19 23:22:03 +02:00
|
|
|
} else {
|
2017-08-17 06:49:41 +02:00
|
|
|
sendTextMessage(forceSms, expiresIn, subscriptionId, initiating);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
} catch (RecipientFormattingException ex) {
|
2012-09-08 05:03:23 +02:00
|
|
|
Toast.makeText(ConversationActivity.this,
|
2012-09-20 04:56:04 +02:00
|
|
|
R.string.ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation,
|
2012-09-08 05:03:23 +02:00
|
|
|
Toast.LENGTH_LONG).show();
|
2014-02-18 05:25:40 +01:00
|
|
|
Log.w(TAG, ex);
|
2012-07-19 23:22:03 +02:00
|
|
|
} catch (InvalidMessageException ex) {
|
2012-09-20 04:56:04 +02:00
|
|
|
Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_message_is_empty_exclamation,
|
2012-09-08 05:03:23 +02:00
|
|
|
Toast.LENGTH_SHORT).show();
|
2014-02-18 05:25:40 +01:00
|
|
|
Log.w(TAG, ex);
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
private void sendMediaMessage(@NonNull MediaSendActivityResult result) {
|
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
|
|
|
QuoteModel quote = result.isViewOnce() ? null : inputPanel.getQuote().orNull();
|
2020-08-05 22:45:52 +02:00
|
|
|
List<Mention> mentions = new ArrayList<>(result.getMentions());
|
2020-01-08 21:56:51 +01:00
|
|
|
boolean initiating = threadId == -1;
|
2020-08-05 22:45:52 +02:00
|
|
|
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient.get(), new SlideDeck(), result.getBody(), System.currentTimeMillis(), -1, expiresIn, result.isViewOnce(), distributionType, quote, Collections.emptyList(), Collections.emptyList(), mentions);
|
|
|
|
OutgoingMediaMessage secureMessage = new OutgoingSecureMediaMessage(message);
|
2020-01-08 21:56:51 +01:00
|
|
|
|
|
|
|
ApplicationContext.getInstance(this).getTypingStatusSender().onTypingStopped(threadId);
|
|
|
|
|
|
|
|
inputPanel.clearQuote();
|
|
|
|
attachmentManager.clear(glideRequests, false);
|
|
|
|
silentlySetComposeText("");
|
|
|
|
|
|
|
|
long id = fragment.stageOutgoingMessage(message);
|
|
|
|
|
|
|
|
SimpleTask.run(() -> {
|
|
|
|
long resultId = MessageSender.sendPushWithPreUploadedMedia(this, secureMessage, result.getPreUploadResults(), threadId, () -> fragment.releaseOutgoingMessage(id));
|
|
|
|
|
|
|
|
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
|
|
|
|
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
|
|
|
|
|
|
|
|
return resultId;
|
|
|
|
}, this::sendComplete);
|
|
|
|
}
|
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
private void sendMediaMessage(final boolean forceSms, final long expiresIn, final boolean viewOnce, final int subscriptionId, final 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...");
|
2020-08-05 22:45:52 +02:00
|
|
|
sendMediaMessage(forceSms,
|
|
|
|
getMessage(),
|
|
|
|
attachmentManager.buildSlideDeck(),
|
|
|
|
inputPanel.getQuote().orNull(),
|
|
|
|
Collections.emptyList(),
|
|
|
|
linkPreviewViewModel.getActiveLinkPreviews(),
|
|
|
|
composeText.getMentions(),
|
|
|
|
expiresIn,
|
|
|
|
viewOnce,
|
|
|
|
subscriptionId,
|
|
|
|
initiating,
|
|
|
|
true);
|
2015-11-18 23:52:26 +01:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:41:05 +01:00
|
|
|
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms,
|
2020-07-08 17:54:47 +02:00
|
|
|
@NonNull String body,
|
2019-01-15 09:41:05 +01:00
|
|
|
SlideDeck slideDeck,
|
2019-04-17 16:21:30 +02:00
|
|
|
QuoteModel quote,
|
2019-01-15 09:41:05 +01:00
|
|
|
List<Contact> contacts,
|
|
|
|
List<LinkPreview> previews,
|
2020-08-05 22:45:52 +02:00
|
|
|
List<Mention> mentions,
|
2019-01-15 09:41:05 +01:00
|
|
|
final long expiresIn,
|
2019-08-01 01:33:56 +02:00
|
|
|
final boolean viewOnce,
|
2019-01-15 09:41:05 +01:00
|
|
|
final int subscriptionId,
|
2019-04-17 16:21:30 +02:00
|
|
|
final boolean initiating,
|
|
|
|
final boolean clearComposeBox)
|
2019-01-15 09:41:05 +01:00
|
|
|
{
|
2018-12-13 03:15:09 +01:00
|
|
|
if (!isDefaultSms && (!isSecureText || forceSms)) {
|
|
|
|
showDefaultSmsPrompt();
|
2018-11-20 18:59:23 +01:00
|
|
|
return new SettableFuture<>(null);
|
2018-12-13 03:15:09 +01:00
|
|
|
}
|
|
|
|
|
2019-02-27 04:29:52 +01:00
|
|
|
if (isSecureText && !forceSms) {
|
2020-01-08 21:56:51 +01:00
|
|
|
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(this, body, sendButton.getSelectedTransport().calculateCharacters(body).maxPrimaryMessageSize);
|
|
|
|
body = splitMessage.getBody();
|
2019-02-27 04:29:52 +01:00
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
if (splitMessage.getTextSlide().isPresent()) {
|
|
|
|
slideDeck.addSlide(splitMessage.getTextSlide().get());
|
2019-02-27 04:29:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient.get(), slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews, mentions);
|
2014-06-12 00:31:59 +02:00
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
final SettableFuture<Void> future = new SettableFuture<>();
|
|
|
|
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
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
if (isSecureText && !forceSms) {
|
|
|
|
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate);
|
2018-10-29 23:14:31 +01:00
|
|
|
ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
|
2017-12-05 20:35:15 +01:00
|
|
|
} else {
|
|
|
|
outgoingMessage = outgoingMessageCandidate;
|
|
|
|
}
|
2014-11-08 20:35:58 +01:00
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
Permissions.with(this)
|
2017-12-20 20:21:00 +01:00
|
|
|
.request(Manifest.permission.SEND_SMS, Manifest.permission.READ_SMS)
|
2017-12-13 19:29:19 +01:00
|
|
|
.ifNecessary(!isSecureText || forceSms)
|
2017-12-05 20:35:15 +01:00
|
|
|
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms))
|
|
|
|
.onAllGranted(() -> {
|
2019-04-17 16:21:30 +02:00
|
|
|
if (clearComposeBox) {
|
|
|
|
inputPanel.clearQuote();
|
|
|
|
attachmentManager.clear(glideRequests, false);
|
|
|
|
silentlySetComposeText("");
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
|
|
|
|
2020-01-08 21:56:51 +01:00
|
|
|
SimpleTask.run(() -> {
|
|
|
|
return MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
|
|
|
|
}, result -> {
|
|
|
|
sendComplete(result);
|
|
|
|
future.set(null);
|
|
|
|
});
|
2017-12-05 20:35:15 +01:00
|
|
|
})
|
|
|
|
.onAnyDenied(() -> future.set(null))
|
|
|
|
.execute();
|
2015-11-18 23:52:26 +01:00
|
|
|
|
|
|
|
return future;
|
2014-06-12 00:31:59 +02:00
|
|
|
}
|
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiating)
|
2014-06-12 00:31:59 +02:00
|
|
|
throws InvalidMessageException
|
|
|
|
{
|
2018-12-13 03:15:09 +01:00
|
|
|
if (!isDefaultSms && (!isSecureText || forceSms)) {
|
|
|
|
showDefaultSmsPrompt();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
final Context context = getApplicationContext();
|
|
|
|
final String messageBody = getMessage();
|
|
|
|
|
2014-06-12 00:31:59 +02:00
|
|
|
OutgoingTextMessage message;
|
|
|
|
|
2015-09-29 23:26:37 +02:00
|
|
|
if (isSecureText && !forceSms) {
|
2019-08-07 20:22:51 +02:00
|
|
|
message = new OutgoingEncryptedMessage(recipient.get(), messageBody, expiresIn);
|
2018-10-29 23:14:31 +01:00
|
|
|
ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId);
|
2014-06-12 00:31:59 +02:00
|
|
|
} else {
|
2019-08-07 20:22:51 +02:00
|
|
|
message = new OutgoingTextMessage(recipient.get(), messageBody, expiresIn, subscriptionId);
|
2014-06-12 00:31:59 +02:00
|
|
|
}
|
|
|
|
|
2017-12-05 20:35:15 +01:00
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.SEND_SMS)
|
|
|
|
.ifNecessary(forceSms || !isSecureText)
|
|
|
|
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms))
|
|
|
|
.onAllGranted(() -> {
|
2018-10-29 23:14:31 +01:00
|
|
|
silentlySetComposeText("");
|
2017-12-05 20:35:15 +01:00
|
|
|
final long id = fragment.stageOutgoingMessage(message);
|
|
|
|
|
|
|
|
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
|
|
|
@Override
|
|
|
|
protected Long doInBackground(OutgoingTextMessage... messages) {
|
2018-01-25 04:17:44 +01:00
|
|
|
return MessageSender.send(context, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
|
2017-12-05 20:35:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Long result) {
|
|
|
|
sendComplete(result);
|
|
|
|
}
|
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
|
|
|
|
|
|
|
|
})
|
|
|
|
.execute();
|
2014-06-12 00:31:59 +02:00
|
|
|
}
|
|
|
|
|
2018-12-13 03:15:09 +01:00
|
|
|
private void showDefaultSmsPrompt() {
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
.setMessage(R.string.ConversationActivity_signal_cannot_sent_sms_mms_messages_because_it_is_not_your_default_sms_app)
|
|
|
|
.setNegativeButton(R.string.ConversationActivity_no, (dialog, which) -> dialog.dismiss())
|
|
|
|
.setPositiveButton(R.string.ConversationActivity_yes, (dialog, which) -> handleMakeDefaultSms())
|
|
|
|
.show();
|
|
|
|
}
|
|
|
|
|
2015-05-18 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()) {
|
2015-05-18 19:26:32 +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
|
|
|
|
2020-08-13 19:50:38 +02:00
|
|
|
if (!attachmentManager.isAttachmentPresent() && !linkPreviewViewModel.hasLinkPreviewUi()) {
|
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-08-27 16:52:47 +02:00
|
|
|
if (SignalStore.settings().isLinkPreviewsEnabled() && isSecureText && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent()) {
|
2019-01-15 09:41:05 +01:00
|
|
|
linkPreviewViewModel.onEnabled();
|
2020-08-05 22:45:52 +02:00
|
|
|
linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed().toString(), composeText.getSelectionStart(), composeText.getSelectionEnd());
|
2019-01-15 09:41:05 +01:00
|
|
|
} else {
|
|
|
|
linkPreviewViewModel.onUserCancel();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-12 21:22:38 +02:00
|
|
|
private void recordTransportPreference(TransportOption transportOption) {
|
2016-02-06 01:10:33 +01:00
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
2019-04-12 21:22:38 +02:00
|
|
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(ConversationActivity.this);
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
recipientDatabase.setDefaultSubscriptionId(recipient.getId(), transportOption.getSimSubscriptionId().or(-1));
|
2019-04-12 21:22:38 +02:00
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
if (!recipient.resolve().isPushGroup()) {
|
|
|
|
recipientDatabase.setForceSmsSelection(recipient.getId(), recipient.get().getRegistered() == RegisteredState.REGISTERED && transportOption.isSms());
|
2019-05-10 17:13:59 +02:00
|
|
|
}
|
2019-04-12 21:22:38 +02:00
|
|
|
|
2016-02-06 01:10:33 +01:00
|
|
|
return null;
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2016-02-06 01:10:33 +01:00
|
|
|
}
|
|
|
|
|
2017-11-25 07:00:30 +01:00
|
|
|
@Override
|
|
|
|
public void onRecorderPermissionRequired() {
|
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.RECORD_AUDIO)
|
|
|
|
.ifNecessary()
|
2019-10-07 20:43:36 +02:00
|
|
|
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_mic_solid_24)
|
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();
|
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() {
|
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) {
|
2017-12-05 20:35:15 +01:00
|
|
|
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
2020-02-19 23:08:34 +01:00
|
|
|
boolean initiating = threadId == -1;
|
2017-12-05 20:35:15 +01:00
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
2019-08-07 20:22:51 +02:00
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
2020-01-08 21:56:51 +01:00
|
|
|
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first(), result.second(), MediaUtil.AUDIO_AAC, true);
|
2017-12-05 20:35:15 +01:00
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
|
|
|
slideDeck.addSlide(audioSlide);
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
ListenableFuture<Void> sendResult = sendMediaMessage(forceSms,
|
|
|
|
"",
|
|
|
|
slideDeck,
|
|
|
|
inputPanel.getQuote().orNull(),
|
|
|
|
Collections.emptyList(),
|
|
|
|
Collections.emptyList(),
|
|
|
|
composeText.getMentions(),
|
|
|
|
expiresIn,
|
|
|
|
false,
|
|
|
|
subscriptionId,
|
|
|
|
initiating,
|
|
|
|
true);
|
|
|
|
|
|
|
|
sendResult.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) {
|
2020-01-08 21:56:51 +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() {
|
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) {
|
2020-01-08 21:56:51 +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()) {
|
2019-04-17 16:21:30 +02:00
|
|
|
Boolean stickersAvailable = stickerViewModel.getStickersAvailability().getValue();
|
|
|
|
|
|
|
|
initializeMediaKeyboardProviders(emojiDrawerStub.get(), stickersAvailable == null ? false : stickersAvailable);
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
@Override
|
|
|
|
public void onStickerSuggestionSelected(@NonNull StickerRecord sticker) {
|
|
|
|
sendSticker(sticker, true);
|
|
|
|
}
|
|
|
|
|
2016-12-27 00:14:23 +01:00
|
|
|
@Override
|
|
|
|
public void onMediaSelected(@NonNull Uri uri, String contentType) {
|
2020-07-08 17:54:47 +02:00
|
|
|
if (MediaUtil.isGif(contentType) || MediaUtil.isImageType(contentType)) {
|
|
|
|
SimpleTask.run(getLifecycle(),
|
|
|
|
() -> getKeyboardImageDetails(uri),
|
|
|
|
details -> sendKeyboardImage(uri, contentType, details));
|
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) {
|
2020-08-05 22:45:52 +02:00
|
|
|
linkPreviewViewModel.onTextChanged(this, composeText.getTextTrimmed().toString(), start, end);
|
2019-02-14 22:55:48 +01:00
|
|
|
}
|
|
|
|
|
2019-04-17 16:21:30 +02:00
|
|
|
@Override
|
|
|
|
public void onStickerSelected(@NonNull StickerRecord stickerRecord) {
|
|
|
|
sendSticker(stickerRecord, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onStickerManagementClicked() {
|
|
|
|
startActivity(StickerManagementActivity.getIntent(this));
|
|
|
|
container.hideAttachedInput(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendSticker(@NonNull StickerRecord stickerRecord, boolean clearCompose) {
|
2020-09-03 16:02:33 +02:00
|
|
|
sendSticker(new StickerLocator(stickerRecord.getPackId(), stickerRecord.getPackKey(), stickerRecord.getStickerId(), stickerRecord.getEmoji()), stickerRecord.getContentType(), stickerRecord.getUri(), stickerRecord.getSize(), clearCompose);
|
2019-04-17 16:21:30 +02:00
|
|
|
|
2019-08-30 23:45:35 +02:00
|
|
|
SignalExecutors.BOUNDED.execute(() ->
|
|
|
|
DatabaseFactory.getStickerDatabase(getApplicationContext())
|
|
|
|
.updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis())
|
|
|
|
);
|
2019-04-17 16:21:30 +02:00
|
|
|
}
|
|
|
|
|
2020-09-02 18:46:58 +02:00
|
|
|
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull String contentType, @NonNull Uri uri, long size, boolean clearCompose) {
|
2019-04-17 16:21:30 +02:00
|
|
|
if (sendButton.getSelectedTransport().isSms()) {
|
2020-09-02 18:46:58 +02:00
|
|
|
Media media = new Media(uri, contentType, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, 0, false, Optional.absent(), Optional.absent(), Optional.absent());
|
2019-08-07 20:22:51 +02:00
|
|
|
Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport());
|
2019-04-17 16:21:30 +02:00
|
|
|
startActivityForResult(intent, MEDIA_SENDER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:22:51 +02:00
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
2019-04-17 16:21:30 +02:00
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
|
|
|
boolean initiating = threadId == -1;
|
|
|
|
TransportOption transport = sendButton.getSelectedTransport();
|
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
2020-09-02 18:46:58 +02:00
|
|
|
Slide stickerSlide = new StickerSlide(this, uri, size, stickerLocator, contentType);
|
2019-04-17 16:21:30 +02:00
|
|
|
|
|
|
|
slideDeck.addSlide(stickerSlide);
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
sendMediaMessage(transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose);
|
2019-04-17 16:21:30 +02:00
|
|
|
}
|
|
|
|
|
2018-10-29 23:14:31 +01:00
|
|
|
private void silentlySetComposeText(String text) {
|
|
|
|
typingTextWatcher.setEnabled(false);
|
|
|
|
composeText.setText(text);
|
|
|
|
typingTextWatcher.setEnabled(true);
|
|
|
|
}
|
2016-12-27 00:14:23 +01:00
|
|
|
|
2020-07-29 18:44:23 +02:00
|
|
|
@Override
|
|
|
|
public void onReactionsDialogDismissed() {
|
|
|
|
reactionOverlay.hideMask();
|
|
|
|
}
|
|
|
|
|
2012-07-19 23:22:03 +02:00
|
|
|
// Listeners
|
|
|
|
|
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)
|
|
|
|
.ifNecessary()
|
2019-10-07 20:43:36 +02:00
|
|
|
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_camera_solid_24)
|
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();
|
2019-08-07 20:22:51 +02:00
|
|
|
startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient.get(), sendButton.getSelectedTransport()), 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) {
|
|
|
|
calculateCharactersRemaining();
|
2015-05-18 19:26:32 +02:00
|
|
|
|
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
|
|
|
}
|
2019-04-17 16:21:30 +02:00
|
|
|
|
|
|
|
stickerViewModel.onInputTextUpdated(s.toString());
|
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-04-22 17:12:08 +02:00
|
|
|
if (enabled && threadId > 0 && isSecureText && !isSmsForced() && !recipient.get().isBlocked()) {
|
2018-10-29 23:14:31 +01:00
|
|
|
ApplicationContext.getInstance(ConversationActivity.this).getTypingStatusSender().onTypingStarted(threadId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEnabled(boolean enabled) {
|
|
|
|
this.enabled = enabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-19 17:01:07 +01:00
|
|
|
@Override
|
2020-02-19 23:08:34 +01:00
|
|
|
public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) {
|
2020-07-15 20:40:13 +02:00
|
|
|
messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept());
|
2020-02-21 19:52:27 +01:00
|
|
|
messageRequestBottomView.setDeleteOnClickListener(v -> onMessageRequestDeleteClicked(viewModel));
|
|
|
|
messageRequestBottomView.setBlockOnClickListener(v -> onMessageRequestBlockClicked(viewModel));
|
|
|
|
messageRequestBottomView.setUnblockOnClickListener(v -> onMessageRequestUnblockClicked(viewModel));
|
2019-11-19 17:01:07 +01:00
|
|
|
|
2020-10-05 19:50:35 +02:00
|
|
|
viewModel.getMessageData().observe(this, this::presentMessageRequestBottomViewTo);
|
2020-02-21 19:52:27 +01:00
|
|
|
viewModel.getMessageRequestDisplayState().observe(this, this::presentMessageRequestDisplayState);
|
2020-07-15 20:40:13 +02:00
|
|
|
viewModel.getFailures().observe(this, this::showGroupChangeErrorToast);
|
2020-02-21 19:52:27 +01:00
|
|
|
viewModel.getMessageRequestStatus().observe(this, status -> {
|
2020-02-19 23:08:34 +01:00
|
|
|
switch (status) {
|
2020-07-15 20:40:13 +02:00
|
|
|
case IDLE:
|
|
|
|
hideMessageRequestBusy();
|
|
|
|
break;
|
|
|
|
case ACCEPTING:
|
|
|
|
case BLOCKING:
|
|
|
|
case DELETING:
|
|
|
|
showMessageRequestBusy();
|
|
|
|
break;
|
2019-11-19 17:01:07 +01:00
|
|
|
case ACCEPTED:
|
2020-07-15 20:40:13 +02:00
|
|
|
hideMessageRequestBusy();
|
2020-02-19 23:08:34 +01:00
|
|
|
messageRequestBottomView.setVisibility(View.GONE);
|
2020-07-15 20:40:13 +02:00
|
|
|
break;
|
2019-11-19 17:01:07 +01:00
|
|
|
case DELETED:
|
|
|
|
case BLOCKED:
|
2020-07-15 20:40:13 +02:00
|
|
|
hideMessageRequestBusy();
|
2019-11-19 17:01:07 +01:00
|
|
|
finish();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-07-15 20:40:13 +02:00
|
|
|
private void showMessageRequestBusy() {
|
|
|
|
messageRequestBottomView.showBusy();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void hideMessageRequestBusy() {
|
|
|
|
messageRequestBottomView.hideBusy();
|
|
|
|
}
|
|
|
|
|
2020-05-12 20:09:47 +02:00
|
|
|
private void showGroupChangeErrorToast(@NonNull GroupChangeFailureReason e) {
|
|
|
|
Toast.makeText(this, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
|
2020-02-19 23:08:34 +01:00
|
|
|
@Override
|
2019-12-03 22:57:21 +01:00
|
|
|
public void handleReaction(@NonNull View maskTarget,
|
|
|
|
@NonNull MessageRecord messageRecord,
|
|
|
|
@NonNull Toolbar.OnMenuItemClickListener toolbarListener,
|
|
|
|
@NonNull ConversationReactionOverlay.OnHideListener onHideListener)
|
|
|
|
{
|
|
|
|
reactionOverlay.setOnToolbarItemClickedListener(toolbarListener);
|
|
|
|
reactionOverlay.setOnHideListener(onHideListener);
|
2020-10-05 18:59:00 +02:00
|
|
|
reactionOverlay.show(this, maskTarget, recipient.get(), messageRecord, inputAreaHeight());
|
2020-02-19 23:08:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onListVerticalTranslationChanged(float translationY) {
|
|
|
|
reactionOverlay.setListVerticalTranslation(translationY);
|
2019-12-03 22:57:21 +01:00
|
|
|
}
|
|
|
|
|
2020-06-26 17:10:54 +02:00
|
|
|
@Override
|
|
|
|
public void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord) {
|
|
|
|
if (messageRecord.hasFailedWithNetworkFailures()) {
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
.setMessage(R.string.conversation_activity__message_could_not_be_sent)
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
.setPositiveButton(R.string.conversation_activity__send, (dialog, which) -> MessageSender.resend(this, messageRecord))
|
|
|
|
.show();
|
|
|
|
} else if (messageRecord.isIdentityMismatchFailure()) {
|
2020-08-27 22:34:26 +02:00
|
|
|
SafetyNumberChangeDialog.show(this, messageRecord);
|
2020-06-26 17:10:54 +02:00
|
|
|
} else {
|
|
|
|
startActivity(MessageDetailsActivity.getIntentForMessageDetails(this, messageRecord, messageRecord.getRecipient().getId(), messageRecord.getThreadId()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 18:44:23 +02:00
|
|
|
@Override
|
|
|
|
public void handleReactionDetails(@NonNull View maskTarget) {
|
2020-07-31 16:34:46 +02:00
|
|
|
reactionOverlay.showMask(maskTarget, titleView.getMeasuredHeight(), inputAreaHeight());
|
2020-07-29 18:44:23 +02:00
|
|
|
}
|
|
|
|
|
2019-12-03 22:57:21 +01:00
|
|
|
@Override
|
|
|
|
public void onCursorChanged() {
|
|
|
|
if (!reactionOverlay.isShowing()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SimpleTask.run(() -> {
|
|
|
|
//noinspection CodeBlock2Expr
|
|
|
|
return DatabaseFactory.getMmsSmsDatabase(this)
|
|
|
|
.checkMessageExists(reactionOverlay.getMessageRecord());
|
|
|
|
}, messageExists -> {
|
|
|
|
if (!messageExists) {
|
|
|
|
reactionOverlay.hide();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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
|
2020-08-05 22:45:52 +02:00
|
|
|
public void handleReplyMessage(ConversationMessage conversationMessage) {
|
|
|
|
MessageRecord messageRecord = conversationMessage.getMessageRecord();
|
|
|
|
|
2018-02-07 23:01:37 +01:00
|
|
|
Recipient author;
|
|
|
|
|
|
|
|
if (messageRecord.isOutgoing()) {
|
2019-08-07 20:22:51 +02:00
|
|
|
author = Recipient.self();
|
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),
|
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
|
|
|
body,
|
2020-01-09 18:03:52 +01:00
|
|
|
slideDeck);
|
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),
|
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
2020-08-05 22:45:52 +02:00
|
|
|
conversationMessage.getDisplayBody(this),
|
2020-01-09 18:03:52 +01:00
|
|
|
slideDeck);
|
2018-04-27 02:03:54 +02:00
|
|
|
} else {
|
2019-06-11 08:18:45 +02:00
|
|
|
SlideDeck slideDeck = messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck();
|
|
|
|
|
2020-01-10 21:34:38 +01:00
|
|
|
if (messageRecord.isMms() && ((MmsMessageRecord) messageRecord).isViewOnce()) {
|
|
|
|
Attachment attachment = new TombstoneAttachment(MediaUtil.VIEW_ONCE, true);
|
2019-06-11 08:18:45 +02:00
|
|
|
slideDeck = new SlideDeck();
|
|
|
|
slideDeck.addSlide(MediaUtil.getSlideForAttachment(this, attachment));
|
|
|
|
}
|
|
|
|
|
2018-04-27 02:03:54 +02:00
|
|
|
inputPanel.setQuote(GlideApp.with(this),
|
|
|
|
messageRecord.getDateSent(),
|
|
|
|
author,
|
2020-08-05 22:45:52 +02:00
|
|
|
conversationMessage.getDisplayBody(this),
|
2020-01-09 18:03:52 +01:00
|
|
|
slideDeck);
|
2018-04-27 02:03:54 +02:00
|
|
|
}
|
2019-09-05 15:18:54 +02:00
|
|
|
|
|
|
|
inputPanel.clickOnComposeInput();
|
2018-02-07 23:01:37 +01:00
|
|
|
}
|
|
|
|
|
2019-02-01 18:06:59 +01:00
|
|
|
@Override
|
|
|
|
public void onMessageActionToolbarOpened() {
|
|
|
|
searchViewItem.collapseActionView();
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:35:47 +02:00
|
|
|
@Override
|
2020-02-19 23:08:34 +01:00
|
|
|
public void onForwardClicked() {
|
2019-04-09 15:35:47 +02:00
|
|
|
inputPanel.clearQuote();
|
|
|
|
}
|
|
|
|
|
2014-04-15 12:43:14 +02:00
|
|
|
@Override
|
|
|
|
public void onAttachmentChanged() {
|
2017-03-14 21:24:24 +01:00
|
|
|
handleSecurityChange(isSecureText, 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
|
|
|
|
2020-07-31 16:34:46 +02:00
|
|
|
private int inputAreaHeight() {
|
|
|
|
int height = panelParent.getMeasuredHeight();
|
|
|
|
|
|
|
|
if (attachmentKeyboardStub.resolved()) {
|
|
|
|
View keyboard = attachmentKeyboardStub.get();
|
|
|
|
if (keyboard.getVisibility() == View.VISIBLE) {
|
|
|
|
return height + keyboard.getMeasuredHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
2020-02-21 19:52:27 +01:00
|
|
|
private void onMessageRequestDeleteClicked(@NonNull MessageRequestViewModel requestModel) {
|
|
|
|
Recipient recipient = requestModel.getRecipient().getValue();
|
|
|
|
if (recipient == null) {
|
|
|
|
Log.w(TAG, "[onMessageRequestDeleteClicked] No recipient!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
|
|
|
.setNeutralButton(R.string.ConversationActivity_cancel, (d, w) -> d.dismiss());
|
|
|
|
|
|
|
|
if (recipient.isGroup() && recipient.isBlocked()) {
|
|
|
|
builder.setTitle(R.string.ConversationActivity_delete_conversation);
|
|
|
|
builder.setMessage(R.string.ConversationActivity_this_conversation_will_be_deleted_from_all_of_your_devices);
|
|
|
|
builder.setPositiveButton(R.string.ConversationActivity_delete, (d, w) -> requestModel.onDelete());
|
|
|
|
} else if (recipient.isGroup()) {
|
|
|
|
builder.setTitle(R.string.ConversationActivity_delete_and_leave_group);
|
|
|
|
builder.setMessage(R.string.ConversationActivity_you_will_leave_this_group_and_it_will_be_deleted_from_all_of_your_devices);
|
|
|
|
builder.setNegativeButton(R.string.ConversationActivity_delete_and_leave, (d, w) -> requestModel.onDelete());
|
|
|
|
} else {
|
|
|
|
builder.setTitle(R.string.ConversationActivity_delete_conversation);
|
|
|
|
builder.setMessage(R.string.ConversationActivity_this_conversation_will_be_deleted_from_all_of_your_devices);
|
|
|
|
builder.setNegativeButton(R.string.ConversationActivity_delete, (d, w) -> requestModel.onDelete());
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onMessageRequestBlockClicked(@NonNull MessageRequestViewModel requestModel) {
|
|
|
|
Recipient recipient = requestModel.getRecipient().getValue();
|
|
|
|
if (recipient == null) {
|
|
|
|
Log.w(TAG, "[onMessageRequestBlockClicked] No recipient!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-16 17:30:51 +02:00
|
|
|
BlockUnblockDialog.showBlockAndDeleteFor(this, getLifecycle(), recipient, requestModel::onBlock, requestModel::onBlockAndDelete);
|
2020-02-21 19:52:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void onMessageRequestUnblockClicked(@NonNull MessageRequestViewModel requestModel) {
|
|
|
|
Recipient recipient = requestModel.getRecipient().getValue();
|
|
|
|
if (recipient == null) {
|
|
|
|
Log.w(TAG, "[onMessageRequestUnblockClicked] No recipient!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-16 17:30:51 +02:00
|
|
|
BlockUnblockDialog.showUnblockFor(this, getLifecycle(), recipient, requestModel::onUnblock);
|
2020-02-21 19:52:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void presentMessageRequestDisplayState(@NonNull MessageRequestViewModel.DisplayState displayState) {
|
2020-10-12 16:09:35 +02:00
|
|
|
if ((getIntent().hasExtra(TEXT_EXTRA) && !Util.isEmpty(getIntent().getStringExtra(TEXT_EXTRA))) ||
|
|
|
|
getIntent().hasExtra(MEDIA_EXTRA) ||
|
|
|
|
getIntent().hasExtra(STICKER_EXTRA))
|
|
|
|
{
|
2020-02-21 19:52:27 +01:00
|
|
|
Log.d(TAG, "[presentMessageRequestDisplayState] Have extra, so ignoring provided state.");
|
2020-02-19 23:08:34 +01:00
|
|
|
messageRequestBottomView.setVisibility(View.GONE);
|
2020-05-12 20:09:47 +02:00
|
|
|
} else if (isPushGroupV1Conversation() && !isActiveGroup()) {
|
|
|
|
Log.d(TAG, "[presentMessageRequestDisplayState] Inactive push group V1, so ignoring provided state.");
|
|
|
|
messageRequestBottomView.setVisibility(View.GONE);
|
2020-02-19 23:08:34 +01:00
|
|
|
} else {
|
2020-02-21 19:52:27 +01:00
|
|
|
Log.d(TAG, "[presentMessageRequestDisplayState] " + displayState);
|
|
|
|
switch (displayState) {
|
|
|
|
case DISPLAY_MESSAGE_REQUEST:
|
|
|
|
messageRequestBottomView.setVisibility(View.VISIBLE);
|
|
|
|
if (groupShareProfileView.resolved()) {
|
|
|
|
groupShareProfileView.get().setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DISPLAY_LEGACY:
|
|
|
|
if (recipient.get().isGroup()) {
|
|
|
|
groupShareProfileView.get().setRecipient(recipient.get());
|
|
|
|
groupShareProfileView.get().setVisibility(View.VISIBLE);
|
|
|
|
}
|
|
|
|
messageRequestBottomView.setVisibility(View.GONE);
|
|
|
|
break;
|
|
|
|
case DISPLAY_NONE:
|
|
|
|
messageRequestBottomView.setVisibility(View.GONE);
|
|
|
|
if (groupShareProfileView.resolved()) {
|
|
|
|
groupShareProfileView.get().setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-02-19 23:08:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
}
|
|
|
|
|
2020-04-27 17:42:34 +02:00
|
|
|
private static void hideMenuItem(@NonNull Menu menu, @IdRes int menuItem) {
|
|
|
|
if (menu.findItem(menuItem) != null) {
|
|
|
|
menu.findItem(menuItem).setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 17:54:47 +02:00
|
|
|
@WorkerThread
|
|
|
|
private @Nullable KeyboardImageDetails getKeyboardImageDetails(@NonNull Uri uri) {
|
|
|
|
try {
|
|
|
|
Bitmap bitmap = glideRequests.asBitmap()
|
2020-07-10 15:30:00 +02:00
|
|
|
.load(new DecryptableStreamUriLoader.DecryptableUri(uri))
|
2020-07-08 17:54:47 +02:00
|
|
|
.skipMemoryCache(true)
|
|
|
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
|
|
|
.submit()
|
|
|
|
.get(1000, TimeUnit.MILLISECONDS);
|
|
|
|
int topLeft = bitmap.getPixel(0, 0);
|
|
|
|
return new KeyboardImageDetails(bitmap.getWidth(), bitmap.getHeight(), Color.alpha(topLeft) < 255);
|
|
|
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendKeyboardImage(@NonNull Uri uri, @NonNull String contentType, @Nullable KeyboardImageDetails details) {
|
|
|
|
if (details == null || !details.hasTransparency) {
|
|
|
|
setMedia(uri, Objects.requireNonNull(MediaType.from(contentType)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long expiresIn = recipient.get().getExpireMessages() * 1000L;
|
|
|
|
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
|
|
|
|
boolean initiating = threadId == -1;
|
|
|
|
SlideDeck slideDeck = new SlideDeck();
|
|
|
|
|
|
|
|
if (MediaUtil.isGif(contentType)) {
|
|
|
|
slideDeck.addSlide(new GifSlide(this, uri, 0, details.width, details.height, details.hasTransparency, null));
|
|
|
|
} else if (MediaUtil.isImageType(contentType)) {
|
|
|
|
slideDeck.addSlide(new ImageSlide(this, uri, contentType, 0, details.width, details.height, details.hasTransparency, null, null));
|
|
|
|
} else {
|
|
|
|
throw new AssertionError("Only images are supported!");
|
|
|
|
}
|
|
|
|
|
|
|
|
sendMediaMessage(isSmsForced(),
|
|
|
|
"",
|
|
|
|
slideDeck,
|
2020-09-24 18:11:40 +02:00
|
|
|
null,
|
2020-07-08 17:54:47 +02:00
|
|
|
Collections.emptyList(),
|
|
|
|
Collections.emptyList(),
|
2020-08-05 22:45:52 +02:00
|
|
|
composeText.getMentions(),
|
2020-07-08 17:54:47 +02:00
|
|
|
expiresIn,
|
|
|
|
false,
|
|
|
|
subscriptionId,
|
|
|
|
initiating,
|
2020-09-24 18:11:40 +02:00
|
|
|
false);
|
2020-07-08 17:54:47 +02:00
|
|
|
}
|
|
|
|
|
2017-06-07 03:03:09 +02:00
|
|
|
private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener {
|
|
|
|
@Override
|
|
|
|
public void onDismissed(final List<IdentityRecord> unverifiedIdentities) {
|
|
|
|
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this);
|
|
|
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... params) {
|
|
|
|
synchronized (SESSION_LOCK) {
|
|
|
|
for (IdentityRecord identityRecord : unverifiedIdentities) {
|
2019-08-07 20:22:51 +02:00
|
|
|
identityDatabase.setVerified(identityRecord.getRecipientId(),
|
2017-06-07 03:03:09 +02:00
|
|
|
identityRecord.getIdentityKey(),
|
|
|
|
VerifiedStatus.DEFAULT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Void result) {
|
|
|
|
initializeIdentityRecords();
|
|
|
|
}
|
2017-10-23 22:03:32 +02:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class UnverifiedClickedListener implements UnverifiedBannerView.ClickListener {
|
|
|
|
@Override
|
|
|
|
public void onClicked(final List<IdentityRecord> unverifiedIdentities) {
|
2018-08-02 15:25:33 +02:00
|
|
|
Log.i(TAG, "onClicked: " + unverifiedIdentities.size());
|
2017-06-07 03:03:09 +02:00
|
|
|
if (unverifiedIdentities.size() == 1) {
|
2020-04-21 14:47:59 +02:00
|
|
|
startActivity(VerifyIdentityActivity.newIntent(ConversationActivity.this, unverifiedIdentities.get(0), false));
|
2017-06-07 03:03:09 +02:00
|
|
|
} else {
|
|
|
|
String[] unverifiedNames = new String[unverifiedIdentities.size()];
|
|
|
|
|
|
|
|
for (int i=0;i<unverifiedIdentities.size();i++) {
|
2020-06-16 16:37:27 +02:00
|
|
|
unverifiedNames[i] = Recipient.resolved(unverifiedIdentities.get(i).getRecipientId()).getDisplayName(ConversationActivity.this);
|
2017-06-07 03:03:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(ConversationActivity.this);
|
|
|
|
builder.setIconAttribute(R.attr.dialog_alert_icon);
|
|
|
|
builder.setTitle("No longer verified");
|
2017-12-05 20:52:03 +01:00
|
|
|
builder.setItems(unverifiedNames, (dialog, which) -> {
|
2020-04-21 14:47:59 +02:00
|
|
|
startActivity(VerifyIdentityActivity.newIntent(ConversationActivity.this, unverifiedIdentities.get(which), false));
|
2017-06-07 03:03:09 +02:00
|
|
|
});
|
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-24 20:09:54 +02:00
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
private class QuoteRestorationTask extends AsyncTask<Void, Void, ConversationMessage> {
|
2018-04-24 20:09:54 +02:00
|
|
|
|
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
|
2020-08-05 22:45:52 +02:00
|
|
|
protected ConversationMessage doInBackground(Void... voids) {
|
2019-08-07 20:22:51 +02:00
|
|
|
QuoteId quoteId = QuoteId.deserialize(ConversationActivity.this, serialized);
|
2018-04-24 20:09:54 +02:00
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
if (quoteId == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Context context = getApplicationContext();
|
|
|
|
|
|
|
|
MessageRecord messageRecord = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteId.getId(), quoteId.getAuthor());
|
|
|
|
if (messageRecord == null) {
|
|
|
|
return null;
|
2018-04-24 20:09:54 +02:00
|
|
|
}
|
|
|
|
|
2020-08-05 22:45:52 +02:00
|
|
|
return ConversationMessageFactory.createWithUnresolvedData(context, messageRecord);
|
2018-04-24 20:09:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-08-05 22:45:52 +02:00
|
|
|
protected void onPostExecute(ConversationMessage conversationMessage) {
|
|
|
|
if (conversationMessage != null) {
|
|
|
|
handleReplyMessage(conversationMessage);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-19 23:08:34 +01:00
|
|
|
|
2020-10-05 19:50:35 +02:00
|
|
|
private void presentMessageRequestBottomViewTo(@Nullable MessageRequestViewModel.MessageData messageData) {
|
|
|
|
if (messageData == null) return;
|
2020-02-19 23:08:34 +01:00
|
|
|
|
2020-10-05 19:50:35 +02:00
|
|
|
messageRequestBottomView.setMessageData(messageData);
|
2020-02-19 23:08:34 +01:00
|
|
|
}
|
2020-07-08 17:54:47 +02:00
|
|
|
|
|
|
|
private static class KeyboardImageDetails {
|
|
|
|
private final int width;
|
|
|
|
private final int height;
|
|
|
|
private final boolean hasTransparency;
|
|
|
|
|
|
|
|
private KeyboardImageDetails(int width, int height, boolean hasTransparency) {
|
|
|
|
this.width = width;
|
|
|
|
this.height = height;
|
|
|
|
this.hasTransparency = hasTransparency;
|
|
|
|
}
|
|
|
|
}
|
2012-07-19 23:22:03 +02:00
|
|
|
}
|