From 4663299951de3520c051b31aeea11edee3006322 Mon Sep 17 00:00:00 2001 From: bjoern Date: Sat, 5 Jul 2025 11:16:30 +0200 Subject: [PATCH] re-focus profile (#3792) * duplicate ProfileActivity to AllMediaActivity * update CHANGELOG * remove profile stuff from AllMediaActivity * remove media stuff from ProfileActivity * remove TabLayout from ProfileActivity * decouple header from viewType * easier name editing * add link to 'apps & media' * move bio up * move 'send message' up * prepare avatar/title/subtitle * set title * set subtitle to member count * add address to profile * rename ProfileSettings* to just Profile* * set avatar * use avatar view * adaptive avatar cell height * no endless growing of online-indicator * simplify * handle tap on avatars * rename Profile* to AllMedia* * set title accordingly * move 'last seen' up * edit name by tapping * Revert "edit name by tapping" This reverts commit 6727b168302fbe50cbde803fbc83692965d3df59. Reason is that it introduces uncertainity what happens if the name is tapped - we do not want to nudge ppl to edit the group name in a similar way. we may revert this revert, but for now, let's see if the icon atop isn't sufficient. * add media count * space below avatar * refactor allmedia viewtypes * select the first tab that has content * format footer * remove unused headers * space above header * add dividers * tweak some spacings * tap on avatar only for enlarge/set avatar * immediate view of first tap * tweak value display * add icons to buttons * tweak paddings * no address for self-talk and device-chat * use signature background for less cluttered UI * avoid global state modification and showing eg. app-icon tinted also elsewhere * tweak sizes * move introduced-by/server down. these information become less important the more chats you have with the contact - and otherwise just clutter UI * update CHANGELOG * typo * use more normal font and spacing for footer * open "media" deterministically remove the smart forwarding to "tab with content", which results in unclear behaviour. also, we want to push for apps, which is also the thing that really changes. when searching for an image, another tap is fine. --- CHANGELOG.md | 2 + src/main/AndroidManifest.xml | 4 + .../securesms/AllMediaActivity.java | 196 +++++++++ ...ter.java => AllMediaDocumentsAdapter.java} | 8 +- ...nt.java => AllMediaDocumentsFragment.java} | 48 +-- ...apter.java => AllMediaGalleryAdapter.java} | 10 +- ...ment.java => AllMediaGalleryFragment.java} | 20 +- .../securesms/ConversationActivity.java | 19 +- .../securesms/ConversationListActivity.java | 2 +- .../securesms/MediaPreviewActivity.java | 12 +- .../securesms/ProfileActivity.java | 255 ++---------- .../securesms/ProfileAdapter.java | 372 ++++++++++++++++++ .../securesms/ProfileAvatarItem.java | 112 ++++++ ...ingsFragment.java => ProfileFragment.java} | 44 +-- .../securesms/ProfileSettingsAdapter.java | 352 ----------------- .../securesms/ProfileSettingsItem.java | 41 -- .../securesms/ProfileTextItem.java | 57 +++ .../contacts/ContactSelectionListItem.java | 5 - src/main/res/layout/all_media_activity.xml | 46 +++ src/main/res/layout/avatar_view.xml | 2 + .../layout/contact_selection_list_item.xml | 4 +- src/main/res/layout/profile_activity.xml | 20 +- src/main/res/layout/profile_avatar_item.xml | 57 +++ src/main/res/layout/profile_divider.xml | 14 + ...ings_fragment.xml => profile_fragment.xml} | 0 src/main/res/layout/profile_settings_item.xml | 17 - src/main/res/layout/profile_status_item.xml | 7 +- src/main/res/layout/profile_text_item.xml | 19 + .../res/layout/profile_text_item_button.xml | 34 ++ .../res/layout/profile_text_item_small.xml | 22 ++ src/main/res/menu/conversation.xml | 6 +- src/main/res/menu/profile_common.xml | 3 +- 32 files changed, 1058 insertions(+), 752 deletions(-) create mode 100644 src/main/java/org/thoughtcrime/securesms/AllMediaActivity.java rename src/main/java/org/thoughtcrime/securesms/{ProfileDocumentsAdapter.java => AllMediaDocumentsAdapter.java} (96%) rename src/main/java/org/thoughtcrime/securesms/{ProfileDocumentsFragment.java => AllMediaDocumentsFragment.java} (84%) rename src/main/java/org/thoughtcrime/securesms/{ProfileGalleryAdapter.java => AllMediaGalleryAdapter.java} (93%) rename src/main/java/org/thoughtcrime/securesms/{ProfileGalleryFragment.java => AllMediaGalleryFragment.java} (92%) create mode 100644 src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java create mode 100644 src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java rename src/main/java/org/thoughtcrime/securesms/{ProfileSettingsFragment.java => ProfileFragment.java} (91%) delete mode 100644 src/main/java/org/thoughtcrime/securesms/ProfileSettingsAdapter.java delete mode 100644 src/main/java/org/thoughtcrime/securesms/ProfileSettingsItem.java create mode 100644 src/main/java/org/thoughtcrime/securesms/ProfileTextItem.java create mode 100644 src/main/res/layout/all_media_activity.xml create mode 100644 src/main/res/layout/profile_avatar_item.xml create mode 100644 src/main/res/layout/profile_divider.xml rename src/main/res/layout/{profile_settings_fragment.xml => profile_fragment.xml} (100%) delete mode 100644 src/main/res/layout/profile_settings_item.xml create mode 100644 src/main/res/layout/profile_text_item.xml create mode 100644 src/main/res/layout/profile_text_item_button.xml create mode 100644 src/main/res/layout/profile_text_item_small.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index abc858022..cc57f3942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Profiles focus on recognizing contacts +* See the number of media directly in the profile, no need to tap around * Clearer app lists by removing redundant "App" subtitle * New button for quick access to the apps sent in current chat * New icon for the in-chat apps button diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2473b9b9c..0573786d7 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -288,6 +288,10 @@ android:theme="@style/TextSecure.LightNoActionBar" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> + + tabs = new ArrayList<>(); + private Toolbar toolbar; + private TabLayout tabLayout; + private ViewPager viewPager; + + @Override + protected void onPreCreate() { + dynamicTheme = new DynamicNoActionBarTheme(); + super.onPreCreate(); + dcContext = DcHelper.getContext(this); + } + + @Override + protected void onCreate(Bundle bundle, boolean ready) { + tabs.add(new TabData(R.string.webxdc_apps, DcMsg.DC_MSG_WEBXDC, 0, 0)); + tabs.add(new TabData(R.string.tab_gallery, DcMsg.DC_MSG_IMAGE, DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_VIDEO)); + tabs.add(new TabData(R.string.files, DcMsg.DC_MSG_FILE, 0, 0)); + tabs.add(new TabData(R.string.audio, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0)); + + setContentView(R.layout.all_media_activity); + + initializeResources(); + + setSupportActionBar(this.toolbar); + ActionBar supportActionBar = getSupportActionBar(); + if (supportActionBar != null) { + supportActionBar.setDisplayHomeAsUpEnabled(true); + supportActionBar.setTitle(isGlobalGallery() ? R.string.menu_all_media : R.string.apps_and_media); + } + + this.tabLayout.setupWithViewPager(viewPager); + this.viewPager.setAdapter(new AllMediaPagerAdapter(getSupportFragmentManager())); + if (getIntent().getBooleanExtra(FORCE_GALLERY, false)) { + this.viewPager.setCurrentItem(1, false); + } + + DcEventCenter eventCenter = DcHelper.getEventCenter(this); + eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this); + eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this); + } + + @Override + public void onDestroy() { + DcHelper.getEventCenter(this).removeObservers(this); + super.onDestroy(); + } + + @Override + public void handleEvent(@NonNull DcEvent event) { + } + + private void initializeResources() { + chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, 0); + contactId = getIntent().getIntExtra(CONTACT_ID_EXTRA, 0); + + if (contactId!=0) { + chatId = dcContext.getChatIdByContactId(contactId); + } + + if(chatId!=0) { + DcChat dcChat = dcContext.getChat(chatId); + if(!dcChat.isMultiUser()) { + final int[] members = dcContext.getChatContacts(chatId); + contactId = members.length>=1? members[0] : 0; + } + } + + this.viewPager = ViewUtil.findById(this, R.id.pager); + this.toolbar = ViewUtil.findById(this, R.id.toolbar); + this.tabLayout = ViewUtil.findById(this, R.id.tab_layout); + } + + private boolean isGlobalGallery() { + return contactId==0 && chatId==0; + } + + private class AllMediaPagerAdapter extends FragmentStatePagerAdapter { + private Object currentFragment = null; + + AllMediaPagerAdapter(FragmentManager fragmentManager) { + super(fragmentManager); + } + + @Override + public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + super.setPrimaryItem(container, position, object); + if (currentFragment != null && currentFragment != object) { + ActionMode action = null; + if (currentFragment instanceof MessageSelectorFragment) { + action = ((MessageSelectorFragment) currentFragment).getActionMode(); + } + if (action != null) { + action.finish(); + } + } + currentFragment = object; + } + + @NonNull + @Override + public Fragment getItem(int position) { + TabData data = tabs.get(position); + Fragment fragment; + Bundle args = new Bundle(); + + if (data.type1 == DcMsg.DC_MSG_IMAGE) { + fragment = new AllMediaGalleryFragment(); + args.putInt(AllMediaGalleryFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalGallery())? -1 : chatId); + } else { + fragment = new AllMediaDocumentsFragment(); + args.putInt(AllMediaDocumentsFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalGallery())? -1 : chatId); + args.putInt(AllMediaDocumentsFragment.VIEWTYPE1, data.type1); + args.putInt(AllMediaDocumentsFragment.VIEWTYPE2, data.type2); + } + fragment.setArguments(args); + return fragment; + } + + @Override + public int getCount() { + return tabs.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return getString(tabs.get(position).title); + } + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + super.onOptionsItemSelected(item); + + int itemId = item.getItemId(); + if (itemId == android.R.id.home) { + finish(); + return true; + } + + return false; + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsAdapter.java similarity index 96% rename from src/main/java/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java rename to src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsAdapter.java index 424547412..c582c7288 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsAdapter.java @@ -25,7 +25,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -class ProfileDocumentsAdapter extends StickyHeaderGridAdapter { +class AllMediaDocumentsAdapter extends StickyHeaderGridAdapter { private final Context context; private final ItemClickListener itemClickListener; @@ -57,9 +57,9 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter { } } - ProfileDocumentsAdapter(@NonNull Context context, - BucketedThreadMedia media, - ItemClickListener clickListener) + AllMediaDocumentsAdapter(@NonNull Context context, + BucketedThreadMedia media, + ItemClickListener clickListener) { this.context = context; this.media = media; diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileDocumentsFragment.java b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java similarity index 84% rename from src/main/java/org/thoughtcrime/securesms/ProfileDocumentsFragment.java rename to src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java index 1f2e2688e..32b8acdcd 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileDocumentsFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java @@ -33,21 +33,21 @@ import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Set; -public class ProfileDocumentsFragment +public class AllMediaDocumentsFragment extends MessageSelectorFragment implements LoaderManager.LoaderCallbacks, - ProfileDocumentsAdapter.ItemClickListener + AllMediaDocumentsAdapter.ItemClickListener { public static final String CHAT_ID_EXTRA = "chat_id"; - public static final String SHOW_AUDIO_EXTRA = "show_audio"; - public static final String SHOW_WEBXDC_EXTRA = "show_webxdc"; + public static final String VIEWTYPE1 = "viewtype1"; + public static final String VIEWTYPE2 = "viewtype2"; protected TextView noMedia; protected RecyclerView recyclerView; private StickyHeaderGridLayoutManager gridManager; private final ActionModeCallback actionModeCallback = new ActionModeCallback(); - private boolean showAudio; - private boolean showWebxdc; + private int viewtype1; + private int viewtype2; protected int chatId; @@ -57,8 +57,8 @@ public class ProfileDocumentsFragment dcContext = DcHelper.getContext(getContext()); chatId = getArguments().getInt(CHAT_ID_EXTRA, -1); - showAudio = getArguments().getBoolean(SHOW_AUDIO_EXTRA, false); - showWebxdc = getArguments().getBoolean(SHOW_WEBXDC_EXTRA, false); + viewtype1 = getArguments().getInt(VIEWTYPE1, 0); + viewtype2 = getArguments().getInt(VIEWTYPE2, 0); getLoaderManager().initLoader(0, null, this); } @@ -71,7 +71,7 @@ public class ProfileDocumentsFragment this.noMedia = ViewUtil.findById(view, R.id.no_documents); this.gridManager = new StickyHeaderGridLayoutManager(1); - this.recyclerView.setAdapter(new ProfileDocumentsAdapter(getContext(), + this.recyclerView.setAdapter(new AllMediaDocumentsAdapter(getContext(), new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()), this)); this.recyclerView.setLayoutManager(gridManager); @@ -105,32 +105,26 @@ public class ProfileDocumentsFragment @Override public Loader onCreateLoader(int i, Bundle bundle) { - if (showAudio) { - return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0); - } else if (showWebxdc) { - return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_WEBXDC, 0, 0); - } else { - return new BucketedThreadMediaLoader(getContext(), chatId, DcMsg.DC_MSG_FILE, 0, 0); - } + return new BucketedThreadMediaLoader(getContext(), chatId, viewtype1, viewtype2, 0); } @Override public void onLoadFinished(Loader loader, BucketedThreadMediaLoader.BucketedThreadMedia bucketedThreadMedia) { - ((ProfileDocumentsAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia); - ((ProfileDocumentsAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged(); + ((AllMediaDocumentsAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia); + ((AllMediaDocumentsAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged(); noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE); if (chatId == DC_CHAT_NO_CHAT) { - if (showWebxdc) { + if (viewtype1 == DcMsg.DC_MSG_WEBXDC) { noMedia.setText(R.string.all_apps_empty_hint); - } else if (!showAudio){ + } else if (viewtype1 == DcMsg.DC_MSG_FILE){ noMedia.setText(R.string.all_files_empty_hint); } else { noMedia.setText(R.string.tab_all_media_empty_hint); } - } else if (showAudio) { + } else if (viewtype1 == DcMsg.DC_MSG_AUDIO) { noMedia.setText(R.string.tab_audio_empty_hint); - } else if (showWebxdc) { + } else if (viewtype1 == DcMsg.DC_MSG_WEBXDC) { noMedia.setText(R.string.tab_webxdc_empty_hint); } getActivity().invalidateOptionsMenu(); @@ -138,7 +132,7 @@ public class ProfileDocumentsFragment @Override public void onLoaderReset(Loader cursorLoader) { - ((ProfileDocumentsAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext())); + ((AllMediaDocumentsAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext())); } @Override @@ -156,7 +150,7 @@ public class ProfileDocumentsFragment } private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) { - ProfileDocumentsAdapter adapter = getListAdapter(); + AllMediaDocumentsAdapter adapter = getListAdapter(); adapter.toggleSelection(mediaRecord); if (adapter.getSelectedMediaCount() == 0) { @@ -188,7 +182,7 @@ public class ProfileDocumentsFragment @Override public void onMediaLongClicked(DcMsg mediaRecord) { if (actionMode == null) { - ((ProfileDocumentsAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord); + ((AllMediaDocumentsAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord); actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback); } @@ -221,8 +215,8 @@ public class ProfileDocumentsFragment menu.findItem(R.id.menu_add_to_home_screen).setVisible(webxdcApp); } - private ProfileDocumentsAdapter getListAdapter() { - return (ProfileDocumentsAdapter) recyclerView.getAdapter(); + private AllMediaDocumentsAdapter getListAdapter() { + return (AllMediaDocumentsAdapter) recyclerView.getAdapter(); } private class ActionModeCallback implements ActionMode.Callback { diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileGalleryAdapter.java b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryAdapter.java similarity index 93% rename from src/main/java/org/thoughtcrime/securesms/ProfileGalleryAdapter.java rename to src/main/java/org/thoughtcrime/securesms/AllMediaGalleryAdapter.java index 1efa30457..85d940625 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileGalleryAdapter.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryAdapter.java @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -class ProfileGalleryAdapter extends StickyHeaderGridAdapter { +class AllMediaGalleryAdapter extends StickyHeaderGridAdapter { private final Context context; private final GlideRequests glideRequests; @@ -50,10 +50,10 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter { } } - ProfileGalleryAdapter(@NonNull Context context, - @NonNull GlideRequests glideRequests, - BucketedThreadMedia media, - ItemClickListener clickListener) + AllMediaGalleryAdapter(@NonNull Context context, + @NonNull GlideRequests glideRequests, + BucketedThreadMedia media, + ItemClickListener clickListener) { this.context = context; this.glideRequests = glideRequests; diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileGalleryFragment.java b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java similarity index 92% rename from src/main/java/org/thoughtcrime/securesms/ProfileGalleryFragment.java rename to src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java index 97ad4471e..a3c742027 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileGalleryFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java @@ -35,10 +35,10 @@ import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Set; -public class ProfileGalleryFragment +public class AllMediaGalleryFragment extends MessageSelectorFragment implements LoaderManager.LoaderCallbacks, - ProfileGalleryAdapter.ItemClickListener + AllMediaGalleryAdapter.ItemClickListener { public static final String CHAT_ID_EXTRA = "chat_id"; @@ -67,7 +67,7 @@ public class ProfileGalleryFragment this.noMedia = ViewUtil.findById(view, R.id.no_images); this.gridManager = new StickyHeaderGridLayoutManager(getCols()); - this.recyclerView.setAdapter(new ProfileGalleryAdapter(getContext(), + this.recyclerView.setAdapter(new AllMediaGalleryAdapter(getContext(), GlideApp.with(this), new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()), this)); @@ -112,8 +112,8 @@ public class ProfileGalleryFragment @Override public void onLoadFinished(Loader loader, BucketedThreadMediaLoader.BucketedThreadMedia bucketedThreadMedia) { - ((ProfileGalleryAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia); - ((ProfileGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged(); + ((AllMediaGalleryAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia); + ((AllMediaGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged(); noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE); if (chatId == DC_CHAT_NO_CHAT) { @@ -124,7 +124,7 @@ public class ProfileGalleryFragment @Override public void onLoaderReset(Loader cursorLoader) { - ((ProfileGalleryAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext())); + ((AllMediaGalleryAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMediaLoader.BucketedThreadMedia(getContext())); } @Override @@ -142,7 +142,7 @@ public class ProfileGalleryFragment } private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) { - ProfileGalleryAdapter adapter = getListAdapter(); + AllMediaGalleryAdapter adapter = getListAdapter(); adapter.toggleSelection(mediaRecord); if (adapter.getSelectedMediaCount() == 0) { @@ -175,7 +175,7 @@ public class ProfileGalleryFragment @Override public void onMediaLongClicked(DcMsg mediaRecord) { if (actionMode == null) { - ((ProfileGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord); + ((AllMediaGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord); recyclerView.getAdapter().notifyDataSetChanged(); actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback); @@ -206,8 +206,8 @@ public class ProfileGalleryFragment menu.findItem(R.id.menu_resend).setVisible(canResend); } - private ProfileGalleryAdapter getListAdapter() { - return (ProfileGalleryAdapter) recyclerView.getAdapter(); + private AllMediaGalleryAdapter getListAdapter() { + return (AllMediaGalleryAdapter) recyclerView.getAdapter(); } private class ActionModeCallback implements ActionMode.Callback { diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index cdd31e0a8..fd09a9bbb 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -526,8 +526,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else if (itemId == R.id.menu_show_map) { WebxdcActivity.openMaps(this, chatId); return true; - } else if (itemId == R.id.menu_show_apps) { - handleProfile(true); + } else if (itemId == R.id.menu_all_media) { + handleAllMedia(); return true; } else if (itemId == R.id.menu_search_up) { handleMenuSearchNext(false); @@ -609,15 +609,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } - private void handleProfile(boolean showApps) { + private void handleProfile() { Intent intent = new Intent(this, ProfileActivity.class); intent.putExtra(ProfileActivity.CHAT_ID_EXTRA, chatId); - intent.putExtra(ProfileActivity.FROM_CHAT, true); - if (showApps) { - intent.putExtra(ProfileActivity.FORCE_TAB_EXTRA, ProfileActivity.TAB_WEBXDC); - } startActivity(intent); - overridePendingTransition(0, 0); + } + + private void handleAllMedia() { + Intent intent = new Intent(this, AllMediaActivity.class); + intent.putExtra(AllMediaActivity.CHAT_ID_EXTRA, chatId); + startActivity(intent); } private void handleLeaveGroup() { @@ -869,7 +870,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity buttonToggle.getBackground().invalidateSelf(); }); - titleView.setOnClickListener(v -> handleProfile(false)); + titleView.setOnClickListener(v -> handleProfile()); titleView.setOnBackClickedListener(view -> handleReturnToConversationList()); composeText.setOnKeyListener(composeKeyPressedListener); diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java index a659dca12..b0507d6cc 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java @@ -447,7 +447,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit onBackPressed(); return true; } else if (itemId == R.id.menu_all_media) { - startActivity(new Intent(this, ProfileActivity.class)); + startActivity(new Intent(this, AllMediaActivity.class)); return true; } diff --git a/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index b603d2eff..bcb848cae 100644 --- a/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -262,16 +262,16 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity finish(); } else if(conversationRecipient.getAddress().isDcChat()) { - Intent intent = new Intent(this, ProfileActivity.class); - intent.putExtra(ProfileActivity.CHAT_ID_EXTRA, conversationRecipient.getAddress().getDcChatId()); - intent.putExtra(ProfileActivity.FORCE_TAB_EXTRA, ProfileActivity.TAB_GALLERY); + Intent intent = new Intent(this, AllMediaActivity.class); + intent.putExtra(AllMediaActivity.CHAT_ID_EXTRA, conversationRecipient.getAddress().getDcChatId()); + intent.putExtra(AllMediaActivity.FORCE_GALLERY, true); startActivity(intent); finish(); } else if(conversationRecipient.getAddress().isDcContact()) { - Intent intent = new Intent(this, ProfileActivity.class); - intent.putExtra(ProfileActivity.CONTACT_ID_EXTRA, conversationRecipient.getAddress().getDcContactId()); - intent.putExtra(ProfileActivity.FORCE_TAB_EXTRA, ProfileActivity.TAB_GALLERY); + Intent intent = new Intent(this, AllMediaActivity.class); + intent.putExtra(AllMediaActivity.CONTACT_ID_EXTRA, conversationRecipient.getAddress().getDcContactId()); + intent.putExtra(AllMediaActivity.FORCE_GALLERY, true); startActivity(intent); finish(); } diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java b/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java index 50a3ddf11..78d637510 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileActivity.java @@ -11,19 +11,13 @@ import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.widget.EditText; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; import com.b44t.messenger.DcChat; import com.b44t.messenger.DcContact; @@ -31,11 +25,9 @@ import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEvent; import com.b44t.messenger.rpc.Rpc; import com.b44t.messenger.rpc.RpcException; -import com.google.android.material.tabs.TabLayout; import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; -import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.Prefs; import org.thoughtcrime.securesms.util.RelayUtil; @@ -43,7 +35,6 @@ import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import java.io.File; -import java.util.ArrayList; public class ProfileActivity extends PassphraseRequiredActionBarActivity implements DcEventCenter.DcEventDelegate @@ -51,16 +42,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity public static final String CHAT_ID_EXTRA = "chat_id"; public static final String CONTACT_ID_EXTRA = "contact_id"; - public static final String FORCE_TAB_EXTRA = "force_tab"; - public static final String FROM_CHAT = "from_chat"; - - public static final int TAB_SETTINGS = 10; - public static final int TAB_GALLERY = 20; - public static final int TAB_AUDIO = 25; - public static final int TAB_DOCS = 30; - public static final int TAB_WEBXDC = 35; - public static final int TAB_LINKS = 40; - public static final int TAB_MAP = 50; private static final int REQUEST_CODE_PICK_RINGTONE = 1; @@ -72,13 +53,8 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity private boolean chatIsMailingList; private boolean chatIsBroadcast; private int contactId; - private boolean fromChat; - - private final ArrayList tabs = new ArrayList<>(); - private Toolbar toolbar; - private ConversationTitleView titleView; - private TabLayout tabLayout; - private ViewPager viewPager; + private boolean contactIsBot; + private Toolbar toolbar; @Override protected void onPreCreate() { @@ -97,38 +73,27 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity setSupportActionBar(this.toolbar); ActionBar supportActionBar = getSupportActionBar(); if (supportActionBar != null) { - if (isGlobalProfile()) { - supportActionBar.setDisplayHomeAsUpEnabled(true); - supportActionBar.setHomeActionContentDescription(getString(R.string.back)); - } else { - supportActionBar.setDisplayHomeAsUpEnabled(false); - supportActionBar.setCustomView(R.layout.conversation_title_view); - supportActionBar.setDisplayShowCustomEnabled(true); - supportActionBar.setDisplayShowTitleEnabled(false); - Toolbar parent = (Toolbar) supportActionBar.getCustomView().getParent(); - parent.setPadding(0,0,0,0); - parent.setContentInsetsAbsolute(0,0); - - titleView = (ConversationTitleView) supportActionBar.getCustomView(); - titleView.setOnBackClickedListener(view -> onBackPressed()); - titleView.setOnClickListener(view -> onEnlargeAvatar()); - if (isContactProfile() && !isSelfProfile() && !chatIsDeviceTalk) { - titleView.registerForContextMenu(this); - } + String title = getString(R.string.profile); + if (chatIsMailingList) { + title = getString(R.string.mailing_list); + } else if (chatIsBroadcast) { + title = getString(R.string.broadcast_list); + } else if (chatIsMultiUser) { + title = getString(R.string.tab_group); + } else if (contactIsBot) { + title = getString(R.string.bot); + } else if (!chatIsDeviceTalk && !isSelfProfile()) { + title = getString(R.string.tab_contact); } + + supportActionBar.setDisplayHomeAsUpEnabled(true); + supportActionBar.setTitle(title); } - updateToolbar(); - - this.tabLayout.setupWithViewPager(viewPager); - this.viewPager.setAdapter(new ProfilePagerAdapter(getSupportFragmentManager())); - int forceTab = getIntent().getIntExtra(FORCE_TAB_EXTRA, -1); - if (forceTab != -1) { - int forceIndex = tabs.indexOf(forceTab); - if (forceIndex != -1) { - this.viewPager.setCurrentItem(forceIndex); - } - } + Bundle args = new Bundle(); + args.putInt(ProfileFragment.CHAT_ID_EXTRA, (chatId == 0) ? -1 : chatId); + args.putInt(ProfileFragment.CONTACT_ID_EXTRA, (contactId == 0) ? -1 : contactId); + initFragment(R.id.fragment_container, new ProfileFragment(), args); DcEventCenter eventCenter = DcHelper.getEventCenter(this); eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this); @@ -137,7 +102,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity @Override public boolean onCreateOptionsMenu(Menu menu) { - if (!isSelfProfile() && !isGlobalProfile()) { + if (!isSelfProfile()) { getMenuInflater().inflate(R.menu.profile_common, menu); boolean canReceive = true; @@ -148,6 +113,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity menu.findItem(R.id.show_encr_info).setVisible(false); menu.findItem(R.id.share).setVisible(false); } else if (chatIsMultiUser) { + menu.findItem(R.id.edit_name).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); if (chatIsBroadcast) { canReceive = false; } else { @@ -205,21 +171,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity getMenuInflater().inflate(R.menu.profile_title_context, menu); } - boolean backPressed = false; - @Override - public void onBackPressed() { - backPressed = true; - super.onBackPressed(); - } - - @Override - protected void onPause() { - super.onPause(); - if (backPressed && fromChat) { - overridePendingTransition(0, 0); - } - } - @Override public void onDestroy() { DcHelper.getEventCenter(this).removeObservers(this); @@ -228,22 +179,24 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity @Override public void handleEvent(@NonNull DcEvent event) { - updateToolbar(); } private void initializeResources() { chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, 0); contactId = getIntent().getIntExtra(CONTACT_ID_EXTRA, 0); + contactIsBot = false; chatIsMultiUser = false; chatIsDeviceTalk = false; chatIsMailingList= false; chatIsBroadcast = false; - fromChat = getIntent().getBooleanExtra(FROM_CHAT, false); if (contactId!=0) { + DcContact dcContact = dcContext.getContact(contactId); chatId = dcContext.getChatIdByContactId(contactId); + contactIsBot = dcContact.isBot(); } - else if(chatId!=0) { + + if(chatId!=0) { DcChat dcChat = dcContext.getChat(chatId); chatIsMultiUser = dcChat.isMultiUser(); chatIsDeviceTalk = dcChat.isDeviceTalk(); @@ -255,38 +208,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity } } - if(!isGlobalProfile() && !isSelfProfile() && !chatIsMailingList) { - tabs.add(TAB_SETTINGS); - } - tabs.add(TAB_GALLERY); - tabs.add(TAB_AUDIO); - tabs.add(TAB_DOCS); - tabs.add(TAB_WEBXDC); - //tabs.add(TAB_LINKS); - //if(Prefs.isLocationStreamingEnabled(this)) { - // tabs.add(TAB_MAP); - //} - - this.viewPager = ViewUtil.findById(this, R.id.pager); this.toolbar = ViewUtil.findById(this, R.id.toolbar); - this.tabLayout = ViewUtil.findById(this, R.id.tab_layout); - } - - private void updateToolbar() { - if (isGlobalProfile()){ - getSupportActionBar().setTitle(R.string.menu_all_media); - } - else if (chatId > 0) { - DcChat dcChat = dcContext.getChat(chatId); - titleView.setTitle(GlideApp.with(this), dcChat, true); - } - else if (isContactProfile()){ - titleView.setTitle(GlideApp.with(this), dcContext.getContact(contactId)); - } - } - - private boolean isGlobalProfile() { - return contactId==0 && chatId==0; } private boolean isContactProfile() { @@ -298,124 +220,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity return isContactProfile() && contactId==DcContact.DC_CONTACT_ID_SELF; } - private class ProfilePagerAdapter extends FragmentStatePagerAdapter { - private Object currentFragment = null; - - ProfilePagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - @Override - public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) { - super.setPrimaryItem(container, position, object); - if (currentFragment != null && currentFragment != object) { - ActionMode action = null; - if (currentFragment instanceof MessageSelectorFragment) { - action = ((MessageSelectorFragment) currentFragment).getActionMode(); - } else if (currentFragment instanceof ProfileSettingsFragment) { - action = ((ProfileSettingsFragment) currentFragment).getActionMode(); - } - if (action != null) { - action.finish(); - } - } - currentFragment = object; - } - - @NonNull - @Override - public Fragment getItem(int position) { - int tabId = tabs.get(position); - Fragment fragment; - Bundle args = new Bundle(); - - switch(tabId) { - case TAB_SETTINGS: - fragment = new ProfileSettingsFragment(); - args.putInt(ProfileSettingsFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalProfile())? -1 : chatId); - args.putInt(ProfileSettingsFragment.CONTACT_ID_EXTRA, (contactId==0&&!isGlobalProfile())? -1 : contactId); - break; - - case TAB_GALLERY: - fragment = new ProfileGalleryFragment(); - args.putInt(ProfileGalleryFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalProfile())? -1 : chatId); - break; - - case TAB_AUDIO: - fragment = new ProfileDocumentsFragment(); - args.putInt(ProfileDocumentsFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalProfile())? -1 : chatId); - args.putBoolean(ProfileDocumentsFragment.SHOW_AUDIO_EXTRA, true); - break; - - case TAB_WEBXDC: - fragment = new ProfileDocumentsFragment(); - args.putInt(ProfileDocumentsFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalProfile())? -1 : chatId); - args.putBoolean(ProfileDocumentsFragment.SHOW_WEBXDC_EXTRA, true); - break; - - default: - fragment = new ProfileDocumentsFragment(); - args.putInt(ProfileGalleryFragment.CHAT_ID_EXTRA, (chatId==0&&!isGlobalProfile())? -1 : chatId); - break; - } - - fragment.setArguments(args); - return fragment; - } - - @Override - public int getCount() { - return tabs.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - int tabId = tabs.get(position); - switch(tabId) { - case TAB_SETTINGS: - if (chatIsDeviceTalk) { - return getString(R.string.profile); - } else if(isContactProfile()) { - if (dcContext.getContact(contactId).isBot()) { - return getString(R.string.bot); - } else { - return getString(R.string.tab_contact); - } - } - else if (chatIsBroadcast) { - return getString(R.string.broadcast_list); - } - else if (chatIsMailingList) { - return getString(R.string.mailing_list); - } else { - return getString(R.string.tab_group); - } - - case TAB_GALLERY: - return getString(R.string.tab_gallery); - - case TAB_AUDIO: - return getString(R.string.audio); - - case TAB_DOCS: - return getString(R.string.files); - - case TAB_WEBXDC: - return getString(R.string.webxdc_apps); - - case TAB_LINKS: - return getString(R.string.tab_links); - - case TAB_MAP: - return getString(R.string.tab_map); - - default: - throw new AssertionError(); - } - } - } - - // handle events // ========================================================================= @@ -425,7 +229,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity int itemId = item.getItemId(); if (itemId == android.R.id.home) { - backPressed = true; finish(); return true; } else if (itemId == R.id.menu_mute_notifications) { @@ -503,7 +306,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity .show(); } - private void onEnlargeAvatar() { + public void onEnlargeAvatar() { String profileImagePath; String title; Uri profileImageUri; @@ -528,7 +331,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity intent.putExtra(MediaPreviewActivity.ACTIVITY_TITLE_EXTRA, title); intent.putExtra(MediaPreviewActivity.EDIT_AVATAR_CHAT_ID, chatIsMultiUser ? chatId : 0); // shows edit-button, might be 0 for a contact-profile startActivity(intent); - } else { + } else if (chatIsMultiUser){ onEditName(); } } diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java b/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java new file mode 100644 index 000000000..22331b89d --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/ProfileAdapter.java @@ -0,0 +1,372 @@ +package org.thoughtcrime.securesms; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.b44t.messenger.DcChat; +import com.b44t.messenger.DcChatlist; +import com.b44t.messenger.DcContact; +import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcLot; +import com.b44t.messenger.DcMsg; + +import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.contacts.ContactSelectionListItem; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.util.DateUtils; +import org.thoughtcrime.securesms.util.Util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class ProfileAdapter extends RecyclerView.Adapter +{ + public static final int ITEM_AVATAR = 10; + public static final int ITEM_DIVIDER = 20; + public static final int ITEM_SIGNATURE = 25; + public static final int ITEM_ALL_MEDIA_BUTTON = 30; + public static final int ITEM_SEND_MESSAGE_BUTTON = 35; + public static final int ITEM_LAST_SEEN = 40; + public static final int ITEM_INTRODUCED_BY = 45; + public static final int ITEM_ADDRESS = 50; + public static final int ITEM_HEADER = 53; + public static final int ITEM_MEMBERS = 55; + public static final int ITEM_SHARED_CHATS = 60; + + private final @NonNull Context context; + private final @NonNull DcContext dcContext; + private @Nullable DcChat dcChat; + private @Nullable DcContact dcContact; + + private final @NonNull ArrayList itemData = new ArrayList<>(); + private DcChatlist itemDataSharedChats; + private String itemDataStatusText; + private boolean isBroadcast; + private int memberCount; + private final Set selectedMembers; + + private final LayoutInflater layoutInflater; + private final ItemClickListener clickListener; + private final GlideRequests glideRequests; + + static class ItemData { + final int viewType; + final int contactId; + final int chatlistIndex; + final String label; + final int icon; + + ItemData(int viewType, String label, int icon) { + this(viewType, 0, 0, label, icon); + } + + ItemData(int viewType, int contactId, int chatlistIndex) { + this(viewType, contactId, chatlistIndex, null, 0); + } + + private ItemData(int viewType, int contactId, int chatlistIndex, @Nullable String label, int icon) { + this.viewType = viewType; + this.contactId = contactId; + this.chatlistIndex = chatlistIndex; + this.label = label; + this.icon = icon; + } + }; + + public ProfileAdapter(@NonNull Context context, + @NonNull GlideRequests glideRequests, + @Nullable ItemClickListener clickListener) + { + super(); + this.context = context; + this.glideRequests = glideRequests; + this.clickListener = clickListener; + this.dcContext = DcHelper.getContext(context); + this.layoutInflater = LayoutInflater.from(context); + this.selectedMembers= new HashSet<>(); + } + + @Override + public int getItemCount() { + return itemData.size(); + } + + @Override + public int getItemViewType(int i) { + return itemData.get(i).viewType; + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + public ViewHolder(View itemView) { + super(itemView); + } + } + + @NonNull + @Override + public ProfileAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + if (viewType == ITEM_HEADER) { + final View item = LayoutInflater.from(context).inflate(R.layout.contact_selection_list_divider, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_DIVIDER) { + final View item = LayoutInflater.from(context).inflate(R.layout.profile_divider, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_MEMBERS) { + final ContactSelectionListItem item = (ContactSelectionListItem)layoutInflater.inflate(R.layout.contact_selection_list_item, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_SHARED_CHATS) { + final ConversationListItem item = (ConversationListItem)layoutInflater.inflate(R.layout.conversation_list_item_view, parent, false); + item.hideItemDivider(); + return new ViewHolder(item); + } else if (viewType == ITEM_SIGNATURE) { + final ProfileStatusItem item = (ProfileStatusItem)layoutInflater.inflate(R.layout.profile_status_item, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_AVATAR) { + final ProfileAvatarItem item = (ProfileAvatarItem)layoutInflater.inflate(R.layout.profile_avatar_item, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_ALL_MEDIA_BUTTON || viewType == ITEM_SEND_MESSAGE_BUTTON) { + final ProfileTextItem item = (ProfileTextItem)layoutInflater.inflate(R.layout.profile_text_item_button, parent, false); + return new ViewHolder(item); + } else if (viewType == ITEM_LAST_SEEN || viewType == ITEM_INTRODUCED_BY || viewType == ITEM_ADDRESS) { + final ProfileTextItem item = (ProfileTextItem)layoutInflater.inflate(R.layout.profile_text_item_small, parent, false); + return new ViewHolder(item); + } else { + final ProfileTextItem item = (ProfileTextItem)layoutInflater.inflate(R.layout.profile_text_item, parent, false); + return new ViewHolder(item); + } + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { + ViewHolder holder = (ViewHolder) viewHolder; + ItemData data = itemData.get(i); + if (holder.itemView instanceof ContactSelectionListItem) { + ContactSelectionListItem contactItem = (ContactSelectionListItem) holder.itemView; + + int contactId = data.contactId; + DcContact dcContact = null; + String label = null; + String name; + String addr = null; + + if (contactId == DcContact.DC_CONTACT_ID_ADD_MEMBER) { + if (isBroadcast) { + name = context.getString(R.string.add_recipients); + } else { + name = context.getString(R.string.group_add_members); + } + } + else if (contactId == DcContact.DC_CONTACT_ID_QR_INVITE) { + name = context.getString(R.string.qrshow_title); + } + else { + dcContact = dcContext.getContact(contactId); + name = dcContact.getDisplayName(); + addr = dcContact.getAddr(); + } + + contactItem.unbind(glideRequests); + contactItem.set(glideRequests, contactId, dcContact, name, addr, label, false, true); + contactItem.setSelected(selectedMembers.contains(contactId)); + contactItem.setOnClickListener(view -> clickListener.onMemberClicked(contactId)); + contactItem.setOnLongClickListener(view -> {clickListener.onMemberLongClicked(contactId); return true;}); + } + else if (holder.itemView instanceof ConversationListItem) { + ConversationListItem conversationListItem = (ConversationListItem) holder.itemView; + int chatlistIndex = data.chatlistIndex; + + int chatId = itemDataSharedChats.getChatId(chatlistIndex); + DcChat chat = dcContext.getChat(chatId); + DcLot summary = itemDataSharedChats.getSummary(chatlistIndex, chat); + + conversationListItem.bind(DcHelper.getThreadRecord(context, summary, chat), + itemDataSharedChats.getMsgId(chatlistIndex), summary, glideRequests, + Collections.emptySet(), false); + conversationListItem.setOnClickListener(view -> clickListener.onSharedChatClicked(chatId)); + } + else if(holder.itemView instanceof ProfileStatusItem) { + ProfileStatusItem item = (ProfileStatusItem) holder.itemView; + item.setOnLongClickListener(view -> {clickListener.onStatusLongClicked(); return true;}); + item.set(data.label); + } + else if(holder.itemView instanceof ProfileAvatarItem) { + ProfileAvatarItem item = (ProfileAvatarItem) holder.itemView; + item.setAvatarClickListener(view -> clickListener.onAvatarClicked()); + item.set(glideRequests, dcChat, dcContact, memberCount); + } + else if(holder.itemView instanceof ProfileTextItem) { + ProfileTextItem item = (ProfileTextItem) holder.itemView; + item.setOnClickListener(view -> clickListener.onSettingsClicked(data.viewType)); + item.set(data.label, data.icon); + if (data.viewType == ITEM_LAST_SEEN || data.viewType == ITEM_ADDRESS) { + int padding = (int)((float)context.getResources().getDimensionPixelSize(R.dimen.contact_list_normal_padding) * 1.2); + item.setPadding(item.getPaddingLeft(), item.getPaddingTop(), item.getPaddingRight(), padding); + } else if (data.viewType == ITEM_INTRODUCED_BY) { + int padding = context.getResources().getDimensionPixelSize(R.dimen.contact_list_normal_padding); + item.setPadding(item.getPaddingLeft(), padding, item.getPaddingRight(), item.getPaddingBottom()); + } else if (data.viewType == ITEM_ALL_MEDIA_BUTTON && dcChat != null) { + Util.runOnAnyBackgroundThread(() -> { + String c = getAllMediaCountString(dcChat.getId()); + Util.runOnMain(() -> { + item.setValue(c); + }); + }); + } + } else if (data.viewType == ITEM_HEADER) { + TextView textView = holder.itemView.findViewById(R.id.label); + textView.setText(data.label); + } + } + + public interface ItemClickListener { + void onSettingsClicked(int settingsId); + void onStatusLongClicked(); + void onSharedChatClicked(int chatId); + void onMemberClicked(int contactId); + void onMemberLongClicked(int contactId); + void onAvatarClicked(); + } + + public void toggleMemberSelection(int contactId) { + if (!selectedMembers.remove(contactId)) { + selectedMembers.add(contactId); + } + notifyDataSetChanged(); + } + + @NonNull + public Collection getSelectedMembers() { + return new HashSet<>(selectedMembers); + } + + public int getSelectedMembersCount() { + return selectedMembers.size(); + } + + @NonNull + public String getStatusText() { + return itemDataStatusText; + } + + public void clearSelection() { + selectedMembers.clear(); + notifyDataSetChanged(); + } + + public void changeData(@Nullable int[] memberList, @Nullable DcContact dcContact, @Nullable DcChatlist sharedChats, @Nullable DcChat dcChat) { + this.dcChat = dcChat; + this.dcContact = dcContact; + itemData.clear(); + itemDataSharedChats = sharedChats; + itemDataStatusText = ""; + isBroadcast = dcChat != null && dcChat.isBroadcast(); + boolean isMailingList = dcChat != null && dcChat.isMailingList(); + boolean isGroup = dcChat != null && dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP; + boolean isSelfTalk = dcChat != null && dcChat.isSelfTalk(); + boolean isDeviceTalk = dcChat != null && dcChat.isDeviceTalk(); + memberCount = memberList!=null ? memberList.length : 0; + + itemData.add(new ItemData(ITEM_AVATAR, null, 0)); + + if (isSelfTalk || dcContact != null && !dcContact.getStatus().isEmpty()) { + itemDataStatusText = isSelfTalk ? context.getString(R.string.saved_messages_explain) : dcContact.getStatus(); + itemData.add(new ItemData(ITEM_SIGNATURE, itemDataStatusText, 0)); + } else { + itemData.add(new ItemData(ITEM_DIVIDER, null, 0)); + } + + itemData.add(new ItemData(ITEM_ALL_MEDIA_BUTTON, context.getString(R.string.apps_and_media), R.drawable.ic_apps_24)); + + if (dcContact != null && !isDeviceTalk && !isSelfTalk) { + itemData.add(new ItemData(ITEM_SEND_MESSAGE_BUTTON, context.getString(R.string.send_message), R.drawable.ic_send_sms_white_24dp)); + } + + if (dcContact != null && !isDeviceTalk && !isSelfTalk) { + long lastSeenTimestamp = dcContact.getLastSeen(); + String lastSeenTxt; + if (lastSeenTimestamp == 0) { + lastSeenTxt = context.getString(R.string.last_seen_unknown); + } + else { + lastSeenTxt = context.getString(R.string.last_seen_at, DateUtils.getExtendedTimeSpanString(context, lastSeenTimestamp)); + } + itemData.add(new ItemData(ITEM_LAST_SEEN, lastSeenTxt, 0)); + } + + if (memberList!=null) { + itemData.add(new ItemData(ITEM_DIVIDER, null, 0)); + if (dcChat != null) { + if (!isMailingList && dcChat.canSend()) { + itemData.add(new ItemData(ITEM_MEMBERS, DcContact.DC_CONTACT_ID_ADD_MEMBER, 0)); + if (!isBroadcast) { + itemData.add(new ItemData(ITEM_MEMBERS, DcContact.DC_CONTACT_ID_QR_INVITE, 0)); + } + } + } + for (int value : memberList) { + itemData.add(new ItemData(ITEM_MEMBERS, value, 0)); + } + } + + if (sharedChats != null && !isDeviceTalk) { + itemData.add(new ItemData(ITEM_HEADER, context.getString(R.string.profile_shared_chats), 0)); + for (int i = 0; i < sharedChats.getCnt(); i++) { + itemData.add(new ItemData(ITEM_SHARED_CHATS, 0, i)); + } + } + + if (dcContact != null && !isDeviceTalk && !isSelfTalk) { + int verifierId = dcContact.getVerifierId(); + if (verifierId != 0) { + String introducedBy; + if (verifierId == DcContact.DC_CONTACT_ID_SELF) { + introducedBy = context.getString(R.string.verified_by_you); + } else { + introducedBy = context.getString(R.string.verified_by, dcContext.getContact(verifierId).getDisplayName()); + } + itemData.add(new ItemData(ITEM_INTRODUCED_BY, introducedBy, 0)); + } + + if (dcContact != null) { + itemData.add(new ItemData(ITEM_ADDRESS, dcContact.getAddr(), 0)); + } else if (isMailingList) { + itemData.add(new ItemData(ITEM_ADDRESS, dcChat.getMailinglistAddr(), 0)); + } + } + + notifyDataSetChanged(); + } + + public int ALL_MEDIA_COUNT_MAX = 500; + public int getAllMediaCount(int chatId) { + int c = dcContext.getChatMedia(chatId, DcMsg.DC_MSG_IMAGE, DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_VIDEO).length; + if (c < ALL_MEDIA_COUNT_MAX) { + c += dcContext.getChatMedia(chatId, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0).length; + } + if (c < ALL_MEDIA_COUNT_MAX) { + c += dcContext.getChatMedia(chatId, DcMsg.DC_MSG_FILE, DcMsg.DC_MSG_WEBXDC, 0).length; + } + return c; + } + + public String getAllMediaCountString(int chatId) { + final int c = getAllMediaCount(chatId); + if (c == 0) { + return context.getString(R.string.none); + } else if (c >= ALL_MEDIA_COUNT_MAX) { + return ALL_MEDIA_COUNT_MAX + "+"; + } else { + return c + ""; + } + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java b/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java new file mode 100644 index 000000000..301c25fdd --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/ProfileAvatarItem.java @@ -0,0 +1,112 @@ +package org.thoughtcrime.securesms; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.b44t.messenger.DcChat; +import com.b44t.messenger.DcContact; + +import org.thoughtcrime.securesms.components.AvatarView; +import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; + +public class ProfileAvatarItem extends LinearLayout implements RecipientModifiedListener { + + private AvatarView avatarView; + private TextView nameView; + private TextView subtitleView; + + private Recipient recipient; + private GlideRequests glideRequests; + + public ProfileAvatarItem(Context context) { + super(context); + } + + public ProfileAvatarItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + avatarView = findViewById(R.id.avatar); + nameView = findViewById(R.id.name); + subtitleView = findViewById(R.id.subtitle); + + ViewUtil.setTextViewGravityStart(nameView, getContext()); + } + + public void set(@NonNull GlideRequests glideRequests, @Nullable DcChat dcChat, @Nullable DcContact dcContact, int memberCount) { + this.glideRequests = glideRequests; + + String name = ""; + boolean greenCheckmark = false; + String subtitle = null; + if (dcChat != null) { + recipient = new Recipient(getContext(), dcChat); + name = dcChat.getName(); + greenCheckmark = dcChat.isProtected(); + + if (dcChat.isMailingList()) { + subtitle = getContext().getString(R.string.contacts_headline); + } else if (dcChat.isBroadcast()) { + subtitle = getContext().getResources().getQuantityString(R.plurals.n_recipients, memberCount, memberCount); + } else if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) { + subtitle = getContext().getResources().getQuantityString(R.plurals.n_members, memberCount, memberCount); + } + } else if (dcContact != null) { + recipient = new Recipient(getContext(), dcContact); + name = dcContact.getDisplayName(); + greenCheckmark = dcContact.isVerified(); + } + + recipient.addListener(this); + avatarView.setAvatar(glideRequests, recipient, false); + avatarView.setSeenRecently(dcContact != null && dcContact.wasSeenRecently()); + + nameView.setText(name); + nameView.setCompoundDrawablesWithIntrinsicBounds(0,0, greenCheckmark ? R.drawable.ic_verified : 0, 0); + + if (subtitle != null) { + subtitleView.setVisibility(View.VISIBLE); + subtitleView.setText(subtitle); + } else { + subtitleView.setVisibility(View.GONE); + } + } + + public void setAvatarClickListener(OnClickListener listener) { + avatarView.setAvatarClickListener(listener); + } + + public void unbind(GlideRequests glideRequests) { + if (recipient != null) { + recipient.removeListener(this); + recipient = null; + } + + avatarView.clear(glideRequests); + } + + @Override + public void onModified(final Recipient recipient) { + if (this.recipient == recipient) { + Util.runOnMain(() -> { + avatarView.setAvatar(glideRequests, recipient, false); + DcContact contact = recipient.getDcContact(); + avatarView.setSeenRecently(contact != null && contact.wasSeenRecently()); + nameView.setText(recipient.toShortString()); + }); + } + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsFragment.java b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java similarity index 91% rename from src/main/java/org/thoughtcrime/securesms/ProfileSettingsFragment.java rename to src/main/java/org/thoughtcrime/securesms/ProfileFragment.java index 649a6e2e0..1a1ee6e76 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java @@ -31,7 +31,6 @@ import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.qr.QrShowActivity; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; @@ -39,16 +38,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class ProfileSettingsFragment extends Fragment - implements ProfileSettingsAdapter.ItemClickListener, DcEventCenter.DcEventDelegate { +public class ProfileFragment extends Fragment + implements ProfileAdapter.ItemClickListener, DcEventCenter.DcEventDelegate { public static final String CHAT_ID_EXTRA = "chat_id"; public static final String CONTACT_ID_EXTRA = "contact_id"; private static final int REQUEST_CODE_PICK_CONTACT = 2; - private StickyHeaderDecoration listDecoration; - private ProfileSettingsAdapter adapter; + private ProfileAdapter adapter; private ActionMode actionMode; private final ActionModeCallback actionModeCallback = new ActionModeCallback(); @@ -57,10 +55,6 @@ public class ProfileSettingsFragment extends Fragment protected int chatId; private int contactId; - protected ActionMode getActionMode() { - return actionMode; - } - @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); @@ -72,14 +66,12 @@ public class ProfileSettingsFragment extends Fragment @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.profile_settings_fragment, container, false); - adapter = new ProfileSettingsAdapter(requireContext(), GlideApp.with(this), this); + View view = inflater.inflate(R.layout.profile_fragment, container, false); + adapter = new ProfileAdapter(requireContext(), GlideApp.with(this), this); RecyclerView list = ViewUtil.findById(view, R.id.recycler_view); list.setAdapter(adapter); list.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false)); - listDecoration = new StickyHeaderDecoration(adapter, false, true); - list.addItemDecoration(listDecoration); update(); @@ -97,12 +89,6 @@ public class ProfileSettingsFragment extends Fragment super.onDestroyView(); } - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - listDecoration.onConfigurationChanged(newConfig); - } - @Override public void handleEvent(@NonNull DcEvent event) { update(); @@ -121,12 +107,11 @@ public class ProfileSettingsFragment extends Fragment if(dcChat!=null && dcChat.isMultiUser()) { memberList = dcContext.getChatContacts(chatId); } - else if(contactId>0) { + else if(contactId>0 && contactId!=DcContact.DC_CONTACT_ID_SELF) { sharedChats = dcContext.getChatlist(0, null, contactId); } adapter.changeData(memberList, dcContact, sharedChats, dcChat); - listDecoration.invalidateLayouts(); } @@ -136,10 +121,17 @@ public class ProfileSettingsFragment extends Fragment @Override public void onSettingsClicked(int settingsId) { switch(settingsId) { - case ProfileSettingsAdapter.INFO_SEND_MESSAGE_BUTTON: + case ProfileAdapter.ITEM_ALL_MEDIA_BUTTON: + if (chatId > 0) { + Intent intent = new Intent(getActivity(), AllMediaActivity.class); + intent.putExtra(AllMediaActivity.CHAT_ID_EXTRA, chatId); + startActivity(intent); + } + break; + case ProfileAdapter.ITEM_SEND_MESSAGE_BUTTON: onSendMessage(); break; - case ProfileSettingsAdapter.INFO_VERIFIED: + case ProfileAdapter.ITEM_INTRODUCED_BY: onVerifiedByClicked(); break; } @@ -202,6 +194,12 @@ public class ProfileSettingsFragment extends Fragment } } + @Override + public void onAvatarClicked() { + ProfileActivity activity = (ProfileActivity)getActivity(); + activity.onEnlargeAvatar(); + } + public void onAddMember() { DcChat dcChat = dcContext.getChat(chatId); Intent intent = new Intent(getContext(), ContactMultiSelectionActivity.class); diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsAdapter.java b/src/main/java/org/thoughtcrime/securesms/ProfileSettingsAdapter.java deleted file mode 100644 index dd04ba374..000000000 --- a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsAdapter.java +++ /dev/null @@ -1,352 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.b44t.messenger.DcChat; -import com.b44t.messenger.DcChatlist; -import com.b44t.messenger.DcContact; -import com.b44t.messenger.DcContext; -import com.b44t.messenger.DcLot; - -import org.thoughtcrime.securesms.connect.DcHelper; -import org.thoughtcrime.securesms.contacts.ContactSelectionListItem; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class ProfileSettingsAdapter extends RecyclerView.Adapter - implements StickyHeaderAdapter -{ - public static final int INFO_VERIFIED = 118; - public static final int INFO_LAST_SEEN = 119; - public static final int INFO_SEND_MESSAGE_BUTTON = 120; - - private final @NonNull Context context; - private final @NonNull DcContext dcContext; - - private final @NonNull ArrayList itemData = new ArrayList<>(); - private int itemDataMemberCount; - private DcChatlist itemDataSharedChats; - private String itemDataStatusText; - private boolean isMailingList; - private boolean isBroadcast; - private final Set selectedMembers; - - private final LayoutInflater layoutInflater; - private final ItemClickListener clickListener; - private final GlideRequests glideRequests; - - static class ItemData { - static final int CATEGORY_INFO = 1; - static final int CATEGORY_SIGNATURE = 2; - static final int CATEGORY_MEMBERS = 3; - static final int CATEGORY_SHARED_CHATS = 4; - final int type; - final int contactId; - final int chatlistIndex; - final int settingsId; - final String label; - final int labelColor; - final int iconLeft; - - ItemData(int type, int settingsId, String label, int labelColor, int iconLeft) { - this(type, 0, 0, settingsId, label, labelColor, iconLeft); - } - - ItemData(int type, int contactId, int chatlistIndex) { - this(type, contactId, chatlistIndex, 0, null, 0, 0); - } - - ItemData(int type, int contactId, int chatlistIndex, int settingsId, @Nullable String label, int labelColor, int iconLeft) { - this.type = type; - this.contactId = contactId; - this.chatlistIndex = chatlistIndex; - this.settingsId = settingsId; - this.label = label; - this.labelColor = labelColor; - this.iconLeft = iconLeft; - } - }; - - public ProfileSettingsAdapter(@NonNull Context context, - @NonNull GlideRequests glideRequests, - @Nullable ItemClickListener clickListener) - { - super(); - this.context = context; - this.glideRequests = glideRequests; - this.clickListener = clickListener; - this.dcContext = DcHelper.getContext(context); - this.layoutInflater = LayoutInflater.from(context); - this.selectedMembers= new HashSet<>(); - } - - @Override - public int getItemCount() { - return itemData.size(); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - public ViewHolder(View itemView) { - super(itemView); - } - } - - static class HeaderViewHolder extends RecyclerView.ViewHolder { - final TextView textView; - HeaderViewHolder(View itemView) { - super(itemView); - textView = itemView.findViewById(R.id.label); - } - } - - @NonNull - @Override - public ProfileSettingsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - if (viewType == ItemData.CATEGORY_MEMBERS) { - final ContactSelectionListItem item = (ContactSelectionListItem)layoutInflater.inflate(R.layout.contact_selection_list_item, parent, false); - item.setNoHeaderPadding(); - return new ViewHolder(item); - } - else if (viewType == ItemData.CATEGORY_SHARED_CHATS) { - final ConversationListItem item = (ConversationListItem)layoutInflater.inflate(R.layout.conversation_list_item_view, parent, false); - item.hideItemDivider(); - return new ViewHolder(item); - } - else if (viewType == ItemData.CATEGORY_SIGNATURE) { - final ProfileStatusItem item = (ProfileStatusItem)layoutInflater.inflate(R.layout.profile_status_item, parent, false); - return new ViewHolder(item); - } - else { - final ProfileSettingsItem item = (ProfileSettingsItem)layoutInflater.inflate(R.layout.profile_settings_item, parent, false); - return new ViewHolder(item); - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { - ViewHolder holder = (ViewHolder) viewHolder; - if (holder.itemView instanceof ContactSelectionListItem) { - ContactSelectionListItem contactItem = (ContactSelectionListItem) holder.itemView; - - int contactId = itemData.get(i).contactId; - DcContact dcContact = null; - String label = null; - String name; - String addr = null; - - if (contactId == DcContact.DC_CONTACT_ID_ADD_MEMBER) { - if (isBroadcast) { - name = context.getString(R.string.add_recipients); - } else { - name = context.getString(R.string.group_add_members); - } - } - else if (contactId == DcContact.DC_CONTACT_ID_QR_INVITE) { - name = context.getString(R.string.qrshow_title); - } - else { - dcContact = dcContext.getContact(contactId); - name = dcContact.getDisplayName(); - addr = dcContact.getAddr(); - } - - contactItem.unbind(glideRequests); - contactItem.set(glideRequests, contactId, dcContact, name, addr, label, false, true); - contactItem.setSelected(selectedMembers.contains(contactId)); - contactItem.setOnClickListener(view -> clickListener.onMemberClicked(contactId)); - contactItem.setOnLongClickListener(view -> {clickListener.onMemberLongClicked(contactId); return true;}); - } - else if (holder.itemView instanceof ConversationListItem) { - ConversationListItem conversationListItem = (ConversationListItem) holder.itemView; - int chatlistIndex = itemData.get(i).chatlistIndex; - - int chatId = itemDataSharedChats.getChatId(chatlistIndex); - DcChat chat = dcContext.getChat(chatId); - DcLot summary = itemDataSharedChats.getSummary(chatlistIndex, chat); - - conversationListItem.bind(DcHelper.getThreadRecord(context, summary, chat), - itemDataSharedChats.getMsgId(chatlistIndex), summary, glideRequests, - Collections.emptySet(), false); - conversationListItem.setOnClickListener(view -> clickListener.onSharedChatClicked(chatId)); - } - else if(holder.itemView instanceof ProfileStatusItem) { - ProfileStatusItem item = (ProfileStatusItem) holder.itemView; - item.setOnLongClickListener(view -> {clickListener.onStatusLongClicked(); return true;}); - item.set(itemData.get(i).label); - } - else if(holder.itemView instanceof ProfileSettingsItem) { - int settingsId = itemData.get(i).settingsId; - ProfileSettingsItem profileSettingsItem = (ProfileSettingsItem) holder.itemView; - profileSettingsItem.setOnClickListener(view -> clickListener.onSettingsClicked(settingsId)); - profileSettingsItem.set(itemData.get(i).label, itemData.get(i).labelColor, itemData.get(i).iconLeft); - } - } - - @Override - public int getItemViewType(int i) { - return itemData.get(i).type; - } - - public interface ItemClickListener { - void onSettingsClicked(int settingsId); - void onStatusLongClicked(); - void onSharedChatClicked(int chatId); - void onMemberClicked(int contactId); - void onMemberLongClicked(int contactId); - } - - @Override - public long getHeaderId(int position) { - return getItemViewType(position); - } - - @Override - public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { - return new HeaderViewHolder(LayoutInflater.from(context).inflate(R.layout.contact_selection_list_divider, parent, false)); - } - - @Override - public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { - String txt = ""; - switch(getItemViewType(position)) { - case ItemData.CATEGORY_MEMBERS: - if (isMailingList) { - txt = context.getString(R.string.contacts_headline); - } else if (isBroadcast) { - txt = context.getResources().getQuantityString(R.plurals.n_recipients, (int) itemDataMemberCount, (int) itemDataMemberCount); - } else { - txt = context.getResources().getQuantityString(R.plurals.n_members, (int) itemDataMemberCount, (int) itemDataMemberCount); - } - break; - case ItemData.CATEGORY_SHARED_CHATS: - txt = context.getString(R.string.profile_shared_chats); - break; - case ItemData.CATEGORY_INFO: - txt = context.getString(R.string.info); - break; - case ItemData.CATEGORY_SIGNATURE: - txt = context.getString(R.string.pref_default_status_label); - break; - default: - txt = context.getString(R.string.menu_settings); - break; - } - viewHolder.textView.setText(txt); - } - - public void toggleMemberSelection(int contactId) { - if (!selectedMembers.remove(contactId)) { - selectedMembers.add(contactId); - } - notifyDataSetChanged(); - } - - @NonNull - public Collection getSelectedMembers() { - return new HashSet<>(selectedMembers); - } - - public int getSelectedMembersCount() { - return selectedMembers.size(); - } - - @NonNull - public String getStatusText() { - return itemDataStatusText; - } - - public void clearSelection() { - selectedMembers.clear(); - notifyDataSetChanged(); - } - - public void changeData(@Nullable int[] memberList, @Nullable DcContact dcContact, @Nullable DcChatlist sharedChats, @Nullable DcChat dcChat) { - itemData.clear(); - itemDataMemberCount = 0; - itemDataSharedChats = null; - itemDataStatusText = ""; - isMailingList = false; - isBroadcast = false; - - if (memberList!=null) { - itemDataMemberCount = memberList.length; - if (dcChat != null) { - if (dcChat.isBroadcast()) { - isBroadcast = true; - } - - if (dcChat.isMailingList()) { - isMailingList = true; - } else if (dcChat.canSend()) { - itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, DcContact.DC_CONTACT_ID_ADD_MEMBER, 0)); - if (!isBroadcast) { - itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, DcContact.DC_CONTACT_ID_QR_INVITE, 0)); - } - } - } - - for (int value : memberList) { - itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, value, 0)); - } - } - else if (sharedChats!=null && dcContact!=null) { - boolean chatIsDeviceTalk = dcChat != null && dcChat.isDeviceTalk(); - - if (!chatIsDeviceTalk) { - int verifierId = dcContact.getVerifierId(); - if (verifierId != 0) { - String verifiedInfo; - if (verifierId == DcContact.DC_CONTACT_ID_SELF) { - verifiedInfo = context.getString(R.string.verified_by_you); - } else { - verifiedInfo = context.getString(R.string.verified_by, dcContext.getContact(verifierId).getDisplayName()); - } - itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_VERIFIED, verifiedInfo, 0, R.drawable.ic_verified)); - } - - long lastSeenTimestamp = dcContact.getLastSeen(); - String lastSeenTxt; - if (lastSeenTimestamp == 0) { - lastSeenTxt = context.getString(R.string.last_seen_unknown); - } - else { - lastSeenTxt = context.getString(R.string.last_seen_at, DateUtils.getExtendedTimeSpanString(context, lastSeenTimestamp)); - } - itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_LAST_SEEN, lastSeenTxt, 0, 0)); - - - itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_SEND_MESSAGE_BUTTON, context.getString(R.string.send_message), R.color.delta_accent, 0)); - } - - itemDataStatusText = dcContact.getStatus(); - if (!itemDataStatusText.isEmpty()) { - itemData.add(new ItemData(ItemData.CATEGORY_SIGNATURE, 0, itemDataStatusText, 0, 0)); - } - - itemDataSharedChats = sharedChats; - if (!chatIsDeviceTalk) { - int sharedChatsCnt = sharedChats.getCnt(); - for (int i = 0; i < sharedChatsCnt; i++) { - itemData.add(new ItemData(ItemData.CATEGORY_SHARED_CHATS, 0, i)); - } - } - } - - notifyDataSetChanged(); - } -} diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsItem.java b/src/main/java/org/thoughtcrime/securesms/ProfileSettingsItem.java deleted file mode 100644 index 8b113c29d..000000000 --- a/src/main/java/org/thoughtcrime/securesms/ProfileSettingsItem.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.core.content.ContextCompat; - -import org.thoughtcrime.securesms.util.ResUtil; - -public class ProfileSettingsItem extends LinearLayout { - - private TextView labelView; - - public ProfileSettingsItem(Context context) { - super(context); - } - - public ProfileSettingsItem(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - labelView = findViewById(R.id.label); - } - - public void set(String label, int labelColor, int iconLeft) { - labelView.setText(label==null? "" : label); - labelView.setCompoundDrawablesWithIntrinsicBounds(iconLeft, 0,0,0); - - // we need different color getters as `labelColor` is `R.color.name` while default is `R.attr.name` - if (labelColor != 0) { - labelView.setTextColor(ContextCompat.getColor(getContext(), labelColor)); - } else { - labelView.setTextColor(ResUtil.getColor(getContext(), R.attr.emoji_text_color)); - } - } -} diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileTextItem.java b/src/main/java/org/thoughtcrime/securesms/ProfileTextItem.java new file mode 100644 index 000000000..d75082c04 --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/ProfileTextItem.java @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; + +import org.thoughtcrime.securesms.util.ResUtil; + +public class ProfileTextItem extends LinearLayout { + + private TextView labelView; + private @Nullable TextView valueView; + + public ProfileTextItem(Context context) { + super(context); + } + + public ProfileTextItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + labelView = findViewById(R.id.label); + valueView = findViewById(R.id.value); + } + + public void set(String label, int icon) { + labelView.setText(label); + + if (icon != 0) { + Drawable orgDrawable = ContextCompat.getDrawable(getContext(), icon); + if (orgDrawable != null) { + Drawable drawable = orgDrawable.mutate(); // avoid global state modification and showing eg. app-icon tinted also elsewhere + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, getResources().getColor(R.color.delta_accent)); + labelView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); + } + } + } + + public void setValue(String value) { + if (valueView != null) { + valueView.setText(value); + valueView.setVisibility(View.VISIBLE); + } + } +} diff --git a/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java index 0c0a86424..9f675e654 100644 --- a/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java +++ b/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java @@ -163,9 +163,4 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM }); } } - - public void setNoHeaderPadding() { - int paddinglr = getContext().getResources().getDimensionPixelSize(R.dimen.contact_list_normal_padding); - setPadding(paddinglr, 0, paddinglr, 0); - } } diff --git a/src/main/res/layout/all_media_activity.xml b/src/main/res/layout/all_media_activity.xml new file mode 100644 index 000000000..d2c2e1a7a --- /dev/null +++ b/src/main/res/layout/all_media_activity.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + diff --git a/src/main/res/layout/avatar_view.xml b/src/main/res/layout/avatar_view.xml index f175f8c8b..cb28e84dc 100644 --- a/src/main/res/layout/avatar_view.xml +++ b/src/main/res/layout/avatar_view.xml @@ -25,6 +25,8 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/top_guideline" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintWidth_max="34dp" + app:layout_constraintHeight_max="34dp" android:src="@drawable/ic_circle_status_online" android:contentDescription="@null" diff --git a/src/main/res/layout/contact_selection_list_item.xml b/src/main/res/layout/contact_selection_list_item.xml index 0d2f5243f..2b01e070c 100644 --- a/src/main/res/layout/contact_selection_list_item.xml +++ b/src/main/res/layout/contact_selection_list_item.xml @@ -8,8 +8,8 @@ android:gravity="center_vertical" android:focusable="true" android:background="?attr/conversation_list_item_background" - android:paddingLeft="24dp" - android:paddingRight="24dp"> + android:paddingLeft="16dp" + android:paddingRight="16dp"> - - - - diff --git a/src/main/res/layout/profile_avatar_item.xml b/src/main/res/layout/profile_avatar_item.xml new file mode 100644 index 000000000..ab7ed97bb --- /dev/null +++ b/src/main/res/layout/profile_avatar_item.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/profile_divider.xml b/src/main/res/layout/profile_divider.xml new file mode 100644 index 000000000..a1458c441 --- /dev/null +++ b/src/main/res/layout/profile_divider.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/main/res/layout/profile_settings_fragment.xml b/src/main/res/layout/profile_fragment.xml similarity index 100% rename from src/main/res/layout/profile_settings_fragment.xml rename to src/main/res/layout/profile_fragment.xml diff --git a/src/main/res/layout/profile_settings_item.xml b/src/main/res/layout/profile_settings_item.xml deleted file mode 100644 index 1a57eec3a..000000000 --- a/src/main/res/layout/profile_settings_item.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/main/res/layout/profile_status_item.xml b/src/main/res/layout/profile_status_item.xml index fbeace308..0dc5e6158 100644 --- a/src/main/res/layout/profile_status_item.xml +++ b/src/main/res/layout/profile_status_item.xml @@ -4,15 +4,18 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" - android:padding="16dp"> + android:paddingTop="8dp" + android:paddingBottom="8dp"> + android:textColorLink="?attr/emoji_text_color" + android:background="?attr/contact_list_divider"/> diff --git a/src/main/res/layout/profile_text_item.xml b/src/main/res/layout/profile_text_item.xml new file mode 100644 index 000000000..bd7e2b9f2 --- /dev/null +++ b/src/main/res/layout/profile_text_item.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/main/res/layout/profile_text_item_button.xml b/src/main/res/layout/profile_text_item_button.xml new file mode 100644 index 000000000..027cf5180 --- /dev/null +++ b/src/main/res/layout/profile_text_item_button.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/src/main/res/layout/profile_text_item_small.xml b/src/main/res/layout/profile_text_item_small.xml new file mode 100644 index 000000000..5db68843f --- /dev/null +++ b/src/main/res/layout/profile_text_item_small.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/src/main/res/menu/conversation.xml b/src/main/res/menu/conversation.xml index d21b28434..2cbf9a2c9 100644 --- a/src/main/res/menu/conversation.xml +++ b/src/main/res/menu/conversation.xml @@ -25,10 +25,10 @@ android:visible="false" app:showAsAction="always"/> - + app:showAsAction="always" /> diff --git a/src/main/res/menu/profile_common.xml b/src/main/res/menu/profile_common.xml index 2ae4969d3..7ec7330a6 100644 --- a/src/main/res/menu/profile_common.xml +++ b/src/main/res/menu/profile_common.xml @@ -8,7 +8,8 @@ + android:icon="@drawable/ic_create_white_24dp" + app:showAsAction="always"/>