diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index ae7e644965b3..9743507c85f2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -8,21 +8,19 @@ * SPDX-FileCopyrightText: 2017 Mario Danic * SPDX-FileCopyrightText: 2017 Nextcloud GmbH * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 Daniele Verducci > list = new HashMap<>(); + list.put(UserInfoAdapter.SECTION_USERINFO, createUserInfoDetails(userInfo)); + list.put(UserInfoAdapter.SECTION_GROUPS, createGroupInfoDetails(userInfo)); + binding.userinfoList.setAdapter(new UserInfoAdapter(this, list, viewThemeUtils)); binding.loadingContent.setVisibility(View.GONE); binding.userinfoList.setVisibility(View.VISIBLE); } } - private List createUserInfoDetails(UserInfo userInfo) { - List result = new LinkedList<>(); + private LinkedList createUserInfoDetails(UserInfo userInfo) { + LinkedList result = new LinkedList<>(); addToListIfNeeded(result, R.drawable.ic_phone, userInfo.getPhone(), R.string.user_info_phone); addToListIfNeeded(result, R.drawable.ic_email, userInfo.getEmail(), R.string.user_info_email); @@ -297,10 +297,23 @@ private List createUserInfoDetails(UserInfo userInfo) { return result; } - private void addToListIfNeeded(List info, @DrawableRes int icon, String text, + private LinkedList createGroupInfoDetails(UserInfo userInfo) { + LinkedList result = new LinkedList<>(); + + if (userInfo.getGroups() != null) { + final ArrayList sortedGroups = new ArrayList<>(userInfo.getGroups()); + Collections.sort(sortedGroups); + addToListIfNeeded(result, R.drawable.ic_group, String.join(", ", sortedGroups), + R.string.user_info_groups); + } + + return result; + } + + private void addToListIfNeeded(List info, @DrawableRes int icon, String text, @StringRes int contentDescriptionInt) { if (!TextUtils.isEmpty(text)) { - info.add(new UserInfoDetailsItem(icon, text, getResources().getString(contentDescriptionInt))); + info.add(new UserInfoAdapter.UserInfoDetailsItem(icon, text, getResources().getString(contentDescriptionInt))); } } @@ -358,66 +371,4 @@ public void onMessageEvent(TokenPushEvent event) { PushUtils.pushRegistrationToServer(getUserAccountManager(), preferences.getPushToken()); } - - protected static class UserInfoDetailsItem { - @DrawableRes public int icon; - public String text; - public String iconContentDescription; - - public UserInfoDetailsItem(@DrawableRes int icon, String text, String iconContentDescription) { - this.icon = icon; - this.text = text; - this.iconContentDescription = iconContentDescription; - } - } - - protected static class UserInfoAdapter extends RecyclerView.Adapter { - protected List mDisplayList; - protected ViewThemeUtils viewThemeUtils; - - public static class ViewHolder extends RecyclerView.ViewHolder { - protected UserInfoDetailsTableItemBinding binding; - - public ViewHolder(UserInfoDetailsTableItemBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - } - - public UserInfoAdapter(List displayList, ViewThemeUtils viewThemeUtils) { - mDisplayList = displayList == null ? new LinkedList<>() : displayList; - this.viewThemeUtils = viewThemeUtils; - } - - @SuppressLint("NotifyDataSetChanged") - public void setData(List displayList) { - mDisplayList = displayList == null ? new LinkedList<>() : displayList; - notifyDataSetChanged(); - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder( - UserInfoDetailsTableItemBinding.inflate( - LayoutInflater.from(parent.getContext()), - parent, - false) - ); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - UserInfoDetailsItem item = mDisplayList.get(position); - holder.binding.icon.setImageResource(item.icon); - holder.binding.text.setText(item.text); - holder.binding.icon.setContentDescription(item.iconContentDescription); - viewThemeUtils.platform.colorImageView(holder.binding.icon); - } - - @Override - public int getItemCount() { - return mDisplayList.size(); - } - } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UserInfoAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UserInfoAdapter.kt new file mode 100644 index 000000000000..99b2564d1a9b --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UserInfoAdapter.kt @@ -0,0 +1,129 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-FileCopyrightText: 2026 Daniele Verducci + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.DrawableRes +import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter +import com.afollestad.sectionedrecyclerview.SectionedViewHolder +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.owncloud.android.R +import com.owncloud.android.databinding.UserInfoDetailsTableItemBinding +import com.owncloud.android.databinding.UserInfoDetailsTableItemTitleBinding +import com.owncloud.android.utils.theme.ViewThemeUtils + +class UserInfoAdapter(val context: Context, val displayList: Map>, val viewThemeUtils: ViewThemeUtils) : + SectionedRecyclerViewAdapter() { + companion object { + const val SECTION_USERINFO = 0 + const val SECTION_GROUPS = 1 + } + + data class UserInfoDetailsItem( + @field:DrawableRes @param:DrawableRes var icon: Int, + var text: String?, + var iconContentDescription: String? + ) + + class HeaderSectionedViewHolder(var binding: UserInfoDetailsTableItemTitleBinding) : + SectionedViewHolder(binding.getRoot()) + + class UserInfoSectionedViewHolder(var binding: UserInfoDetailsTableItemBinding) : + SectionedViewHolder(binding.getRoot()) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionedViewHolder = when (viewType) { + VIEW_TYPE_HEADER -> HeaderSectionedViewHolder( + UserInfoDetailsTableItemTitleBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + else -> UserInfoSectionedViewHolder( + UserInfoDetailsTableItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), + ) + } + + private enum class ItemPosition { + SINGLE, FIRST, MIDDLE, LAST; + + fun backgroundRes(): Int = when (this) { + SINGLE -> R.drawable.rounded_corners_listitem_single_background + FIRST -> R.drawable.rounded_corners_listitem_first_background + LAST -> R.drawable.rounded_corners_listitem_last_background + MIDDLE -> R.drawable.rounded_corners_listitem_center_background + } + } + + private fun resolvePosition(position: Int, count: Int): ItemPosition { + val isFirst = (position == 0) + val isLast = (position == count - 1) + + return when { + isFirst && isLast -> ItemPosition.SINGLE + isFirst -> ItemPosition.FIRST + isLast -> ItemPosition.LAST + else -> ItemPosition.MIDDLE + } + } + + override fun onBindViewHolder( + holder: SectionedViewHolder, + section: Int, + relativePosition: Int, + absolutePosition: Int + ) { + val count = getItemCount(section) + val positionType = resolvePosition(relativePosition, count) + val uiHolder = holder as UserInfoSectionedViewHolder + val item = displayList[section]?.get(relativePosition) + + // Set background + uiHolder.binding.root.setBackgroundResource( + positionType.backgroundRes() + ) + + // Populate views + uiHolder.binding.icon.setImageResource(item?.icon ?: R.drawable.ic_user_outline) + uiHolder.binding.text.text = item?.text + uiHolder.binding.icon.contentDescription = item?.iconContentDescription + viewThemeUtils.platform.colorImageView(holder.binding.icon, ColorRole.PRIMARY) + } + + override fun getSectionCount(): Int = 2 + + override fun getItemCount(section: Int): Int = when (section) { + SECTION_GROUPS -> displayList[SECTION_GROUPS]?.size ?: 0 + SECTION_USERINFO -> displayList[SECTION_USERINFO]?.size ?: 0 + else -> 0 + } + + override fun onBindHeaderViewHolder( + holder: SectionedViewHolder?, + section: Int, + expanded: Boolean + ) { + val title = when (section) { + SECTION_GROUPS -> context.getString(R.string.user_info_groups) + SECTION_USERINFO -> context.getString(R.string.user_info_profile) + else -> "" + } + val titleHolder = holder as HeaderSectionedViewHolder + titleHolder.binding.root.text = title + viewThemeUtils.platform.colorTextView(titleHolder.binding.root, ColorRole.PRIMARY) + } + + override fun onBindFooterViewHolder(p0: SectionedViewHolder?, p1: Int) {} +} diff --git a/app/src/main/res/drawable/rounded_corners_listitem_center_background.xml b/app/src/main/res/drawable/rounded_corners_listitem_center_background.xml new file mode 100644 index 000000000000..4ebd24ac9ac4 --- /dev/null +++ b/app/src/main/res/drawable/rounded_corners_listitem_center_background.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_corners_listitem_first_background.xml b/app/src/main/res/drawable/rounded_corners_listitem_first_background.xml new file mode 100644 index 000000000000..8371467569a1 --- /dev/null +++ b/app/src/main/res/drawable/rounded_corners_listitem_first_background.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_corners_listitem_last_background.xml b/app/src/main/res/drawable/rounded_corners_listitem_last_background.xml new file mode 100644 index 000000000000..aec7a236b418 --- /dev/null +++ b/app/src/main/res/drawable/rounded_corners_listitem_last_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_corners_listitem_single_background.xml b/app/src/main/res/drawable/rounded_corners_listitem_single_background.xml new file mode 100644 index 000000000000..9aacdc72c76a --- /dev/null +++ b/app/src/main/res/drawable/rounded_corners_listitem_single_background.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/user_info_details_table_item.xml b/app/src/main/res/layout/user_info_details_table_item.xml index c53b4169af77..1504dfaf9e52 100644 --- a/app/src/main/res/layout/user_info_details_table_item.xml +++ b/app/src/main/res/layout/user_info_details_table_item.xml @@ -4,14 +4,18 @@ ~ ~ SPDX-FileCopyrightText: 2018 Andy Scherzinger ~ SPDX-FileCopyrightText: 2018 Nextcloud + ~ SPDX-FileCopyrightText: 2026 Daniele Verducci ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> + android:layout_height="wrap_content" + android:paddingTop="@dimen/standard_padding" + android:paddingBottom="@dimen/standard_padding" + android:orientation="horizontal" + android:background="@drawable/rounded_corners_listitem_single_background"> + + diff --git a/app/src/main/res/layout/user_info_layout.xml b/app/src/main/res/layout/user_info_layout.xml index fb29fe8a34eb..a7cac58fdad3 100644 --- a/app/src/main/res/layout/user_info_layout.xml +++ b/app/src/main/res/layout/user_info_layout.xml @@ -88,6 +88,7 @@ android:id="@+id/userinfo_list" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_margin="@dimen/standard_margin" android:orientation="vertical" android:visibility="gone" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65591ae894c2..330109ec38d1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -839,6 +839,8 @@ Address Website Twitter + Profile + Groups Error retrieving user information diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d3531bc920cc..af8410de3cdd 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -338,6 +338,12 @@ + +