gt:     if (reminderActionId == R.id.reminder_action_update_now) {
context: /*
   * Copyright (C) 2015 Open 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/>.
   */
  package org.thoughtcrime.securesms.conversationlist;
  
  import android.Manifest;
  import android.annotation.SuppressLint;
  import android.app.Activity;
  import android.app.ProgressDialog;
  import android.content.Context;
  import android.content.Intent;
  import android.content.res.Resources;
  import android.content.res.TypedArray;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.drawable.Drawable;
  import android.net.Uri;
  import android.os.AsyncTask;
  import android.os.Build;
  import android.os.Bundle;
  import android.view.LayoutInflater;
  import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.inputmethod.InputMethodManager;
  import android.widget.FrameLayout;
  import android.widget.ImageView;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import androidx.annotation.DrawableRes;
  import androidx.annotation.IdRes;
  import androidx.annotation.MenuRes;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.annotation.PluralsRes;
  import androidx.annotation.WorkerThread;
  import androidx.appcompat.app.AlertDialog;
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.appcompat.view.ActionMode;
  import androidx.appcompat.widget.Toolbar;
  import androidx.appcompat.widget.TooltipCompat;
  import androidx.constraintlayout.widget.ConstraintLayout;
  import androidx.constraintlayout.widget.ConstraintSet;
  import androidx.core.content.res.ResourcesCompat;
  import androidx.fragment.app.DialogFragment;
  import androidx.lifecycle.ViewModelProviders;
  import androidx.recyclerview.widget.ItemTouchHelper;
  import androidx.recyclerview.widget.LinearLayoutManager;
  import androidx.recyclerview.widget.RecyclerView;
  import androidx.transition.TransitionManager;
  
  import com.annimon.stream.Stream;
  import com.google.android.material.snackbar.Snackbar;
  
  import org.greenrobot.eventbus.EventBus;
  import org.greenrobot.eventbus.Subscribe;
  import org.greenrobot.eventbus.ThreadMode;
  import org.signal.core.util.concurrent.SignalExecutors;
  import org.signal.core.util.logging.Log;
  import org.thoughtcrime.securesms.MainFragment;
  import org.thoughtcrime.securesms.MainNavigator;
  import org.thoughtcrime.securesms.NewConversationActivity;
  import org.thoughtcrime.securesms.R;
  import org.thoughtcrime.securesms.components.RatingManager;
  import org.thoughtcrime.securesms.components.SearchToolbar;
  import org.thoughtcrime.securesms.components.UnreadPaymentsView;
  import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator;
  import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
  import org.thoughtcrime.securesms.components.reminder.DozeReminder;
  import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
  import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder;
  import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder;
  import org.thoughtcrime.securesms.components.reminder.Reminder;
  import org.thoughtcrime.securesms.components.reminder.ReminderView;
  import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder;
  import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder;
  import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
  import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
  import org.thoughtcrime.securesms.components.voice.VoiceNotePlayerView;
  import org.thoughtcrime.securesms.conversation.ConversationFragment;
  import org.thoughtcrime.securesms.conversationlist.model.Conversation;
  import org.thoughtcrime.securesms.conversationlist.model.UnreadPayments;
  import org.thoughtcrime.securesms.database.DatabaseFactory;
  import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
  import org.thoughtcrime.securesms.database.ThreadDatabase;
  import org.thoughtcrime.securesms.database.model.ThreadRecord;
  import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
  import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
  import org.thoughtcrime.securesms.insights.InsightsLauncher;
  import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
  import org.thoughtcrime.securesms.keyvalue.SignalStore;
  import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
  import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
  import org.thoughtcrime.securesms.megaphone.Megaphone;
  import org.thoughtcrime.securesms.megaphone.MegaphoneActionController;
  import org.thoughtcrime.securesms.megaphone.MegaphoneViewBuilder;
  import org.thoughtcrime.securesms.megaphone.Megaphones;
  import org.thoughtcrime.securesms.mms.GlideApp;
  import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
  import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity;
  import org.thoughtcrime.securesms.payments.preferences.details.PaymentDetailsFragmentArgs;
  import org.thoughtcrime.securesms.payments.preferences.details.PaymentDetailsParcelable;
  import org.thoughtcrime.securesms.permissions.Permissions;
  import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment;
  import org.thoughtcrime.securesms.recipients.Recipient;
  import org.thoughtcrime.securesms.recipients.RecipientId;
  import org.thoughtcrime.securesms.search.MessageResult;
  import org.thoughtcrime.securesms.search.SearchResult;
  import org.thoughtcrime.securesms.service.KeyCachingService;
  import org.thoughtcrime.securesms.sms.MessageSender;
  import org.thoughtcrime.securesms.storage.StorageSyncHelper;
  import org.thoughtcrime.securesms.util.AppForegroundObserver;
  import org.thoughtcrime.securesms.util.AppStartup;
  import org.thoughtcrime.securesms.util.AvatarUtil;
  import org.thoughtcrime.securesms.util.PlayStoreUtil;
  import org.thoughtcrime.securesms.util.ServiceUtil;
  import org.thoughtcrime.securesms.util.SignalLocalMetrics;
  import org.thoughtcrime.securesms.util.SignalProxyUtil;
  import org.thoughtcrime.securesms.util.SnapToTopDataObserver;
  import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
  import org.thoughtcrime.securesms.util.Stopwatch;
  import org.thoughtcrime.securesms.util.TextSecurePreferences;
  import org.thoughtcrime.securesms.util.Util;
  import org.thoughtcrime.securesms.util.ViewUtil;
  import org.thoughtcrime.securesms.util.WindowUtil;
  import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
  import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
  import org.thoughtcrime.securesms.util.views.Stub;
  import org.whispersystems.libsignal.util.guava.Optional;
  import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState;
  
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Objects;
  import java.util.Set;
  import java.util.UUID;
  
  import static android.app.Activity.RESULT_OK;
  
  
  public class ConversationListFragment extends MainFragment implements ActionMode.Callback,
                                                                        ConversationListAdapter.OnConversationClickListener,
                                                                        ConversationListSearchAdapter.EventListener,
                                                                        MainNavigator.BackHandler,
                                                                        MegaphoneActionController
  {
    public static final short MESSAGE_REQUESTS_REQUEST_CODE_CREATE_NAME = 32562;
    public static final short SMS_ROLE_REQUEST_CODE                     = 32563;
  
    private static final String TAG = Log.tag(ConversationListFragment.class);
  
    private static final int MAXIMUM_PINNED_CONVERSATIONS = 4;
  
    private ActionMode                        actionMode;
    private ConstraintLayout                  constraintLayout;
    private RecyclerView                      list;
    private Stub<ReminderView>                reminderView;
    private Stub<UnreadPaymentsView>          paymentNotificationView;
    private Stub<ViewGroup>                   emptyState;
    private TextView                          searchEmptyState;
    private PulsingFloatingActionButton       fab;
    private PulsingFloatingActionButton       cameraFab;
    private Stub<SearchToolbar>               searchToolbar;
    private ImageView                         proxyStatus;
    private ImageView                         searchAction;
    private View                              toolbarShadow;
    private View                              unreadPaymentsDot;
    private ConversationListViewModel         viewModel;
    private RecyclerView.Adapter              activeAdapter;
    private ConversationListAdapter           defaultAdapter;
    private ConversationListSearchAdapter     searchAdapter;
    private StickyHeaderDecoration            searchAdapterDecoration;
    private Stub<ViewGroup>                   megaphoneContainer;
    private SnapToTopDataObserver             snapToTopDataObserver;
    private Drawable                          archiveDrawable;
    private AppForegroundObserver.Listener    appForegroundObserver;
    private VoiceNoteMediaControllerOwner     mediaControllerOwner;
    private Stub<FrameLayout>                 voiceNotePlayerViewStub;
    private VoiceNotePlayerView               voiceNotePlayerView;
  
  
    private Stopwatch startupStopwatch;
  
    public static ConversationListFragment newInstance() {
      return new ConversationListFragment();
    }
  
    @Override
    public void onAttach(@NonNull Context context) {
      super.onAttach(context);
  
      if (context instanceof VoiceNoteMediaControllerOwner) {
        mediaControllerOwner = (VoiceNoteMediaControllerOwner) context;
      } else {
        throw new ClassCastException("Expected context to be a Listener");
      }
    }
  
    @Override
    public void onCreate(Bundle icicle) {
      super.onCreate(icicle);
      setHasOptionsMenu(true);
      startupStopwatch = new Stopwatch("startup");
    }
  
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
      return inflater.inflate(R.layout.conversation_list_fragment, container, false);
    }
  
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
      constraintLayout        = view.findViewById(R.id.constraint_layout);
      list                    = view.findViewById(R.id.list);
      fab                     = view.findViewById(R.id.fab);
      cameraFab               = view.findViewById(R.id.camera_fab);
      searchEmptyState        = view.findViewById(R.id.search_no_results);
      searchAction            = view.findViewById(R.id.search_action);
      toolbarShadow           = view.findViewById(R.id.conversation_list_toolbar_shadow);
      proxyStatus             = view.findViewById(R.id.conversation_list_proxy_status);
      unreadPaymentsDot       = view.findViewById(R.id.unread_payments_indicator);
      reminderView            = new Stub<>(view.findViewById(R.id.reminder));
      emptyState              = new Stub<>(view.findViewById(R.id.empty_state));
      searchToolbar           = new Stub<>(view.findViewById(R.id.search_toolbar));
      megaphoneContainer      = new Stub<>(view.findViewById(R.id.megaphone_container));
      paymentNotificationView = new Stub<>(view.findViewById(R.id.payments_notification));
      voiceNotePlayerViewStub = new Stub<>(view.findViewById(R.id.voice_note_player));
  
      Toolbar toolbar = getToolbar(view);
      toolbar.setVisibility(View.VISIBLE);
      ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
  
      proxyStatus.setOnClickListener(v -> onProxyStatusClicked());
  
      fab.show();
      cameraFab.show();
  
      list.setLayoutManager(new LinearLayoutManager(requireActivity()));
      list.setItemAnimator(new DeleteItemAnimator());
      list.addOnScrollListener(new ScrollListener());
  
      snapToTopDataObserver = new SnapToTopDataObserver(list);
  
      new ItemTouchHelper(new ArchiveListenerCallback()).attachToRecyclerView(list);
  
      fab.setOnClickListener(v -> startActivity(new Intent(getActivity(), NewConversationActivity.class)));
      cameraFab.setOnClickListener(v -> {
        Permissions.with(requireActivity())
                   .request(Manifest.permission.CAMERA)
                   .ifNecessary()
                   .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_camera_24)
                   .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
                   .onAllGranted(() -> startActivity(MediaSendActivity.buildCameraFirstIntent(requireActivity())))
                   .onAnyDenied(() -> Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
                   .execute();
      });
  
      initializeViewModel();
      initializeListAdapters();
      initializeTypingObserver();
      initializeSearchListener();
      initializeVoiceNotePlayer();
  
      RatingManager.showRatingDialogIfNecessary(requireContext());
  
      TooltipCompat.setTooltipText(searchAction, getText(R.string.SearchToolbar_search_for_conversations_contacts_and_messages));
    }
  
    @Override
    public void onResume() {
      super.onResume();
  
      updateReminders();
      EventBus.getDefault().register(this);
  
      if (Util.isDefaultSmsProvider(requireContext())) {
        InsightsLauncher.showInsightsModal(requireContext(), requireFragmentManager());
      }
  
      SimpleTask.run(getViewLifecycleOwner().getLifecycle(), Recipient::self, this::initializeProfileIcon);
  
      if ((!searchToolbar.resolved() || !searchToolbar.get().isVisible()) && list.getAdapter() != defaultAdapter) {
        list.removeItemDecoration(searchAdapterDecoration);
        setAdapter(defaultAdapter);
      }
  
      if (activeAdapter != null) {
        activeAdapter.notifyDataSetChanged();
      }
  
      SignalProxyUtil.startListeningToWebsocket();
  
      if (SignalStore.rateLimit().needsRecaptcha()) {
        Log.i(TAG, "Recaptcha required.");
        RecaptchaProofBottomSheetFragment.show(getChildFragmentManager());
      }
    }
  
    @Override
    public void onStart() {
      super.onStart();
      ConversationFragment.prepare(requireContext());
      ApplicationDependencies.getAppForegroundObserver().addListener(appForegroundObserver);
    }
  
    @Override
    public void onPause() {
      super.onPause();
  
      fab.stopPulse();
      cameraFab.stopPulse();
      EventBus.getDefault().unregister(this);
    }
  
    @Override
    public void onStop() {
      super.onStop();
      ApplicationDependencies.getAppForegroundObserver().removeListener(appForegroundObserver);
    }
  
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
      menu.clear();
      inflater.inflate(R.menu.text_secure_normal, menu);
    }
  
    @Override
    public void onPrepareOptionsMenu(Menu menu) {
      menu.findItem(R.id.menu_insights).setVisible(Util.isDefaultSmsProvider(requireContext()));
      menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(requireContext()));
    }
  
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
      super.onOptionsItemSelected(item);
  
      switch (item.getItemId()) {
        case R.id.menu_new_group:         handleCreateGroup();     return true;
        case R.id.menu_settings:          handleDisplaySettings(); return true;
        case R.id.menu_clear_passphrase:  handleClearPassphrase(); return true;
        case R.id.menu_mark_all_read:     handleMarkAllRead();     return true;
        case R.id.menu_invite:            handleInvite();          return true;
        case R.id.menu_insights:          handleInsights();        return true;
      }
  
      return false;
    }
  
    @Override
    public boolean onBackPressed() {
      return closeSearchIfOpen();
    }
  
    private boolean closeSearchIfOpen() {
      if ((searchToolbar.resolved() && searchToolbar.get().isVisible()) || activeAdapter == searchAdapter) {
        list.removeItemDecoration(searchAdapterDecoration);
        setAdapter(defaultAdapter);
        searchToolbar.get().collapse();
        return true;
      }
  
      return false;
    }
  
    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      if (resultCode != RESULT_OK) {
        return;
      }
  
      if (requestCode == CreateKbsPinActivity.REQUEST_NEW_PIN) {
        Snackbar.make(fab, R.string.ConfirmKbsPinFragment__pin_created, Snackbar.LENGTH_LONG).setTextColor(Color.WHITE).show();
        viewModel.onMegaphoneCompleted(Megaphones.Event.PINS_FOR_ALL);
      }
    }
  
    @Override
    public void onConversationClicked(@NonNull ThreadRecord threadRecord) {
      hideKeyboard();
      getNavigator().goToConversation(threadRecord.getRecipient().getId(),
                                      threadRecord.getThreadId(),
                                      threadRecord.getDistributionType(),
                                      -1);
    }
  
    @Override
    public void onShowArchiveClick() {
      getNavigator().goToArchiveList();
    }
  
    @Override
    public void onContactClicked(@NonNull Recipient contact) {
      SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
        return DatabaseFactory.getThreadDatabase(getContext()).getThreadIdIfExistsFor(contact.getId());
      }, threadId -> {
        hideKeyboard();
        getNavigator().goToConversation(contact.getId(),
                                        threadId,
                                        ThreadDatabase.DistributionTypes.DEFAULT,
                                        -1);
      });
    }
  
    @Override
    public void onMessageClicked(@NonNull MessageResult message) {
      SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
        int startingPosition = DatabaseFactory.getMmsSmsDatabase(getContext()).getMessagePositionInConversation(message.getThreadId(), message.getReceivedTimestampMs());
        return Math.max(0, startingPosition);
      }, startingPosition -> {
        hideKeyboard();
        getNavigator().goToConversation(message.getConversationRecipient().getId(),
                                        message.getThreadId(),
                                        ThreadDatabase.DistributionTypes.DEFAULT,
                                        startingPosition);
      });
    }
  
    @Override
    public void onMegaphoneNavigationRequested(@NonNull Intent intent) {
      startActivity(intent);
    }
  
    @Override
    public void onMegaphoneNavigationRequested(@NonNull Intent intent, int requestCode) {
      startActivityForResult(intent, requestCode);
    }
  
    @Override
    public void onMegaphoneToastRequested(@NonNull String string) {
      Snackbar.make(fab, string, Snackbar.LENGTH_LONG)
              .setTextColor(Color.WHITE)
              .show();
    }
  
    @Override
    public @NonNull Activity getMegaphoneActivity() {
      return requireActivity();
    }
  
    @Override
    public void onMegaphoneSnooze(@NonNull Megaphones.Event event) {
      viewModel.onMegaphoneSnoozed(event);
    }
  
    @Override
    public void onMegaphoneCompleted(@NonNull Megaphones.Event event) {
      viewModel.onMegaphoneCompleted(event);
    }
  
    @Override
    public void onMegaphoneDialogFragmentRequested(@NonNull DialogFragment dialogFragment) {
      dialogFragment.show(getChildFragmentManager(), "megaphone_dialog");
    }
  
    private void initializeReminderView() {
      reminderView.get().setOnDismissListener(this::updateReminders);
      reminderView.get().setOnActionClickListener(this::onReminderAction);
    }
  
    private void onReminderAction(@IdRes int reminderActionId) {
  