Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
* SPDX-FileCopyrightText: 2017 Mario Danic <mario@lovelyhq.com>
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH
* SPDX-FileCopyrightText: 2025 TSI-mc <surinder.kumar@t-systems.com>
* SPDX-FileCopyrightText: 2026 Daniele Verducci <daniele.verducci@ichibi.eu>
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
*/
package com.owncloud.android.ui.activity;

import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
Expand All @@ -29,6 +28,7 @@
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
import com.nextcloud.client.account.User;
import com.nextcloud.client.di.Injectable;
import com.nextcloud.client.preferences.AppPreferences;
Expand All @@ -48,13 +48,12 @@
import com.owncloud.android.ui.events.TokenPushEvent;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.PushUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;

import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

import javax.inject.Inject;

Expand All @@ -66,7 +65,6 @@
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
import kotlin.Unit;

/**
Expand Down Expand Up @@ -125,8 +123,6 @@ public void onCreate(Bundle savedInstanceState) {
viewThemeUtils.files.themeActionBar(this, actionBar);
}

binding.userinfoList.setAdapter(new UserInfoAdapter(null, viewThemeUtils));

if (userInfo != null) {
populateUserInfoUi(userInfo);
} else {
Expand Down Expand Up @@ -172,7 +168,7 @@ public boolean onOptionsItemSelected(MenuItem item) {
}

private void setMultiListLoadingMessage() {
binding.userinfoList.setVisibility(View.GONE);
binding.userinfoListContainer.setVisibility(View.GONE);
binding.emptyList.emptyListView.setVisibility(View.GONE);
}

Expand All @@ -183,7 +179,7 @@ private void setErrorMessageForMultiList(String headline, String message, @Drawa

binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE);
binding.userinfoList.setVisibility(View.GONE);
binding.userinfoListContainer.setVisibility(View.GONE);
binding.loadingContent.setVisibility(View.GONE);
}

Expand Down Expand Up @@ -265,10 +261,12 @@ private void populateUserInfoUi(UserInfo userInfo) {
binding.userinfoFullName.setText(userInfo.getDisplayName());
}

if (TextUtils.isEmpty(userInfo.getPhone()) && TextUtils.isEmpty(userInfo.getEmail())
final boolean userInfoEmpty = TextUtils.isEmpty(userInfo.getPhone()) && TextUtils.isEmpty(userInfo.getEmail())
&& TextUtils.isEmpty(userInfo.getAddress()) && TextUtils.isEmpty(userInfo.getTwitter())
&& TextUtils.isEmpty(userInfo.getWebsite())) {
binding.userinfoList.setVisibility(View.GONE);
&& TextUtils.isEmpty(userInfo.getWebsite());
final boolean groupInfoEmpty = userInfo.getGroups() == null || userInfo.getGroups().isEmpty();
if (userInfoEmpty && groupInfoEmpty) {
binding.userinfoListContainer.setVisibility(View.GONE);
binding.loadingContent.setVisibility(View.GONE);
binding.emptyList.emptyListView.setVisibility(View.VISIBLE);

Expand All @@ -278,34 +276,72 @@ private void populateUserInfoUi(UserInfo userInfo) {
binding.loadingContent.setVisibility(View.VISIBLE);
binding.emptyList.emptyListView.setVisibility(View.GONE);

if (binding.userinfoList.getAdapter() instanceof UserInfoAdapter) {
binding.userinfoList.setAdapter(new UserInfoAdapter(createUserInfoDetails(userInfo), viewThemeUtils));
if (!userInfoEmpty) {
viewThemeUtils.platform.colorTextView(binding.userinfoListTitle, ColorRole.PRIMARY);
showUserInfoDetails(binding.userinfoList, userInfo);
binding.userinfoList.setVisibility(View.VISIBLE);
binding.userinfoListTitle.setVisibility(View.VISIBLE);
} else {
binding.userinfoList.setVisibility(View.GONE);
binding.userinfoListTitle.setVisibility(View.GONE);
}

if (!groupInfoEmpty) {
viewThemeUtils.platform.colorTextView(binding.groupinfoListTitle, ColorRole.PRIMARY);
showGroupsInfoDetails(binding.groupinfoList, userInfo);
binding.groupinfoList.setVisibility(View.VISIBLE);
binding.groupinfoListTitle.setVisibility(View.VISIBLE);
} else {
binding.groupinfoList.setVisibility(View.GONE);
binding.groupinfoListTitle.setVisibility(View.GONE);
}

binding.loadingContent.setVisibility(View.GONE);
binding.userinfoList.setVisibility(View.VISIBLE);
binding.userinfoListContainer.setVisibility(View.VISIBLE);
}
}

private List<UserInfoDetailsItem> createUserInfoDetails(UserInfo userInfo) {
List<UserInfoDetailsItem> 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);
addToListIfNeeded(result, R.drawable.ic_map_marker, userInfo.getAddress(), R.string.user_info_address);
addToListIfNeeded(result, R.drawable.ic_web, DisplayUtils.beautifyURL(userInfo.getWebsite()),
R.string.user_info_website);
addToListIfNeeded(result, R.drawable.ic_twitter, DisplayUtils.beautifyTwitterHandle(userInfo.getTwitter()),
R.string.user_info_twitter);
private void showUserInfoDetails(ViewGroup container, UserInfo userInfo) {
container.removeAllViews();
addToListIfNeeded(container, R.drawable.ic_phone, userInfo.getPhone(), R.string.user_info_phone);
addToListIfNeeded(container, R.drawable.ic_email, userInfo.getEmail(), R.string.user_info_email);
addToListIfNeeded(container, R.drawable.ic_map_marker, userInfo.getAddress(), R.string.user_info_address);
addToListIfNeeded(container, R.drawable.ic_web, DisplayUtils.beautifyURL(userInfo.getWebsite()),
R.string.user_info_website);
addToListIfNeeded(container, R.drawable.ic_twitter, DisplayUtils.beautifyTwitterHandle(userInfo.getTwitter()),
R.string.user_info_twitter);
}

return result;
private void showGroupsInfoDetails(ViewGroup container, UserInfo userInfo) {
container.removeAllViews();
if (userInfo.getGroups() != null) {
final ArrayList<String> sortedGroups = new ArrayList<>(userInfo.getGroups());
Collections.sort(sortedGroups);
addToListIfNeeded(container, R.drawable.ic_group, String.join(", ", sortedGroups),
R.string.user_info_groups);
}
}

private void addToListIfNeeded(List<UserInfoDetailsItem> info, @DrawableRes int icon, String text,
private void addToListIfNeeded(ViewGroup container, @DrawableRes int icon, String text,
@StringRes int contentDescriptionInt) {
if (!TextUtils.isEmpty(text)) {
info.add(new UserInfoDetailsItem(icon, text, getResources().getString(contentDescriptionInt)));
}
if (TextUtils.isEmpty(text))
return;

final UserInfoDetailsTableItemBinding binding = UserInfoDetailsTableItemBinding.inflate(
getLayoutInflater(),
container,
false
);
binding.icon.setImageResource(icon);
binding.text.setText(text);
binding.icon.setContentDescription(getString(contentDescriptionInt));
viewThemeUtils.platform.colorImageView(binding.icon, ColorRole.PRIMARY);

// Separator
if (container.getChildCount() > 0)
binding.getRoot().setBackgroundResource(R.drawable.rounded_corners_group_item_background);

container.addView(binding.getRoot());
}

public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) {
Expand Down Expand Up @@ -361,67 +397,4 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
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<UserInfoAdapter.ViewHolder> {
protected List<UserInfoDetailsItem> 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<UserInfoDetailsItem> displayList, ViewThemeUtils viewThemeUtils) {
mDisplayList = displayList == null ? new LinkedList<>() : displayList;
this.viewThemeUtils = viewThemeUtils;
}

@SuppressLint("NotifyDataSetChanged")
public void setData(List<UserInfoDetailsItem> 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();
}
}
}
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/rounded_corners_group_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2026 Daniele Verducci <daniele.verducci@ichibi.eu>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/bg_default"/>
<stroke android:width="1dp" android:color="@color/grey_200" />
<corners android:radius="4dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2026 Daniele Verducci <daniele.verducci@ichibi.eu>
~ SPDX-License-Identifier: AGPL-3.0-or-later
-->

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="0dp" android:left="-2dp" android:right="-2dp" android:bottom="-2dp">
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/grey_200"/>
</shape>
</item>
</layer-list>
4 changes: 3 additions & 1 deletion app/src/main/res/layout/user_info_details_table_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/iconized_single_line_item_layout_height"
android:layout_height="wrap_content"
android:paddingTop="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_padding"
android:orientation="horizontal">

<ImageView
Expand Down
56 changes: 49 additions & 7 deletions app/src/main/res/layout/user_info_layout.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
~
~ SPDX-FileCopyrightText: 2017 Andy Scherzinger <info@andy-scherzinger.de>
~ SPDX-FileCopyrightText: 2017 Nextcloud
~ SPDX-FileCopyrightText: 2026 Daniele Verducci <daniele.verducci@ichibi.eu>
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
Expand Down Expand Up @@ -84,16 +85,57 @@

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/userinfo_list"
<ScrollView
android:id="@+id/userinfo_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="3"
tools:listitem="@layout/user_info_details_table_item"
tools:visibility="gone" />
tools:visibility="gone" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/standard_margin"
android:clipToPadding="false"
android:orientation="vertical">

<TextView
android:id="@+id/userinfo_list_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_margin"
android:textAppearance="@style/NextcloudTextAppearanceSectionTitle"
android:text="@string/user_info_profile"/>

<LinearLayout
android:id="@+id/userinfo_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/rounded_corners_group_background"
android:elevation="@dimen/cardview_default_elevation"/>

<TextView
android:id="@+id/groupinfo_list_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_margin"
android:layout_marginBottom="@dimen/standard_margin"
android:textAppearance="@style/NextcloudTextAppearanceSectionTitle"
android:text="@string/user_info_groups"/>

<LinearLayout
android:id="@+id/groupinfo_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/rounded_corners_group_background"
android:elevation="@dimen/cardview_default_elevation"/>

</LinearLayout>

</ScrollView>

<include
android:id="@+id/emptyList"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,8 @@
<string name="user_info_address">Address</string>
<string name="user_info_website">Website</string>
<string name="user_info_twitter">Twitter</string>
<string name="user_info_profile">Profile</string>
<string name="user_info_groups">Groups</string>

<string name="user_information_retrieval_error">Error retrieving user information</string>

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@

<style name="NextcloudTextAppearanceMedium" parent="@style/TextAppearance.AppCompat.Medium"></style>

<style name="NextcloudTextAppearanceSectionTitle" parent="@style/TextAppearance.AppCompat.Small">
<item name="android:textColor">@color/primary</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">@dimen/activity_list_item_title_header_text_size</item>
</style>

<style name="Widget.App.Login.TextInputLayout" parent="Widget.Material3.TextInputLayout.OutlinedBox">
<item name="materialThemeOverlay">@style/ThemeOverlay.App.Login.TextInputLayout</item>
<item name="shapeAppearance">@style/ShapeAppearance.Material3.SmallComponent</item>
Expand Down
Loading