Maintain app bar layout when switching tabs.

fork-5.53.8
Alex Hart 2022-03-18 13:03:06 -03:00 zatwierdzone przez Greyson Parrelli
rodzic ffad2c7386
commit f798866619
15 zmienionych plików z 574 dodań i 379 usunięć

Wyświetl plik

@ -10,7 +10,6 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
@ -19,7 +18,6 @@ import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferLock
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsState;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.CachedInflater;
@ -60,8 +58,6 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
ConversationListTabRepository repository = new ConversationListTabRepository();
ConversationListTabsViewModel.Factory factory = new ConversationListTabsViewModel.Factory(repository);
navigator.onCreate(savedInstanceState);
handleGroupLinkInIntent(getIntent());
handleProxyInIntent(getIntent());
handleSignalMeIntent(getIntent());
@ -69,18 +65,6 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
CachedInflater.from(this).clear();
conversationListTabsViewModel = new ViewModelProvider(this, factory).get(ConversationListTabsViewModel.class);
Transformations.distinctUntilChanged(Transformations.map(conversationListTabsViewModel.getState(), ConversationListTabsState::getTab))
.observe(this, tab -> {
switch (tab) {
case CHATS:
getSupportFragmentManager().popBackStack();
break;
case STORIES:
navigator.goToStories();
break;
}
});
updateTabVisibility();
}
@ -138,7 +122,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
} else {
findViewById(R.id.conversation_list_tabs).setVisibility(View.GONE);
WindowUtil.setNavigationBarColor(getWindow(), ContextCompat.getColor(this, R.color.signal_background_primary));
navigator.goToChats();
conversationListTabsViewModel.onChatsSelected();
}
}

Wyświetl plik

@ -2,26 +2,19 @@ package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.insights.InsightsLauncher;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment;
public class MainNavigator {
public static final String STORIES_TAG = "STORIES";
public static final int REQUEST_CONFIG_CHANGES = 901;
private final MainActivity activity;
@ -38,16 +31,6 @@ public class MainNavigator {
return ((MainActivity) activity).getNavigator();
}
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
return;
}
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, ConversationListFragment.newInstance())
.commit();
}
/**
* @return True if the back pressed was handled in our own custom way, false if it should be given
* to the system to do the default behavior.
@ -76,29 +59,6 @@ public class MainNavigator {
activity.startActivityForResult(AppSettingsActivity.home(activity), REQUEST_CONFIG_CHANGES);
}
public void goToArchiveList() {
getFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
.replace(R.id.fragment_container, ConversationListArchiveFragment.newInstance())
.addToBackStack(null)
.commit();
}
public void goToStories() {
if (getFragmentManager().findFragmentByTag(STORIES_TAG) == null) {
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new StoriesLandingFragment(), STORIES_TAG)
.addToBackStack(null)
.commit();
}
}
public void goToChats() {
if (getFragmentManager().findFragmentByTag(STORIES_TAG) != null) {
getFragmentManager().popBackStack();
}
}
public void goToGroupCreation() {
activity.startActivity(CreateGroupActivity.newIntent(activity));
}

Wyświetl plik

@ -29,6 +29,9 @@ import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.navigation.NavController;
import androidx.navigation.NavHostController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
@ -62,7 +65,7 @@ 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));
toolbar = requireCallback().getBasicToolbar();
super.onViewCreated(view, savedInstanceState);
@ -71,8 +74,7 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
cameraFab = view.findViewById(R.id.camera_fab);
emptyState = new Stub<>(view.findViewById(R.id.empty_state));
((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.get().setNavigationOnClickListener(v -> requireActivity().onBackPressed());
toolbar.get().setNavigationOnClickListener(v -> NavHostFragment.findNavController(this).popBackStack());
toolbar.get().setTitle(R.string.AndroidManifest_archived_conversations);
fab.hide();

Wyświetl plik

@ -44,6 +44,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
@ -63,6 +64,8 @@ import androidx.core.view.ViewCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavHostController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -187,7 +190,6 @@ import static org.thoughtcrime.securesms.components.TooltipPopup.POSITION_BELOW;
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;
@ -206,12 +208,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private TextView searchEmptyState;
private PulsingFloatingActionButton fab;
private PulsingFloatingActionButton cameraFab;
private Stub<SearchToolbar> searchToolbar;
private ImageView notificationProfileStatus;
private ImageView proxyStatus;
private ImageView searchAction;
private View toolbarShadow;
private View unreadPaymentsDot;
private ConversationListViewModel viewModel;
private RecyclerView.Adapter activeAdapter;
private ConversationListAdapter defaultAdapter;
@ -225,7 +222,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private Stub<FrameLayout> voiceNotePlayerViewStub;
private VoiceNotePlayerView voiceNotePlayerView;
private SignalBottomActionBar bottomActionBar;
private TopToastPopup previousTopToastPopup;
protected ConversationListArchiveItemDecoration archiveDecoration;
protected ConversationListItemAnimator itemAnimator;
@ -265,25 +261,16 @@ public class ConversationListFragment extends MainFragment implements ActionMode
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);
notificationProfileStatus = view.findViewById(R.id.conversation_list_notification_profile_status);
proxyStatus = view.findViewById(R.id.conversation_list_proxy_status);
unreadPaymentsDot = view.findViewById(R.id.unread_payments_indicator);
bottomActionBar = view.findViewById(R.id.conversation_list_bottom_action_bar);
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);
notificationProfileStatus.setOnClickListener(v -> handleNotificationProfile());
proxyStatus.setOnClickListener(v -> onProxyStatusClicked());
fab.show();
cameraFab.show();
@ -321,13 +308,18 @@ public class ConversationListFragment extends MainFragment implements ActionMode
RatingManager.showRatingDialogIfNecessary(requireContext());
TooltipCompat.setTooltipText(searchAction, getText(R.string.SearchToolbar_search_for_conversations_contacts_and_messages));
}
TooltipCompat.setTooltipText(requireCallback().getSearchAction(), getText(R.string.SearchToolbar_search_for_conversations_contacts_and_messages));
@Override
public void onDestroyView() {
previousTopToastPopup = null;
super.onDestroyView();
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (!closeSearchIfOpen()) {
if (!NavHostFragment.findNavController(ConversationListFragment.this).popBackStack()) {
requireActivity().finish();
}
}
}
});
}
@Override
@ -342,11 +334,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
InsightsLauncher.showInsightsModal(requireContext(), requireFragmentManager());
}
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), Recipient::self, this::initializeProfileIcon);
initializeSettingsTouchTarget();
if ((!searchToolbar.resolved() || !searchToolbar.get().isVisible()) && list.getAdapter() != defaultAdapter) {
if ((!requireCallback().getSearchToolbar().resolved() || !requireCallback().getSearchToolbar().get().isVisible()) && list.getAdapter() != defaultAdapter) {
list.removeItemDecoration(searchAdapterDecoration);
setAdapter(defaultAdapter);
}
@ -431,16 +419,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return false;
}
@Override
public boolean onBackPressed() {
return closeSearchIfOpen();
}
private boolean closeSearchIfOpen() {
if ((searchToolbar.resolved() && searchToolbar.get().isVisible()) || activeAdapter == searchAdapter) {
if ((requireCallback().getSearchToolbar().resolved() && requireCallback().getSearchToolbar().get().isVisible()) || activeAdapter == searchAdapter) {
list.removeItemDecoration(searchAdapterDecoration);
setAdapter(defaultAdapter);
searchToolbar.get().collapse();
requireCallback().getSearchToolbar().get().collapse();
requireCallback().onSearchClosed();
return true;
}
@ -475,7 +459,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onShowArchiveClick() {
getNavigator().goToArchiveList();
NavHostFragment.findNavController(this)
.navigate(ConversationListFragmentDirections.actionConversationListFragmentToConversationListArchiveFragment());
}
@Override
@ -558,26 +543,13 @@ public class ConversationListFragment extends MainFragment implements ActionMode
imm.hideSoftInputFromWindow(requireView().getWindowToken(), 0);
}
private void initializeProfileIcon(@NonNull Recipient recipient) {
ImageView icon = requireView().findViewById(R.id.toolbar_icon);
BadgeImageView imageView = requireView().findViewById(R.id.toolbar_badge);
imageView.setBadgeFromRecipient(recipient);
AvatarUtil.loadIconIntoImageView(recipient, icon, getResources().getDimensionPixelSize(R.dimen.toolbar_avatar_size));
}
private void initializeSettingsTouchTarget() {
View touchArea = requireView().findViewById(R.id.toolbar_settings_touch_area);
touchArea.setOnClickListener(v -> getNavigator().goToAppSettings());
}
private void initializeSearchListener() {
searchAction.setOnClickListener(v -> {
searchToolbar.get().display(searchAction.getX() + (searchAction.getWidth() / 2.0f),
searchAction.getY() + (searchAction.getHeight() / 2.0f));
requireCallback().getSearchAction().setOnClickListener(v -> {
requireCallback().onSearchOpened();
requireCallback().getSearchToolbar().get().display(requireCallback().getSearchAction().getX() + (requireCallback().getSearchAction().getWidth() / 2.0f),
requireCallback().getSearchAction().getY() + (requireCallback().getSearchAction().getHeight() / 2.0f));
searchToolbar.get().setListener(new SearchToolbar.SearchListener() {
requireCallback().getSearchToolbar().get().setListener(new SearchToolbar.SearchListener() {
@Override
public void onSearchTextChange(String text) {
String trimmed = text.trim();
@ -602,6 +574,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
public void onSearchClosed() {
list.removeItemDecoration(searchAdapterDecoration);
setAdapter(defaultAdapter);
requireCallback().onSearchClosed();
}
});
});
@ -697,8 +670,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
viewModel.getMegaphone().observe(getViewLifecycleOwner(), this::onMegaphoneChanged);
viewModel.getConversationList().observe(getViewLifecycleOwner(), this::onConversationListChanged);
viewModel.hasNoConversations().observe(getViewLifecycleOwner(), this::updateEmptyState);
viewModel.getNotificationProfiles().observe(getViewLifecycleOwner(), this::updateNotificationProfileStatus);
viewModel.getPipeState().observe(getViewLifecycleOwner(), this::updateProxyStatus);
viewModel.getNotificationProfiles().observe(getViewLifecycleOwner(), profiles -> requireCallback().updateNotificationProfileStatus(profiles));
viewModel.getPipeState().observe(getViewLifecycleOwner(), pipeState -> requireCallback().updateProxyStatus(pipeState));
appForegroundObserver = new AppForegroundObserver.Listener() {
@Override
@ -742,7 +715,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private void animatePaymentUnreadStatusIn() {
paymentNotificationView.get().setVisibility(View.VISIBLE);
unreadPaymentsDot.animate().alpha(1);
requireCallback().getUnreadPaymentsDot().animate().alpha(1);
}
private void animatePaymentUnreadStatusOut() {
@ -750,7 +723,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
paymentNotificationView.get().setVisibility(View.GONE);
}
unreadPaymentsDot.animate().alpha(0);
requireCallback().getUnreadPaymentsDot().animate().alpha(0);
}
private void onSearchResultChanged(@Nullable SearchResult result) {
@ -1094,90 +1067,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
}
}
private void updateNotificationProfileStatus(@NonNull List<NotificationProfile> notificationProfiles) {
NotificationProfile activeProfile = NotificationProfiles.getActiveProfile(notificationProfiles);
if (activeProfile != null) {
if (activeProfile.getId() != SignalStore.notificationProfileValues().getLastProfilePopup()) {
requireView().postDelayed(() -> {
SignalStore.notificationProfileValues().setLastProfilePopup(activeProfile.getId());
SignalStore.notificationProfileValues().setLastProfilePopupTime(System.currentTimeMillis());
if (previousTopToastPopup != null && previousTopToastPopup.isShowing()) {
previousTopToastPopup.dismiss();
}
ViewGroup view = ((ViewGroup) requireView());
Fragment fragment = getParentFragmentManager().findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
if (fragment != null && fragment.isAdded() && fragment.getView() != null) {
view = ((ViewGroup) fragment.requireView());
}
try {
previousTopToastPopup = TopToastPopup.show(view, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.getName()));
} catch (Exception e) {
Log.w(TAG, "Unable to show toast popup", e);
}
}, 500L);
}
notificationProfileStatus.setVisibility(View.VISIBLE);
} else {
notificationProfileStatus.setVisibility(View.GONE);
}
if (!SignalStore.notificationProfileValues().getHasSeenTooltip() && Util.hasItems(notificationProfiles)) {
View target = findOverflowMenuButton(getToolbar(requireView()));
if (target != null) {
TooltipPopup.forTarget(target)
.setText(R.string.ConversationListFragment__turn_your_notification_profile_on_or_off_here)
.setBackgroundTint(ContextCompat.getColor(requireContext(), R.color.signal_button_primary))
.setTextColor(ContextCompat.getColor(requireContext(), R.color.signal_button_primary_text))
.setOnDismissListener(() -> SignalStore.notificationProfileValues().setHasSeenTooltip(true))
.show(POSITION_BELOW);
} else {
Log.w(TAG, "Unable to find overflow menu to show Notification Profile tooltip");
}
}
}
private @Nullable View findOverflowMenuButton(@NonNull Toolbar viewGroup) {
for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
View v = viewGroup.getChildAt(i);
if (v instanceof ActionMenuView) {
return v;
}
}
return null;
}
private void updateProxyStatus(@NonNull WebSocketConnectionState state) {
if (SignalStore.proxy().isProxyEnabled()) {
proxyStatus.setVisibility(View.VISIBLE);
switch (state) {
case CONNECTING:
case DISCONNECTING:
case DISCONNECTED:
proxyStatus.setImageResource(R.drawable.ic_proxy_connecting_24);
break;
case CONNECTED:
proxyStatus.setImageResource(R.drawable.ic_proxy_connected_24);
break;
case AUTHENTICATION_FAILED:
case FAILED:
proxyStatus.setImageResource(R.drawable.ic_proxy_failed_24);
break;
}
} else {
proxyStatus.setVisibility(View.GONE);
}
}
private void onProxyStatusClicked() {
startActivity(AppSettingsActivity.proxy(requireContext()));
}
protected void onPostSubmitList(int conversationCount) {
if (conversationCount >= 6 && (SignalStore.onboarding().shouldShowInviteFriends() || SignalStore.onboarding().shouldShowNewGroup())) {
SignalStore.onboarding().clearAll();
@ -1361,8 +1250,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode
bottomActionBar.setItems(items);
}
protected Callback requireCallback() {
return ((Callback) getParentFragment().getParentFragment());
}
protected Toolbar getToolbar(@NonNull View rootView) {
return rootView.findViewById(R.id.toolbar);
return requireCallback().getToolbar();
}
protected @PluralsRes int getArchivedSnackbarTitleRes() {
@ -1668,6 +1561,19 @@ public class ConversationListFragment extends MainFragment implements ActionMode
MainNavigator.get(requireActivity()).goToConversation(threadRecipientId, threadId, ThreadDatabase.DistributionTypes.DEFAULT, (int) messagePositionInThread);
}
}
public interface Callback {
@NonNull Toolbar getToolbar();
@NonNull ImageView getSearchAction();
@NonNull Stub<SearchToolbar> getSearchToolbar();
@NonNull View getUnreadPaymentsDot();
@NonNull Stub<Toolbar> getBasicToolbar();
void updateNotificationProfileStatus(@NonNull List<NotificationProfile> notificationProfiles);
void updateProxyStatus(@NonNull WebSocketConnectionState state);
void onSearchOpened();
void onSearchClosed();
}
}

Wyświetl plik

@ -0,0 +1,280 @@
package org.thoughtcrime.securesms.main
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.ActionMenuView
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.SearchToolbar
import org.thoughtcrime.securesms.components.TooltipPopup
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfiles
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.stories.tabs.ConversationListTab
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsState
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel
import org.thoughtcrime.securesms.util.AvatarUtil
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.TopToastPopup
import org.thoughtcrime.securesms.util.TopToastPopup.Companion.show
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.concurrent.SimpleTask
import org.thoughtcrime.securesms.util.views.Stub
import org.thoughtcrime.securesms.util.visible
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_fragment), ConversationListFragment.Callback {
companion object {
private val TAG = Log.tag(MainActivityListHostFragment::class.java)
}
private val conversationListTabsViewModel: ConversationListTabsViewModel by viewModels(ownerProducer = { requireActivity() })
private lateinit var _toolbar: Toolbar
private lateinit var _basicToolbar: Stub<Toolbar>
private lateinit var notificationProfileStatus: ImageView
private lateinit var proxyStatus: ImageView
private lateinit var _searchToolbar: Stub<SearchToolbar>
private lateinit var _searchAction: ImageView
private lateinit var _unreadPaymentsDot: View
private var previousTopToastPopup: TopToastPopup? = null
private val destinationChangedListener = DestinationChangedListener()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
_toolbar = view.findViewById(R.id.toolbar)
_basicToolbar = Stub(view.findViewById(R.id.toolbar_basic))
notificationProfileStatus = view.findViewById(R.id.conversation_list_notification_profile_status)
proxyStatus = view.findViewById(R.id.conversation_list_proxy_status)
_searchAction = view.findViewById(R.id.search_action)
_searchToolbar = Stub(view.findViewById(R.id.search_toolbar))
_unreadPaymentsDot = view.findViewById(R.id.unread_payments_indicator)
notificationProfileStatus.setOnClickListener { handleNotificationProfile() }
proxyStatus.setOnClickListener { onProxyStatusClicked() }
initializeSettingsTouchTarget()
(requireActivity() as AppCompatActivity).setSupportActionBar(_toolbar)
conversationListTabsViewModel.state.observe(viewLifecycleOwner) { state ->
val controller: NavController = requireView().findViewById<View>(R.id.fragment_container).findNavController()
when (controller.currentDestination?.id) {
R.id.conversationListFragment -> goToStateFromConversationList(state, controller)
R.id.conversationListArchiveFragment -> goToStateFromConversationArchiveList(state, controller)
R.id.storiesLandingFragment -> goToStateFromStories(state, controller)
}
}
}
private fun goToStateFromConversationArchiveList(state: ConversationListTabsState, navController: NavController) {
if (state.tab == ConversationListTab.CHATS) {
return
} else {
navController.navigate(R.id.action_conversationListArchiveFragment_to_storiesLandingFragment)
}
}
private fun goToStateFromConversationList(state: ConversationListTabsState, navController: NavController) {
if (state.tab == ConversationListTab.CHATS) {
return
} else {
navController.navigate(R.id.action_conversationListFragment_to_storiesLandingFragment)
}
}
private fun goToStateFromStories(state: ConversationListTabsState, navController: NavController) {
if (state.tab == ConversationListTab.STORIES) {
return
} else {
navController.popBackStack()
}
}
override fun onResume() {
super.onResume()
SimpleTask.run(viewLifecycleOwner.lifecycle, { Recipient.self() }, ::initializeProfileIcon)
requireView()
.findViewById<View>(R.id.fragment_container)
.findNavController()
.addOnDestinationChangedListener(destinationChangedListener)
}
override fun onPause() {
super.onPause()
requireView()
.findViewById<View>(R.id.fragment_container)
.findNavController()
.removeOnDestinationChangedListener(destinationChangedListener)
}
private fun presentToolbarForConversationListFragment() {
_toolbar.visible = true
_searchAction.visible = true
if (_basicToolbar.resolved()) {
_basicToolbar.get().visible = false
}
}
private fun presentToolbarForConversationListArchiveFragment() {
_toolbar.visible = false
_basicToolbar.get().visible = true
}
private fun presentToolbarForStoriesLandingFragment() {
_toolbar.visible = true
_searchAction.visible = false
if (_basicToolbar.resolved()) {
_basicToolbar.get().visible = false
}
}
override fun onDestroyView() {
previousTopToastPopup = null
super.onDestroyView()
}
override fun getToolbar(): Toolbar {
return _toolbar
}
override fun getSearchAction(): ImageView {
return _searchAction
}
override fun getSearchToolbar(): Stub<SearchToolbar> {
return _searchToolbar
}
override fun getUnreadPaymentsDot(): View {
return _unreadPaymentsDot
}
override fun getBasicToolbar(): Stub<Toolbar> {
return _basicToolbar
}
override fun onSearchOpened() {
conversationListTabsViewModel.onSearchOpened()
}
override fun onSearchClosed() {
conversationListTabsViewModel.onSearchClosed()
}
private fun initializeProfileIcon(recipient: Recipient) {
Log.d(TAG, "Initializing profile icon")
val icon = requireView().findViewById<ImageView>(R.id.toolbar_icon)
val imageView: BadgeImageView = requireView().findViewById(R.id.toolbar_badge)
imageView.setBadgeFromRecipient(recipient)
AvatarUtil.loadIconIntoImageView(recipient, icon, resources.getDimensionPixelSize(R.dimen.toolbar_avatar_size))
}
private fun initializeSettingsTouchTarget() {
val touchArea = requireView().findViewById<View>(R.id.toolbar_settings_touch_area)
touchArea.setOnClickListener { startActivity(AppSettingsActivity.home(requireContext())) }
}
private fun handleNotificationProfile() {
NotificationProfileSelectionFragment.show(parentFragmentManager)
}
private fun onProxyStatusClicked() {
startActivity(AppSettingsActivity.proxy(requireContext()))
}
override fun updateProxyStatus(state: WebSocketConnectionState) {
if (SignalStore.proxy().isProxyEnabled) {
proxyStatus.visibility = View.VISIBLE
when (state) {
WebSocketConnectionState.CONNECTING, WebSocketConnectionState.DISCONNECTING, WebSocketConnectionState.DISCONNECTED -> proxyStatus.setImageResource(R.drawable.ic_proxy_connecting_24)
WebSocketConnectionState.CONNECTED -> proxyStatus.setImageResource(R.drawable.ic_proxy_connected_24)
WebSocketConnectionState.AUTHENTICATION_FAILED, WebSocketConnectionState.FAILED -> proxyStatus.setImageResource(R.drawable.ic_proxy_failed_24)
else -> proxyStatus.visibility = View.GONE
}
} else {
proxyStatus.visibility = View.GONE
}
}
override fun updateNotificationProfileStatus(notificationProfiles: List<NotificationProfile>) {
val activeProfile = NotificationProfiles.getActiveProfile(notificationProfiles)
if (activeProfile != null) {
if (activeProfile.id != SignalStore.notificationProfileValues().lastProfilePopup) {
requireView().postDelayed({
SignalStore.notificationProfileValues().lastProfilePopup = activeProfile.id
SignalStore.notificationProfileValues().lastProfilePopupTime = System.currentTimeMillis()
if (previousTopToastPopup?.isShowing == true) {
previousTopToastPopup?.dismiss()
}
var view = requireView() as ViewGroup
val fragment = parentFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
if (fragment != null && fragment.isAdded && fragment.view != null) {
view = fragment.requireView() as ViewGroup
}
try {
previousTopToastPopup = show(view, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.name))
} catch (e: Exception) {
Log.w(TAG, "Unable to show toast popup", e)
}
}, 500L)
}
notificationProfileStatus.visibility = View.VISIBLE
} else {
notificationProfileStatus.visibility = View.GONE
}
if (!SignalStore.notificationProfileValues().hasSeenTooltip && Util.hasItems(notificationProfiles)) {
val target: View? = findOverflowMenuButton(_toolbar)
if (target != null) {
TooltipPopup.forTarget(target)
.setText(R.string.ConversationListFragment__turn_your_notification_profile_on_or_off_here)
.setBackgroundTint(ContextCompat.getColor(requireContext(), R.color.signal_button_primary))
.setTextColor(ContextCompat.getColor(requireContext(), R.color.signal_button_primary_text))
.setOnDismissListener { SignalStore.notificationProfileValues().hasSeenTooltip = true }
.show(TooltipPopup.POSITION_BELOW)
} else {
Log.w(TAG, "Unable to find overflow menu to show Notification Profile tooltip")
}
}
}
private fun findOverflowMenuButton(viewGroup: Toolbar): View? {
return viewGroup.children.find { it is ActionMenuView }
}
private inner class DestinationChangedListener : NavController.OnDestinationChangedListener {
override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
when (destination.id) {
R.id.conversationListFragment -> {
presentToolbarForConversationListFragment()
}
R.id.conversationListArchiveFragment -> {
presentToolbarForConversationListArchiveFragment()
}
R.id.storiesLandingFragment -> {
presentToolbarForStoriesLandingFragment()
}
}
}
}
}

Wyświetl plik

@ -3,14 +3,17 @@ package org.thoughtcrime.securesms.stories.landing
import android.Manifest
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.viewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import org.thoughtcrime.securesms.MainNavigator
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
@ -34,13 +37,7 @@ import org.thoughtcrime.securesms.util.visible
/**
* The "landing page" for Stories.
*/
class StoriesLandingFragment :
DSLSettingsFragment(
layoutId = R.layout.stories_landing_fragment,
menuId = R.menu.story_landing_menu,
titleId = R.string.ConversationListTabs__stories
),
MainNavigator.BackHandler {
class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_landing_fragment) {
private lateinit var emptyNotice: View
private lateinit var cameraFab: View
@ -55,6 +52,16 @@ class StoriesLandingFragment :
private val tabsViewModel: ConversationListTabsViewModel by viewModels(ownerProducer = { requireActivity() })
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.clear()
inflater.inflate(R.menu.story_landing_menu, menu)
}
override fun bindAdapter(adapter: DSLSettingsAdapter) {
StoriesLandingItem.register(adapter)
MyStoriesItem.register(adapter)
@ -79,6 +86,15 @@ class StoriesLandingFragment :
adapter.submitList(getConfiguration(it).toMappingModelList())
emptyNotice.visible = it.hasNoStories
}
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
tabsViewModel.onChatsSelected()
}
}
)
}
private fun getConfiguration(state: StoriesLandingState): DSLConfiguration {
@ -182,11 +198,6 @@ class StoriesLandingFragment :
.show()
}
override fun onBackPressed(): Boolean {
tabsViewModel.onChatsSelected()
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.action_settings) {
startActivity(StorySettingsActivity.getIntent(requireContext()))

Wyświetl plik

@ -47,6 +47,8 @@ class ConversationListTabsFragment : Fragment(R.layout.conversation_list_tabs) {
storiesUnreadIndicator.visible = state.unreadStoriesCount > 0
storiesUnreadIndicator.text = formatCount(state.unreadStoriesCount)
requireView().visible = !state.isSearchOpen
}
private fun formatCount(count: Long): String {

Wyświetl plik

@ -3,5 +3,6 @@ package org.thoughtcrime.securesms.stories.tabs
data class ConversationListTabsState(
val tab: ConversationListTab = ConversationListTab.CHATS,
val unreadChatsCount: Long = 0L,
val unreadStoriesCount: Long = 0L
val unreadStoriesCount: Long = 0L,
val isSearchOpen: Boolean = false
)

Wyświetl plik

@ -35,6 +35,14 @@ class ConversationListTabsViewModel(repository: ConversationListTabRepository) :
store.update { it.copy(tab = ConversationListTab.STORIES) }
}
fun onSearchOpened() {
store.update { it.copy(isSearchOpen = true) }
}
fun onSearchClosed() {
store.update { it.copy(isSearchOpen = false) }
}
class Factory(private val repository: ConversationListTabRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return modelClass.cast(ConversationListTabsViewModel(repository)) as T

Wyświetl plik

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar_basic"
android:layout_width="match_parent"
@ -10,4 +9,5 @@
android:theme="?attr/actionBarStyle"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_arrow_left_24"
app:titleTextAppearance="@style/TextSecure.TitleTextStyle" />

Wyświetl plik

@ -14,160 +14,18 @@
android:layout_height="wrap_content"
android:inflatedId="@+id/voice_note_player"
android:layout="@layout/voice_note_player_stub"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/signal_background_primary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarStyle"
android:visibility="gone"
app:contentInsetStart="0dp"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/toolbar_icon"
android:layout_width="@dimen/toolbar_avatar_size"
android:layout_height="@dimen/toolbar_avatar_size"
android:layout_alignParentStart="true"
android:layout_marginStart="@dimen/toolbar_avatar_margin"
android:contentDescription="@string/conversation_list_settings_shortcut"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_contact_picture" />
<org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/toolbar_badge"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="14dp"
android:layout_marginTop="16dp"
app:badge_size="small"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="@id/toolbar_icon" />
<View
android:id="@+id/toolbar_settings_touch_area"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/unread_payments_indicator"
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:alpha="0"
android:background="@drawable/unread_count_background"
app:layout_constraintBottom_toBottomOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
tools:alpha="1" />
<TextView
android:id="@+id/conversation_list_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:text="@string/app_name"
android:textAlignment="viewStart"
android:textColor="@color/signal_text_primary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_notification_profile_status"
app:layout_constraintStart_toEndOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/conversation_list_notification_profile_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:paddingHorizontal="3dp"
android:paddingVertical="11dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_proxy_status"
app:layout_constraintStart_toEndOf="@id/conversation_list_title"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_notification_profile_active"
tools:visibility="visible" />
<ImageView
android:id="@+id/conversation_list_proxy_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:padding="12dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/search_action"
app:layout_constraintStart_toEndOf="@id/conversation_list_notification_profile_status"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_proxy_connected_24"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/search_action"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?actionBarItemBackground"
android:contentDescription="@string/conversation_list_search_description"
android:padding="12dp"
android:tint="@color/signal_icon_tint_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_search_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.thoughtcrime.securesms.util.views.DarkOverflowToolbar>
<ViewStub
android:id="@+id/toolbar_basic"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout="@layout/conversation_list_archive_toolbar"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/search_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_search_toolbar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/toolbar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="toolbar,toolbar_basic" />
<View
android:id="@+id/conversation_list_toolbar_shadow"
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@drawable/toolbar_shadow"
android:visibility="gone"
android:elevation="100dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
@ -180,7 +38,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/SearchFragment_no_results" />
<ViewStub
@ -191,7 +49,7 @@
android:layout="@layout/conversation_list_empty_state"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/reminder"
@ -199,7 +57,7 @@
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_list_reminder_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/payments_notification"
@ -207,7 +65,7 @@
android:layout_height="wrap_content"
android:inflatedId="@+id/payments_notification"
android:layout="@layout/payment_notification_view"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/banner_barrier"

Wyświetl plik

@ -5,7 +5,8 @@
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
<androidx.fragment.app.FragmentContainerView
android:name="org.thoughtcrime.securesms.main.MainActivityListHostFragment"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"

Wyświetl plik

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/signal_background_primary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarStyle"
android:visibility="gone"
app:contentInsetStart="0dp"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/toolbar_icon"
android:layout_width="@dimen/toolbar_avatar_size"
android:layout_height="@dimen/toolbar_avatar_size"
android:layout_alignParentStart="true"
android:layout_marginStart="@dimen/toolbar_avatar_margin"
android:contentDescription="@string/conversation_list_settings_shortcut"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_contact_picture" />
<org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/toolbar_badge"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="14dp"
android:layout_marginTop="16dp"
app:badge_size="small"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="@id/toolbar_icon" />
<View
android:id="@+id/toolbar_settings_touch_area"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/unread_payments_indicator"
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:alpha="0"
android:background="@drawable/unread_count_background"
app:layout_constraintBottom_toBottomOf="@id/toolbar_icon"
app:layout_constraintStart_toStartOf="@id/toolbar_icon"
tools:alpha="1" />
<TextView
android:id="@+id/conversation_list_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:text="@string/app_name"
android:textAlignment="viewStart"
android:textColor="@color/signal_text_primary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_notification_profile_status"
app:layout_constraintStart_toEndOf="@id/toolbar_icon"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/conversation_list_notification_profile_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:paddingHorizontal="3dp"
android:paddingVertical="11dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/conversation_list_proxy_status"
app:layout_constraintStart_toEndOf="@id/conversation_list_title"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_notification_profile_active"
tools:visibility="visible" />
<ImageView
android:id="@+id/conversation_list_proxy_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:padding="12dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/search_action"
app:layout_constraintStart_toEndOf="@id/conversation_list_notification_profile_status"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_proxy_connected_24"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/search_action"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?actionBarItemBackground"
android:contentDescription="@string/conversation_list_search_description"
android:padding="12dp"
android:tint="@color/signal_icon_tint_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_search_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</org.thoughtcrime.securesms.util.views.DarkOverflowToolbar>
<ViewStub
android:id="@+id/toolbar_basic"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout="@layout/conversation_list_archive_toolbar"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/search_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/conversation_list_search_toolbar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/toolbar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="toolbar,toolbar_basic" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier"
app:navGraph="@navigation/main_activity_list" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -4,16 +4,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.util.views.DarkOverflowToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentInsetStart="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/ConversationListTabs__stories" />
<View
android:id="@+id/toolbar_shadow"
android:layout_width="match_parent"
@ -22,7 +12,7 @@
android:background="@drawable/toolbar_shadow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
@ -31,7 +21,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
app:layout_constraintTop_toTopOf="parent" />
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/camera_fab"

Wyświetl plik

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_activity_list"
app:startDestination="@id/conversationListFragment">
<fragment
android:id="@+id/conversationListFragment"
android:name="org.thoughtcrime.securesms.conversationlist.ConversationListFragment"
android:label="conversation_list_fragment" >
<action
android:id="@+id/action_conversationListFragment_to_conversationListArchiveFragment"
app:destination="@id/conversationListArchiveFragment" />
<action
android:id="@+id/action_conversationListFragment_to_storiesLandingFragment"
app:destination="@id/storiesLandingFragment" />
</fragment>
<fragment
android:id="@+id/conversationListArchiveFragment"
android:name="org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment"
android:label="conversation_list_archive_fragment" >
<action
android:id="@+id/action_conversationListArchiveFragment_to_storiesLandingFragment"
app:destination="@id/storiesLandingFragment" />
</fragment>
<fragment
android:id="@+id/storiesLandingFragment"
android:name="org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment"
android:label="stories_landing_fragment" />
</navigation>