Merge branch 'main' into adb/pgp-contacts2

This commit is contained in:
adbenitez 2025-07-08 18:08:51 +02:00
commit b9520c95ed
32 changed files with 1058 additions and 752 deletions

View file

@ -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

View file

@ -288,6 +288,10 @@
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".AllMediaActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"

View file

@ -0,0 +1,196 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
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.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import com.google.android.material.tabs.TabLayout;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.ArrayList;
public class AllMediaActivity extends PassphraseRequiredActionBarActivity
implements DcEventCenter.DcEventDelegate
{
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
public static final String FORCE_GALLERY = "force_gallery";
static class TabData {
final int title;
final int type1;
final int type2;
final int type3;
TabData(int title, int type1, int type2, int type3) {
this.title = title;
this.type1 = type1;
this.type2 = type2;
this.type3 = type3;
}
};
private DcContext dcContext;
private int chatId;
private int contactId;
private final ArrayList<TabData> 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;
}
}

View file

@ -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,7 +57,7 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
}
}
ProfileDocumentsAdapter(@NonNull Context context,
AllMediaDocumentsAdapter(@NonNull Context context,
BucketedThreadMedia media,
ItemClickListener clickListener)
{

View file

@ -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<BucketedThreadMediaLoader.BucketedThreadMedia>,
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<BucketedThreadMediaLoader.BucketedThreadMedia> 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<BucketedThreadMediaLoader.BucketedThreadMedia> 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<BucketedThreadMediaLoader.BucketedThreadMedia> 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 {

View file

@ -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,7 +50,7 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
}
}
ProfileGalleryAdapter(@NonNull Context context,
AllMediaGalleryAdapter(@NonNull Context context,
@NonNull GlideRequests glideRequests,
BucketedThreadMedia media,
ItemClickListener clickListener)

View file

@ -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<BucketedThreadMediaLoader.BucketedThreadMedia>,
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<BucketedThreadMediaLoader.BucketedThreadMedia> 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<BucketedThreadMediaLoader.BucketedThreadMedia> 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 {

View file

@ -529,8 +529,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);
@ -612,15 +612,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() {
@ -872,7 +873,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);

View file

@ -449,7 +449,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;
}

View file

@ -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();
}

View file

@ -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<Integer> tabs = new ArrayList<>();
private boolean contactIsBot;
private Toolbar toolbar;
private ConversationTitleView titleView;
private TabLayout tabLayout;
private ViewPager viewPager;
@Override
protected void onPreCreate() {
@ -97,38 +73,27 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
setSupportActionBar(this.toolbar);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
if (isGlobalProfile()) {
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.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);
}
}
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;
@ -149,6 +114,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 {
@ -207,21 +173,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);
@ -230,22 +181,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();
@ -257,38 +210,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() {
@ -300,124 +222,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
// =========================================================================
@ -427,7 +231,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) {
@ -505,7 +308,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
.show();
}
private void onEnlargeAvatar() {
public void onEnlargeAvatar() {
String profileImagePath;
String title;
Uri profileImageUri;
@ -530,7 +333,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();
}
}

View file

@ -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> itemData = new ArrayList<>();
private DcChatlist itemDataSharedChats;
private String itemDataStatusText;
private boolean isBroadcast;
private int memberCount;
private final Set<Integer> 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<Integer> 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() && dcChat.isEncrypted()) {
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 + "";
}
}
}

View file

@ -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());
});
}
}
}

View file

@ -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);

View file

@ -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<ProfileSettingsAdapter.HeaderViewHolder>
{
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> itemData = new ArrayList<>();
private int itemDataMemberCount;
private DcChatlist itemDataSharedChats;
private String itemDataStatusText;
private boolean isMailingList;
private boolean isBroadcast;
private final Set<Integer> 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<Integer> 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() && dcChat.isEncrypted()) {
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();
}
}

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:contentInsetStart="14dp"
app:contentInsetLeft="14dp"
android:elevation="4dp"
android:theme="?attr/actionBarStyle"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:layout_gravity="top"
android:background="?attr/colorPrimary"
app:tabMode="scrollable"
app:tabPaddingStart="8dp"
app:tabPaddingEnd="8dp"
app:tabBackground="?attr/colorPrimary"
app:tabIndicatorColor="@color/white"
app:tabTextColor="@color/gray10"
app:tabSelectedTextColor="@color/white"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -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"

View file

@ -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">
<org.thoughtcrime.securesms.components.AvatarView
android:id="@+id/avatar"

View file

@ -19,26 +19,10 @@
app:contentInsetLeft="14dp"
android:elevation="4dp"
android:theme="?attr/actionBarStyle"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:layout_gravity="top"
android:background="?attr/colorPrimary"
app:tabMode="scrollable"
app:tabPaddingStart="8dp"
app:tabPaddingEnd="8dp"
app:tabBackground="?attr/colorPrimary"
app:tabIndicatorColor="@color/white"
app:tabTextColor="@color/gray10"
app:tabSelectedTextColor="@color/white"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ProfileAvatarItem
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:focusable="true"
android:background="?attr/conversation_list_item_background"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginBottom="16dp">
<LinearLayout android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1"
android:orientation="vertical">
<org.thoughtcrime.securesms.components.AvatarView
android:id="@+id/avatar"
android:layout_width="152dp"
android:layout_height="152dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="marquee"
android:singleLine="true"
android:fontFamily="sans-serif"
android:textStyle="bold"
android:textSize="24sp"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="Some Group" />
<!-- Attention: Using android:maxLines="1", if the name is an emoji followed by a
long word and the chat is muted, then the long word is not shown at all
(instead of using `…`). That's why we use android:singleLine="true" -->
<TextView android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:singleLine="true"
android:fontFamily="sans-serif"
android:ellipsize="end"
android:textSize="16sp"
android:textColor="?attr/conversation_list_item_contact_color"
tools:text="3 members" />
</LinearLayout>
</org.thoughtcrime.securesms.ProfileAvatarItem>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?attr/contact_list_divider"/>
</LinearLayout>

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ProfileSettingsItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/conversation_list_item_background"
android:focusable="true"
android:padding="16dp">
<TextView android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="7dp"
android:gravity="start|center_vertical"
android:textColor="?attr/emoji_text_color"
android:textSize="16sp"/>
</org.thoughtcrime.securesms.ProfileSettingsItem>

View file

@ -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">
<androidx.appcompat.widget.AppCompatTextView
android:padding="16dp"
android:id="@+id/status_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
style="@style/Signal.Text.Body"
android:textColor="?attr/emoji_text_color"
android:textColorLink="?attr/emoji_text_color"/>
android:textColorLink="?attr/emoji_text_color"
android:background="?attr/contact_list_divider"/>
</org.thoughtcrime.securesms.ProfileStatusItem>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ProfileTextItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/conversation_list_item_background"
android:focusable="true"
android:padding="16dp">
<TextView
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start|center_vertical"
android:textColor="?attr/emoji_text_color"
android:textSize="16sp" />
</org.thoughtcrime.securesms.ProfileTextItem>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ProfileTextItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/conversation_list_item_background"
android:focusable="true"
android:padding="16dp">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:textColor="@color/delta_accent"
android:drawablePadding="5dp"
android:textSize="16sp" />
<TextView
android:id="@+id/value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end|center_vertical"
style="@style/Signal.Text.Caption"
android:textColor="?attr/conversation_list_item_date_color"
android:visibility="gone" />
</LinearLayout>
</org.thoughtcrime.securesms.ProfileTextItem>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ProfileTextItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/conversation_list_item_background"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start|center_vertical"
android:textSize="16sp"
android:textColor="?attr/conversation_list_item_contact_color" />
</org.thoughtcrime.securesms.ProfileTextItem>

View file

@ -25,10 +25,10 @@
android:visible="false"
app:showAsAction="always"/>
<item android:id="@+id/menu_show_apps"
android:title="@string/webxdc_apps"
<item android:id="@+id/menu_all_media"
android:title="@string/apps_and_media"
android:icon="@drawable/ic_apps_24"
app:showAsAction="ifRoom" />
app:showAsAction="always" />
<item android:id="@+id/menu_ephemeral_messages"
android:title="@string/ephemeral_messages" />

View file

@ -8,7 +8,8 @@
<item android:title="@string/global_menu_edit_desktop"
android:id="@+id/edit_name"
app:showAsAction="never"/>
android:icon="@drawable/ic_create_white_24dp"
app:showAsAction="always"/>
<item android:title="@string/menu_mute"
android:id="@+id/menu_mute_notifications"