diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListArchiveFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListArchiveFragment.java index efc7f6b30c..86f050a88f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListArchiveFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListArchiveFragment.java @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask; +import org.thoughtcrime.securesms.util.views.Stub; import java.util.Set; @@ -45,9 +46,10 @@ import java.util.Set; public class ConversationListArchiveFragment extends ConversationListFragment implements ActionMode.Callback { private RecyclerView list; - private View emptyState; + private Stub emptyState; private PulsingFloatingActionButton fab; private PulsingFloatingActionButton cameraFab; + private Stub toolbar; public static ConversationListArchiveFragment newInstance() { return new ConversationListArchiveFragment(); @@ -61,17 +63,18 @@ public class ConversationListArchiveFragment extends ConversationListFragment im @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + toolbar = new Stub<>(view.findViewById(R.id.toolbar_basic)); + super.onViewCreated(view, savedInstanceState); list = view.findViewById(R.id.list); fab = view.findViewById(R.id.fab); cameraFab = view.findViewById(R.id.camera_fab); - emptyState = view.findViewById(R.id.empty_state); + emptyState = new Stub<>(view.findViewById(R.id.empty_state)); ((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); - Toolbar toolbar = view.findViewById(R.id.toolbar_basic); - toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed()); - toolbar.setTitle(R.string.AndroidManifest_archived_conversations); + toolbar.get().setNavigationOnClickListener(v -> requireActivity().onBackPressed()); + toolbar.get().setTitle(R.string.AndroidManifest_archived_conversations); fab.hide(); cameraFab.hide(); @@ -80,7 +83,10 @@ public class ConversationListArchiveFragment extends ConversationListFragment im @Override protected void onPostSubmitList() { list.setVisibility(View.VISIBLE); - emptyState.setVisibility(View.GONE); + + if (emptyState.resolved()) { + emptyState.get().setVisibility(View.GONE); + } } @Override @@ -89,8 +95,8 @@ public class ConversationListArchiveFragment extends ConversationListFragment im } @Override - protected int getToolbarRes() { - return R.id.toolbar_basic; + protected @NonNull Toolbar getToolbar(@NonNull View rootView) { + return toolbar.get(); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 966a9923b3..7d458588cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -103,7 +103,6 @@ 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; @@ -128,6 +127,7 @@ 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 java.util.Collections; @@ -162,13 +162,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode private ActionMode actionMode; private RecyclerView list; - private ReminderView reminderView; - private View emptyState; - private ImageView emptyImage; + private Stub reminderView; + private Stub emptyState; private TextView searchEmptyState; private PulsingFloatingActionButton fab; private PulsingFloatingActionButton cameraFab; - private SearchToolbar searchToolbar; + private Stub searchToolbar; private ImageView searchAction; private View toolbarShadow; private ConversationListViewModel viewModel; @@ -176,7 +175,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode private ConversationListAdapter defaultAdapter; private ConversationListSearchAdapter searchAdapter; private StickyHeaderDecoration searchAdapterDecoration; - private ViewGroup megaphoneContainer; + private Stub megaphoneContainer; private SnapToTopDataObserver snapToTopDataObserver; private Drawable archiveDrawable; private LifecycleObserver visibilityLifecycleObserver; @@ -198,28 +197,24 @@ public class ConversationListFragment extends MainFragment implements ActionMode @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - reminderView = view.findViewById(R.id.reminder); list = view.findViewById(R.id.list); fab = view.findViewById(R.id.fab); cameraFab = view.findViewById(R.id.camera_fab); - emptyState = view.findViewById(R.id.empty_state); - emptyImage = view.findViewById(R.id.empty); searchEmptyState = view.findViewById(R.id.search_no_results); - searchToolbar = view.findViewById(R.id.search_toolbar); searchAction = view.findViewById(R.id.search_action); toolbarShadow = view.findViewById(R.id.conversation_list_toolbar_shadow); - megaphoneContainer = view.findViewById(R.id.megaphone_container); + 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)); - Toolbar toolbar = view.findViewById(getToolbarRes()); + Toolbar toolbar = getToolbar(view); toolbar.setVisibility(View.VISIBLE); ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); fab.show(); cameraFab.show(); - reminderView.setOnDismissListener(this::updateReminders); - reminderView.setOnActionClickListener(this::onReminderAction); - list.setLayoutManager(new LinearLayoutManager(requireActivity())); list.setItemAnimator(new DeleteItemAnimator()); list.addOnScrollListener(new ScrollListener()); @@ -263,7 +258,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode SimpleTask.run(getViewLifecycleOwner().getLifecycle(), Recipient::self, this::initializeProfileIcon); - if (!searchToolbar.isVisible() && list.getAdapter() != defaultAdapter) { + if ((!searchToolbar.resolved() || !searchToolbar.get().isVisible()) && list.getAdapter() != defaultAdapter) { list.removeItemDecoration(searchAdapterDecoration); setAdapter(defaultAdapter); } @@ -329,10 +324,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode } private boolean closeSearchIfOpen() { - if (searchToolbar.isVisible() || activeAdapter == searchAdapter) { + if ((searchToolbar.resolved() && searchToolbar.get().isVisible()) || activeAdapter == searchAdapter) { list.removeItemDecoration(searchAdapterDecoration); setAdapter(defaultAdapter); - searchToolbar.collapse(); + searchToolbar.get().collapse(); return true; } @@ -429,6 +424,11 @@ public class ConversationListFragment extends MainFragment implements ActionMode dialogFragment.show(getChildFragmentManager(), "megaphone_dialog"); } + private void initializeReminderView() { + reminderView.get().setOnDismissListener(this::updateReminders); + reminderView.get().setOnActionClickListener(this::onReminderAction); + } + private void onReminderAction(@IdRes int reminderActionId) { if (reminderActionId == R.id.reminder_action_update_now) { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()); @@ -449,36 +449,36 @@ public class ConversationListFragment extends MainFragment implements ActionMode private void initializeSearchListener() { searchAction.setOnClickListener(v -> { - searchToolbar.display(searchAction.getX() + (searchAction.getWidth() / 2.0f), - searchAction.getY() + (searchAction.getHeight() / 2.0f)); - }); + searchToolbar.get().display(searchAction.getX() + (searchAction.getWidth() / 2.0f), + searchAction.getY() + (searchAction.getHeight() / 2.0f)); - searchToolbar.setListener(new SearchToolbar.SearchListener() { - @Override - public void onSearchTextChange(String text) { - String trimmed = text.trim(); + searchToolbar.get().setListener(new SearchToolbar.SearchListener() { + @Override + public void onSearchTextChange(String text) { + String trimmed = text.trim(); - viewModel.updateQuery(trimmed); + viewModel.updateQuery(trimmed); - if (trimmed.length() > 0) { - if (activeAdapter != searchAdapter) { - setAdapter(searchAdapter); - list.removeItemDecoration(searchAdapterDecoration); - list.addItemDecoration(searchAdapterDecoration); - } - } else { - if (activeAdapter != defaultAdapter) { - list.removeItemDecoration(searchAdapterDecoration); - setAdapter(defaultAdapter); + if (trimmed.length() > 0) { + if (activeAdapter != searchAdapter) { + setAdapter(searchAdapter); + list.removeItemDecoration(searchAdapterDecoration); + list.addItemDecoration(searchAdapterDecoration); + } + } else { + if (activeAdapter != defaultAdapter) { + list.removeItemDecoration(searchAdapterDecoration); + setAdapter(defaultAdapter); + } } } - } - @Override - public void onSearchClosed() { - list.removeItemDecoration(searchAdapterDecoration); - setAdapter(defaultAdapter); - } + @Override + public void onSearchClosed() { + list.removeItemDecoration(searchAdapterDecoration); + setAdapter(defaultAdapter); + } + }); }); } @@ -557,20 +557,22 @@ public class ConversationListFragment extends MainFragment implements ActionMode private void onMegaphoneChanged(@Nullable Megaphone megaphone) { if (megaphone == null) { - megaphoneContainer.setVisibility(View.GONE); - megaphoneContainer.removeAllViews(); + if (megaphoneContainer.resolved()) { + megaphoneContainer.get().setVisibility(View.GONE); + megaphoneContainer.get().removeAllViews(); + } return; } View view = MegaphoneViewBuilder.build(requireContext(), megaphone, this); - megaphoneContainer.removeAllViews(); + megaphoneContainer.get().removeAllViews(); if (view != null) { - megaphoneContainer.addView(view); - megaphoneContainer.setVisibility(View.VISIBLE); + megaphoneContainer.get().addView(view); + megaphoneContainer.get().setVisibility(View.VISIBLE); } else { - megaphoneContainer.setVisibility(View.GONE); + megaphoneContainer.get().setVisibility(View.GONE); if (megaphone.getOnVisibleListener() != null) { megaphone.getOnVisibleListener().onEvent(megaphone, this); @@ -608,9 +610,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode } }, reminder -> { if (reminder.isPresent() && getActivity() != null && !isRemoving()) { - reminderView.showReminder(reminder.get()); - } else if (!reminder.isPresent()) { - reminderView.hide(); + if (!reminderView.resolved()) { + initializeReminderView(); + } + reminderView.get().showReminder(reminder.get()); + } else if (reminderView.resolved() && !reminder.isPresent()) { + reminderView.get().hide(); } }); } @@ -831,15 +836,20 @@ public class ConversationListFragment extends MainFragment implements ActionMode if (isConversationEmpty) { Log.i(TAG, "Received an empty data set."); list.setVisibility(View.INVISIBLE); - emptyState.setVisibility(View.VISIBLE); - emptyImage.setImageResource(EMPTY_IMAGES[(int) (Math.random() * EMPTY_IMAGES.length)]); + emptyState.get().setVisibility(View.VISIBLE); fab.startPulse(3 * 1000); cameraFab.startPulse(3 * 1000); + + ImageView emptyImage = emptyState.get().findViewById(R.id.empty); + emptyImage.setImageResource(EMPTY_IMAGES[(int) (Math.random() * EMPTY_IMAGES.length)]); } else { list.setVisibility(View.VISIBLE); - emptyState.setVisibility(View.GONE); fab.stopPulse(); cameraFab.stopPulse(); + + if (emptyState.resolved()) { + emptyState.get().setVisibility(View.GONE); + } } } @@ -973,8 +983,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode } } - protected @IdRes int getToolbarRes() { - return R.id.toolbar; + protected Toolbar getToolbar(@NonNull View rootView) { + return rootView.findViewById(R.id.toolbar); } protected @PluralsRes int getArchivedSnackbarTitleRes() { diff --git a/app/src/main/res/layout/conversation_list_archive_toolbar.xml b/app/src/main/res/layout/conversation_list_archive_toolbar.xml new file mode 100644 index 0000000000..aaa6290389 --- /dev/null +++ b/app/src/main/res/layout/conversation_list_archive_toolbar.xml @@ -0,0 +1,13 @@ + + diff --git a/app/src/main/res/layout/conversation_list_empty_state.xml b/app/src/main/res/layout/conversation_list_empty_state.xml new file mode 100644 index 0000000000..1c2fb372d6 --- /dev/null +++ b/app/src/main/res/layout/conversation_list_empty_state.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/app/src/main/res/layout/conversation_list_fragment.xml b/app/src/main/res/layout/conversation_list_fragment.xml index ab6e76d1d5..e6ddc139da 100644 --- a/app/src/main/res/layout/conversation_list_fragment.xml +++ b/app/src/main/res/layout/conversation_list_fragment.xml @@ -1,6 +1,7 @@ - - + app:layout_constraintTop_toTopOf="parent" /> - - + app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" /> - - - - - - - + android:layout="@layout/conversation_list_reminder_view" + app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" /> - - + app:layout_constraintBottom_toBottomOf="parent" /> - + diff --git a/app/src/main/res/layout/conversation_list_megaphone_container.xml b/app/src/main/res/layout/conversation_list_megaphone_container.xml new file mode 100644 index 0000000000..af4549d477 --- /dev/null +++ b/app/src/main/res/layout/conversation_list_megaphone_container.xml @@ -0,0 +1,19 @@ + + diff --git a/app/src/main/res/layout/conversation_list_reminder_view.xml b/app/src/main/res/layout/conversation_list_reminder_view.xml new file mode 100644 index 0000000000..1af341fa65 --- /dev/null +++ b/app/src/main/res/layout/conversation_list_reminder_view.xml @@ -0,0 +1,8 @@ + + diff --git a/app/src/main/res/layout/conversation_list_search_toolbar.xml b/app/src/main/res/layout/conversation_list_search_toolbar.xml new file mode 100644 index 0000000000..3177455a7d --- /dev/null +++ b/app/src/main/res/layout/conversation_list_search_toolbar.xml @@ -0,0 +1,11 @@ + +