Skip to content

Commit ada2b5f

Browse files
committed
feat: implement UI for listing oCIS shares
Signed-off-by: Jorge Aguado Recio <jaguado@izertis.com>
1 parent 7e7dc46 commit ada2b5f

6 files changed

Lines changed: 262 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2026 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.presentation.sharing
22+
23+
import android.view.LayoutInflater
24+
import android.view.View
25+
import android.view.ViewGroup
26+
import androidx.core.view.isVisible
27+
import androidx.recyclerview.widget.DiffUtil
28+
import androidx.recyclerview.widget.RecyclerView
29+
import com.owncloud.android.R
30+
import com.owncloud.android.databinding.MemberItemBinding
31+
import com.owncloud.android.domain.sharing.shares.model.OCShare
32+
import com.owncloud.android.domain.sharing.shares.model.ShareType
33+
import com.owncloud.android.utils.DisplayUtils
34+
import com.owncloud.android.utils.PreferenceUtils
35+
36+
class OcisSharesAdapter(
37+
private val listener: OcisSharesAdapterListener,
38+
) : RecyclerView.Adapter<OcisSharesAdapter.OcisShareViewHolder>() {
39+
40+
private var shares: List<OCShare> = emptyList()
41+
private var canRemoveShares = false
42+
private var canEditShares = false
43+
44+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OcisShareViewHolder {
45+
val inflater = LayoutInflater.from(parent.context)
46+
val view = inflater.inflate(R.layout.member_item, parent, false)
47+
view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context)
48+
return OcisShareViewHolder(view)
49+
}
50+
51+
override fun onBindViewHolder(holder: OcisShareViewHolder, position: Int) {
52+
val share = shares[position]
53+
54+
holder.binding.apply {
55+
val isGroup = share.shareType == ShareType.GROUP
56+
memberIcon.setImageResource(if (isGroup) R.drawable.ic_group else R.drawable.ic_user)
57+
memberName.text = share.sharedWithDisplayName ?: share.shareWith.orEmpty()
58+
memberName.contentDescription = holder.itemView.context.getString(
59+
if (isGroup) R.string.content_description_member_group else R.string.content_description_member_user,
60+
memberName.text
61+
)
62+
memberRole.text = ""
63+
64+
val hasExpirationDate = share.expirationDate != 0L
65+
expirationCalendarIcon.isVisible = hasExpirationDate
66+
expirationDate.isVisible = hasExpirationDate
67+
if (hasExpirationDate) {
68+
expirationDate.text = DisplayUtils.displayDateToHumanReadable(share.expirationDate.toString())
69+
expirationDate.contentDescription =
70+
holder.itemView.context.getString(R.string.content_description_member_expiration_date, expirationDate.text)
71+
}
72+
73+
editMemberButton.apply {
74+
contentDescription = holder.itemView.context.getString(
75+
R.string.content_description_edit_member_button, memberName.text
76+
)
77+
isVisible = canEditShares
78+
setOnClickListener { listener.onEditShare(share) }
79+
}
80+
81+
removeMemberButton.apply {
82+
contentDescription = holder.itemView.context.getString(
83+
R.string.content_description_remove_member_button, memberName.text
84+
)
85+
isVisible = canRemoveShares
86+
setOnClickListener { listener.onRemoveShare(share) }
87+
}
88+
}
89+
}
90+
91+
override fun getItemCount(): Int = shares.size
92+
93+
fun setShares(
94+
shares: List<OCShare>,
95+
canRemoveShares: Boolean,
96+
canEditShares: Boolean,
97+
) {
98+
val hasPermissionsChanged = this.canEditShares != canEditShares || this.canRemoveShares != canRemoveShares
99+
this.canRemoveShares = canRemoveShares
100+
this.canEditShares = canEditShares
101+
val sortedShares = shares.sortedBy { it.sharedWithDisplayName ?: it.shareWith.orEmpty() }
102+
val diffResult = DiffUtil.calculateDiff(OcisSharesDiffUtil(this.shares, sortedShares, hasPermissionsChanged))
103+
this.shares = sortedShares
104+
diffResult.dispatchUpdatesTo(this)
105+
}
106+
107+
class OcisShareViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
108+
val binding = MemberItemBinding.bind(itemView)
109+
}
110+
111+
interface OcisSharesAdapterListener {
112+
fun onEditShare(share: OCShare)
113+
fun onRemoveShare(share: OCShare)
114+
}
115+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2026 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.presentation.sharing
22+
23+
import androidx.recyclerview.widget.DiffUtil
24+
import com.owncloud.android.domain.sharing.shares.model.OCShare
25+
26+
class OcisSharesDiffUtil(
27+
private val oldList: List<OCShare>,
28+
private val newList: List<OCShare>,
29+
private val hasPermissionsChanged: Boolean,
30+
) : DiffUtil.Callback() {
31+
32+
override fun getOldListSize(): Int = oldList.size
33+
34+
override fun getNewListSize(): Int = newList.size
35+
36+
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
37+
return oldList[oldItemPosition].remoteId == newList[newItemPosition].remoteId
38+
}
39+
40+
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
41+
val oldItem = oldList[oldItemPosition]
42+
val newItem = newList[newItemPosition]
43+
return oldItem.remoteId == newItem.remoteId &&
44+
oldItem.sharedWithDisplayName == newItem.sharedWithDisplayName &&
45+
oldItem.permissions == newItem.permissions &&
46+
oldItem.expirationDate == newItem.expirationDate &&
47+
!hasPermissionsChanged
48+
}
49+
}

‎owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ class ShareActivity : FileActivity(), ShareFragmentListener {
9393
setContentView(binding.root)
9494
setupToolbar(binding.root)
9595
setupFileHeader(binding)
96+
if (file != null && account != null) {
97+
supportFragmentManager.transaction {
98+
replace(R.id.members_fragment_container, ShareOcisFragment.newInstance(file, account!!.name), TAG_SHARE_OCIS_FRAGMENT)
99+
}
100+
}
96101
}
97102

98103
private fun setupLegacyLayout(savedInstanceState: Bundle?) {
@@ -365,6 +370,7 @@ class ShareActivity : FileActivity(), ShareFragmentListener {
365370

366371
companion object {
367372
const val TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT"
373+
const val TAG_SHARE_OCIS_FRAGMENT = "SHARE_OCIS_FRAGMENT"
368374
const val TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT"
369375
const val TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT"
370376
const val TAG_PUBLIC_SHARE_DIALOG_FRAGMENT = "PUBLIC_SHARE_DIALOG_FRAGMENT"
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2026 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.presentation.sharing
22+
23+
import android.os.Bundle
24+
import android.view.LayoutInflater
25+
import android.view.View
26+
import android.view.ViewGroup
27+
import androidx.fragment.app.Fragment
28+
import com.owncloud.android.R
29+
import com.owncloud.android.databinding.MembersFragmentBinding
30+
import com.owncloud.android.domain.files.model.OCFile
31+
import kotlin.apply
32+
33+
class ShareOcisFragment : Fragment() {
34+
private var _binding: MembersFragmentBinding? = null
35+
private val binding get() = _binding!!
36+
37+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
38+
_binding = MembersFragmentBinding.inflate(inflater, container, false)
39+
return binding.root
40+
}
41+
42+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
43+
super.onViewCreated(view, savedInstanceState)
44+
binding.membersTitle.text = getString(R.string.share_with_people_title)
45+
}
46+
47+
override fun onDestroyView() {
48+
super.onDestroyView()
49+
_binding = null
50+
}
51+
52+
companion object {
53+
private const val ARG_FILE = "FILE"
54+
private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME"
55+
56+
fun newInstance(file: OCFile, accountName: String): ShareOcisFragment {
57+
val args = Bundle().apply {
58+
putParcelable(ARG_FILE, file)
59+
putString(ARG_ACCOUNT_NAME, accountName)
60+
}
61+
return ShareOcisFragment().apply {
62+
arguments = args
63+
}
64+
}
65+
}
66+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2026 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.presentation.sharing
22+
23+
import androidx.lifecycle.ViewModel
24+
25+
class ShareOcisViewModel : ViewModel()

‎owncloudApp/src/main/res/values/strings.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@
506506
<string name="share_via_link_edit_permission_read_and_write_label">Download/View/Upload</string>
507507
<string name="share_via_link_edit_permission_upload_only_label">Upload Only (File Drop)</string>
508508
<string name="share_with_title">Share with</string>
509+
<string name="share_with_people_title">Share with people</string>
509510
<string name="share_with_edit_title">Share with %1$s</string>
510511

511512
<string name="share_search">Search</string>

0 commit comments

Comments
 (0)