Add nav drawer with account switcher

(cherry picked from commit f06a7b48814c0981c07cc110185930459547cdb2)
This commit is contained in:
Stephen Paul Weber 2024-09-11 21:37:38 +02:00 committed by Arne
parent 3f45cedebc
commit ca390c81e1
12 changed files with 304 additions and 49 deletions

View file

@ -133,6 +133,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.github.inusedname:AndroidPhotoFilters:1.0.7.2'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.mikepenz:materialdrawer:9.0.1'
}
ext {

View file

@ -156,6 +156,11 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
}
}
@Override
public boolean colorCodeAccounts() {
return mActivatedAccounts.size() > 1;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
@ -282,21 +287,23 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
getListItemAdapter().notifyDataSetChanged();
return;
}
for (final Account account : xmppConnectionService.getAccounts()) {
if (account.isEnabled()) {
for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInContactList() &&
!filterContacts.contains(contact.getJid().asBareJid().toString())
&& contact.match(this, needle)) {
getListItems().add(contact);
}
final var accounts = new ArrayList<Account>();
for (final var account : xmppConnectionService.getAccounts()) {
if (mActivatedAccounts.contains(account.getJid().asBareJid().toEscapedString())) accounts.add(account);
}
for (final Account account : accounts) {
for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInContactList() &&
!filterContacts.contains(contact.getJid().asBareJid().toString())
&& contact.match(this, needle)) {
getListItems().add(contact);
}
}
final Contact self = new Contact(account.getSelfContact());
self.setSystemName("Note to Self");
if (self.match(this, needle)) {
getListItems().add(self);
}
final Contact self = new Contact(account.getSelfContact());
self.setSystemName("Note to Self");
if (self.match(this, needle)) {
getListItems().add(self);
}
}
Collections.sort(getListItems());
@ -383,13 +390,14 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
@Override
protected void onBackendConnected() {
filterContacts();
this.mActivatedAccounts.clear();
final var selected = getIntent().getStringExtra(EXTRA_ACCOUNT);
for (final Account account : xmppConnectionService.getAccounts()) {
if (account.isEnabled()) {
if (account.isEnabled() && (selected == null || selected.equals(account.getJid().asBareJid().toEscapedString()))) {
this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
}
}
filterContacts();
ActivityResult activityResult = this.postponedActivityResult.pop();
if (activityResult != null) {
handleActivityResult(activityResult);

View file

@ -111,6 +111,7 @@ import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ToolbarUtils;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.utils.SignupUtils;
@ -147,6 +148,10 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
public static final int DIALLER_INTEGRATION = 0x5432ff;
public static final int REQUEST_DOWNLOAD_STICKERS = 0xbf8702;
public static final long DRAWER_ALL_CHATS = 1;
public static final long DRAWER_SETTINGS = 2;
public static final long DRAWER_MANAGE_ACCOUNT = 3;
public static final long DRAWER_MANAGE_PHONE_ACCOUNTS = 4;
//secondary fragment (when holding the conversation, must be initialized before refreshing the overview fragment
private static final @IdRes
@ -160,6 +165,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
private Set<Jid> newCapsJids = new HashSet<>();
private int mRequestCode = -1;
private boolean showLastSeen = false;
private com.mikepenz.materialdrawer.widget.AccountHeaderView accountHeader;
private Bundle savedState = null;
private static boolean isViewOrShareIntent(Intent i) {
Log.d(Config.LOGTAG, "action: " + (i == null ? null : i.getAction()));
@ -182,10 +189,63 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
refreshForNewCaps = false;
newCapsJids.clear();
invalidateActionBarTitle();
final var accounts = xmppConnectionService.getAccounts();
final var inHeader = new HashSet<>();
for (final var p : ImmutableList.copyOf(accountHeader.getProfiles())) {
if (p instanceof com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem) continue;
if (accounts.contains(p.getTag()) || (accounts.size() > 1 && p.getTag() == null)) {
inHeader.add(p.getTag());
} else {
accountHeader.removeProfile(p);
}
}
if (accounts.size() > 1 && !inHeader.contains(null)) {
final var all = new com.mikepenz.materialdrawer.model.ProfileDrawerItem();
all.setIdentifier(100);
com.mikepenz.materialdrawer.model.interfaces.DescribableKt.setDescriptionText(all, "All Accounts");
com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(all, R.drawable.main_logo);
accountHeader.addProfile(all, 0);
}
var hasPhoneAccounts = false;
accountHeader.removeProfileByIdentifier(DRAWER_MANAGE_PHONE_ACCOUNTS);
outer:
for (Account account : xmppConnectionService.getAccounts()) {
for (Contact contact : account.getRoster().getContacts()) {
if (contact.getPresences().anyIdentity("gateway", "pstn")) {
hasPhoneAccounts = true;
final var phoneAccounts = new com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem();
phoneAccounts.setIdentifier(DRAWER_MANAGE_PHONE_ACCOUNTS);
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(phoneAccounts, "Manage Phone Accounts");
com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(phoneAccounts, R.drawable.ic_call_24dp);
accountHeader.addProfile(phoneAccounts, accountHeader.getProfiles().size() - 1);
break outer;
}
}
}
int id = 101;
for (final var a : accounts) {
final var p = new com.mikepenz.materialdrawer.model.ProfileDrawerItem();
p.setIdentifier(id++);
p.setTag(a);
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(p, a.getDisplayName());
com.mikepenz.materialdrawer.model.interfaces.DescribableKt.setDescriptionText(p, a.getJid().asBareJid().toString());
com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconDrawable(p, xmppConnectionService.getAvatarService().get(a, (int) getResources().getDimension(R.dimen.avatar_on_drawer), false));
if (inHeader.contains(a)) {
accountHeader.updateProfile(p);
} else {
accountHeader.addProfile(p, accountHeader.getProfiles().size() - (hasPhoneAccounts ? 2 : 1));
}
}
}
@Override
protected void onBackendConnected() {
final var useSavedState = savedState;
savedState = null;
if (performRedirectIfNecessary(true)) {
return;
}
@ -217,6 +277,135 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
}
showDialogsIfMainIsOverview();
if (accountHeader != null) {
refreshUiReal();
return;
}
accountHeader = new com.mikepenz.materialdrawer.widget.AccountHeaderView(this);
final var manageAccount = new com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem();
manageAccount.setIdentifier(DRAWER_MANAGE_ACCOUNT);
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(manageAccount, "Manage Accounts");
com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(manageAccount, R.drawable.ic_settings_24dp);
accountHeader.addProfiles(manageAccount);
final var item = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
item.setIdentifier(DRAWER_ALL_CHATS);
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(item, "All Chats");
binding.drawer.getItemAdapter().add(item);
final var settings = new com.mikepenz.materialdrawer.model.PrimaryDrawerItem();
settings.setIdentifier(DRAWER_SETTINGS);
settings.setSelectable(false);
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(settings, "Settings");
com.mikepenz.materialdrawer.model.interfaces.IconableKt.setIconRes(settings, R.drawable.ic_settings_24dp);
com.mikepenz.materialdrawer.util.MaterialDrawerSliderViewExtensionsKt.addStickyDrawerItems(binding.drawer, settings);
refreshUiReal();
if (useSavedState != null) binding.drawer.setSavedInstance(useSavedState);
accountHeader.attachToSliderView(binding.drawer);
if (useSavedState != null) accountHeader.withSavedInstance(useSavedState);
if (binding.drawer.getSelectedItemPosition() < 1) {
binding.drawer.setSelectedItemIdentifier(DRAWER_ALL_CHATS);
}
binding.drawer.setOnDrawerItemClickListener((v, drawerItem, pos) -> {
final var id = drawerItem.getIdentifier();
if (id == DRAWER_SETTINGS) {
startActivity(new Intent(this, eu.siacs.conversations.ui.activity.SettingsActivity.class));
}
return false;
});
accountHeader.setOnAccountHeaderListener((v, profile, isCurrent) -> {
final var id = profile.getIdentifier();
if (isCurrent) return false; // Ignore switching to already selected profile
if (id == DRAWER_MANAGE_ACCOUNT) {
final Account account = (Account) accountHeader.getActiveProfile().getTag();
if (account == null) {
AccountUtils.launchManageAccounts(this);
} else {
switchToAccount(account);
}
return false;
}
if (id == DRAWER_MANAGE_PHONE_ACCOUNTS) {
final String[] permissions;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
permissions = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.BLUETOOTH_CONNECT};
} else {
permissions = new String[]{Manifest.permission.RECORD_AUDIO};
}
requestPermissions(permissions, REQUEST_MICROPHONE);
return false;
}
// Clicked on an actual profile
if (profile.getTag() == null) {
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(manageAccount, "Manage Accounts");
} else {
com.mikepenz.materialdrawer.model.interfaces.NameableKt.setNameText(manageAccount, "Manage Account");
}
accountHeader.updateProfile(manageAccount);
final var fm = getFragmentManager();
while (fm.getBackStackEntryCount() > 0) {
try {
fm.popBackStackImmediate();
} catch (IllegalStateException e) {
break;
}
}
refreshUi();
return false;
});
accountHeader.setOnAccountHeaderProfileImageListener((v, profile, isCurrent) -> {
if (isCurrent) {
final Account account = (Account) accountHeader.getActiveProfile().getTag();
if (account == null) {
AccountUtils.launchManageAccounts(this);
} else {
switchToAccount(account);
}
}
return false;
});
}
@Override
public boolean colorCodeAccounts() {
if (accountHeader != null) {
final var active = accountHeader.getActiveProfile();
if (active != null && active.getTag() != null) return false;
}
return super.colorCodeAccounts();
}
@Override
public void populateWithOrderedConversations(List<Conversation> list) {
super.populateWithOrderedConversations(list);
if (accountHeader == null || accountHeader.getActiveProfile() == null) return;
if (accountHeader.getActiveProfile().getTag() != null) {
final var selected = ((Account) accountHeader.getActiveProfile().getTag()).getUuid();
for (final var c : ImmutableList.copyOf(list)) {
if (!selected.equals(c.getAccount().getUuid())) {
list.remove(c);
}
}
}
}
@Override
public void launchStartConversation() {
StartConversationActivity.launch(this, (Account) accountHeader.getActiveProfile().getTag());
}
private boolean performRedirectIfNecessary(boolean noAnimation) {
@ -522,6 +711,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedState = savedInstanceState;
ConversationMenuConfigurator.reloadFeatures(this);
OmemoSetting.load(this);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations);
@ -787,9 +977,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
@Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
public void onSaveInstanceState(Bundle savedInstanceState) {
final Intent pendingIntent = pendingViewIntent.peek();
savedInstanceState.putParcelable("intent", pendingIntent != null ? pendingIntent : getIntent());
savedInstanceState = binding.drawer.saveInstanceState(savedInstanceState);
savedInstanceState = accountHeader.saveInstanceState(savedInstanceState);
super.onSaveInstanceState(savedInstanceState);
}

View file

@ -287,7 +287,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversations_overview, container, false);
this.binding.fab.setOnClickListener((view) -> StartConversationActivity.launch(getActivity()));
this.binding.fab.setOnClickListener((view) -> activity.launchStartConversation());
this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations);
this.conversationsAdapter.setConversationClickListener((view, conversation) -> {
@ -503,7 +503,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
Log.d(Config.LOGTAG,"ConversationsOverviewFragment.refresh() skipped updated because view binding or activity was null");
return;
}
this.activity.xmppConnectionService.populateWithOrderedConversations(this.conversations);
this.activity.populateWithOrderedConversations(this.conversations);
Conversation removed = this.swipedConversation.peek();
if (removed != null) {
if (removed.isRead()) {

View file

@ -127,6 +127,7 @@ public class StartConversationActivity extends XmppActivity
"contact_list_integration_consent";
public static final String EXTRA_INVITE_URI = "eu.siacs.conversations.invite_uri";
public static final String EXTRA_ACCOUNT_FILTER = "account_filter";
private final int REQUEST_SYNC_CONTACTS = 0x28cf;
private final int REQUEST_CREATE_CONFERENCE = 0x39da;
@ -282,10 +283,24 @@ public class StartConversationActivity extends XmppActivity
}
public static void launch(Context context) {
launch(context, null);
}
public static void launch(Context context, final Account account) {
final Intent intent = new Intent(context, StartConversationActivity.class);
if (account != null) {
intent.putExtra(
EXTRA_ACCOUNT_FILTER,
account.getJid().asBareJid().toEscapedString());
}
context.startActivity(intent);
}
@Override
public boolean colorCodeAccounts() {
return mActivatedAccounts.size() > 1;
}
private static Intent createLauncherIntent(Context context) {
final Intent intent = new Intent(context, StartConversationActivity.class);
intent.setAction(Intent.ACTION_MAIN);
@ -365,9 +380,10 @@ public class StartConversationActivity extends XmppActivity
if (intent.getBooleanExtra("init", false)) {
pendingViewIntent.push(intent);
}
if (isViewIntent(intent)) {
} else if(intent.hasExtra(EXTRA_ACCOUNT_FILTER)) {
pendingViewIntent.push(intent);
setIntent(intent);
} else if (isViewIntent(intent)) {
pendingViewIntent.push(intent);
createdByViewIntent = true;
setIntent(createLauncherIntent(this));
@ -1175,10 +1191,16 @@ public class StartConversationActivity extends XmppActivity
mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
this.mPostponedActivityResult = null;
}
var intent = pendingViewIntent.pop();
if (intent == null) intent = getIntent();
this.mActivatedAccounts.clear();
this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService));
final String accountFilterJid = intent == null ? null : intent.getStringExtra(EXTRA_ACCOUNT_FILTER);
if (accountFilterJid == null) {
this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService));
} else {
this.mActivatedAccounts.add(accountFilterJid);
}
configureHomeButton();
Intent intent = pendingViewIntent.pop();
/* // Better Onboarding later
final boolean onboardingCancel = xmppConnectionService.getPreferences().getString("onboarding_action", "").equals("cancel");
@ -1370,36 +1392,37 @@ public class StartConversationActivity extends XmppActivity
protected void filterContacts(String needle) {
this.contacts.clear();
ArrayList<ListItem.Tag> tags = new ArrayList<>();
final List<Account> accounts = xmppConnectionService.getAccounts();
final var accounts = new ArrayList<Account>();
for (final var account : xmppConnectionService.getAccounts()) {
if (mActivatedAccounts.contains(account.getJid().asBareJid().toEscapedString())) accounts.add(account);
}
boolean foundSopranica = false;
for (final Account account : accounts) {
if (account.isEnabled()) {
for (Contact contact : account.getRoster().getContacts()) {
Presence.Status s = contact.getShownStatus();
if (contact.showInContactList()
&& contact.match(this, needle)
&& (!this.mHideOfflineContacts
|| (needle != null && !needle.trim().isEmpty())
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
this.contacts.add(contact);
tags.addAll(contact.getTags(this));
}
for (Contact contact : account.getRoster().getContacts()) {
Presence.Status s = contact.getShownStatus();
if (contact.showInContactList()
&& contact.match(this, needle)
&& (!this.mHideOfflineContacts
|| (needle != null && !needle.trim().isEmpty())
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
this.contacts.add(contact);
tags.addAll(contact.getTags(this));
}
}
final Contact self = new Contact(account.getSelfContact());
self.setSystemName("Note to Self");
if (self.match(this, needle)) {
this.contacts.add(self);
}
final Contact self = new Contact(account.getSelfContact());
self.setSystemName("Note to Self");
if (self.match(this, needle)) {
this.contacts.add(self);
}
for (Bookmark bookmark : account.getBookmarks()) {
if (bookmark.match(this, needle)) {
if (bookmark.getJid().toString().equals("support@conference.monocles.eu")) {
foundSopranica = true;
}
this.contacts.add(bookmark);
tags.addAll(bookmark.getTags(this));
for (Bookmark bookmark : account.getBookmarks()) {
if (bookmark.match(this, needle)) {
if (bookmark.getJid().toString().equals("support@conference.monocles.eu")) {
foundSopranica = true;
}
this.contacts.add(bookmark);
tags.addAll(bookmark.getTags(this));
}
}
}

View file

@ -565,6 +565,18 @@ public abstract class XmppActivity extends ActionBarActivity {
startActivity(intent);
}
public boolean colorCodeAccounts() {
return xmppConnectionService.getAccounts().size() > 1;
}
public void populateWithOrderedConversations(List<Conversation> list) {
xmppConnectionService.populateWithOrderedConversations(list);
}
public void launchStartConversation() {
StartConversationActivity.launch(this);
}
public void switchToConversation(Conversation conversation) {
switchToConversation(conversation, null);
}

View file

@ -83,7 +83,7 @@ public class ConversationAdapter
R.drawable.background_selected_item_conversation);
viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurfaceDim));
} else {
if (activity.xmppConnectionService != null && activity.xmppConnectionService.getAccounts().size() > 1) {
if (activity.xmppConnectionService != null && activity.colorCodeAccounts()) {
viewHolder.binding.frame.setBackgroundColor(conversation.getAccount().getColor(activity.isDark()));
} else {
viewHolder.binding.frame.setBackgroundColor(

View file

@ -81,7 +81,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
if (view.isActivated()) {
Log.d(Config.LOGTAG,"item "+item.getDisplayName()+" is activated");
}
if (activity.xmppConnectionService != null && activity.xmppConnectionService.getAccounts().size() > 1) {
if (activity.colorCodeAccounts()) {
innerView.setBackgroundColor(item.getAccount().getColor(activity.isDark()));
}
//view.setBackground(StyledAttributes.getDrawable(view.getContext(),R.attr.list_item_background));

View file

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -71,4 +77,13 @@
android:state_checked="true"
android:visibility="gone" />
</RelativeLayout>
<com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
android:id="@+id/drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true" />
</androidx.drawerlayout.widget.DrawerLayout>
</layout>

View file

@ -21,4 +21,5 @@
android:text="@string/start_chat"
app:icon="@drawable/ic_chat_24dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View file

@ -30,6 +30,7 @@
<dimen name="image_radius">6dp</dimen>
<dimen name="avatar_on_details_screen_size">56dp</dimen>
<dimen name="avatar_on_conversation_overview">56dp</dimen>
<dimen name="avatar_on_drawer">128dp</dimen>
<dimen name="input_label_vertical_spacing">4dp</dimen>
<dimen name="input_label_horizontal_spacing">4dp</dimen>

View file

@ -50,6 +50,8 @@
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_light_inversePrimary</item>
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
<item name="ic_chat_unselected" type="reference">@drawable/outline_chat_black_24</item>
<item name="ic_chat_selected" type="reference">@drawable/chat_selected_black_24</item>