nextcloud-android/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java

459 lines
18 KiB
Java

/*
* Nextcloud Android client application
*
* @author masensio
* @author Andy Scherzinger
* @author Chris Narkiewicz <hello@ezaquarii.com>
*
* Copyright (C) 2015 ownCloud GmbH
* Copyright (C) 2018 Andy Scherzinger
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* Copyright (C) 2020 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.adapter;
import android.accounts.Account;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.shares.OCShare;
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.ui.TextDrawable;
import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
import com.owncloud.android.ui.dialog.NoteDialogFragment;
import com.owncloud.android.ui.fragment.util.SharingMenuHelper;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ThemeUtils;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Adapter to show a user/group/email/remote in Sharing list in file details view.
*/
public class ShareeListAdapter extends RecyclerView.Adapter<ShareeListAdapter.UserViewHolder>
implements DisplayUtils.AvatarGenerationListener {
private ShareeListAdapterListener listener;
private OCCapability capabilities;
private FragmentManager fragmentManager;
private Context context;
private int accentColor;
private List<OCShare> shares;
private float avatarRadiusDimension;
private OCFile file;
private String userId;
public ShareeListAdapter(FragmentManager fragmentManager, Context context, List<OCShare> shares, Account account,
OCFile file, ShareeListAdapterListener listener, String userId) {
this.context = context;
this.fragmentManager = fragmentManager;
this.shares = shares;
this.listener = listener;
this.file = file;
this.userId = userId;
accentColor = ThemeUtils.primaryAccentColor(context);
capabilities = new FileDataStorageManager(account, context.getContentResolver()).getCapability(account.name);
avatarRadiusDimension = context.getResources().getDimension(R.dimen.user_icon_radius);
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_details_share_user_item, parent, false);
return new UserViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
if (shares != null && shares.size() > position) {
final OCShare share = shares.get(position);
String name = share.getSharedWithDisplayName();
switch (share.getShareType()) {
case GROUP:
name = context.getString(R.string.share_group_clarification, name);
setImage(holder, name, R.drawable.ic_group);
break;
case EMAIL:
name = context.getString(R.string.share_email_clarification, name);
setImage(holder, name, R.drawable.ic_email);
break;
case ROOM:
name = context.getString(R.string.share_room_clarification, name);
setImage(holder, name, R.drawable.ic_chat_bubble);
break;
case CIRCLE:
holder.avatar.setImageResource(R.drawable.ic_circles);
break;
default:
setImage(holder, name, R.drawable.ic_user);
break;
}
holder.name.setText(name);
if (share.getShareWith().equalsIgnoreCase(userId) || share.getUserId().equalsIgnoreCase(userId)) {
holder.allowEditing.setVisibility(View.VISIBLE);
holder.editShareButton.setVisibility(View.VISIBLE);
ThemeUtils.tintCheckbox(holder.allowEditing, accentColor);
holder.allowEditing.setChecked(canEdit(share));
holder.allowEditing.setOnClickListener(v -> allowEditClick(holder.allowEditing, share));
// bind listener to edit privileges
holder.editShareButton.setOnClickListener(v -> onOverflowIconClicked(v, holder.allowEditing, share));
} else {
holder.allowEditing.setVisibility(View.GONE);
holder.editShareButton.setVisibility(View.GONE);
}
}
}
private void setImage(UserViewHolder holder, String name, @DrawableRes int fallback) {
try {
holder.avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));
} catch (NoSuchAlgorithmException e) {
holder.avatar.setImageResource(fallback);
}
}
@Override
public long getItemId(int position) {
return shares.get(position).getId();
}
@Override
public int getItemCount() {
return shares.size();
}
private void allowEditClick(AppCompatCheckBox checkBox, @NonNull OCShare share) {
if (!share.isFolder()) {
share.setPermissions(listener.updatePermissionsToShare(
share,
canReshare(share),
checkBox.isChecked(),
false,
false,
false
));
} else {
share.setPermissions(listener.updatePermissionsToShare(
share,
canReshare(share),
checkBox.isChecked(),
checkBox.isChecked(),
checkBox.isChecked(),
checkBox.isChecked()
));
}
}
private void onOverflowIconClicked(View view, AppCompatCheckBox allowEditsCheckBox, OCShare share) {
// use grey as fallback for elements where custom theming is not available
if (ThemeUtils.themingEnabled(context)) {
context.getTheme().applyStyle(R.style.FallbackThemingTheme, true);
}
PopupMenu popup = new PopupMenu(context, view);
popup.inflate(R.menu.item_user_sharing_settings);
prepareOptionsMenu(popup.getMenu(), share);
popup.setOnMenuItemClickListener(item -> optionsItemSelected(popup.getMenu(), item, allowEditsCheckBox, share));
popup.show();
}
/**
* Updates the sharee's menu with the current permissions of the {@link OCShare}
*
* @param menu the menu of the sharee/shared file
* @param share the shared file
*/
private void prepareOptionsMenu(Menu menu, OCShare share) {
MenuItem editCreateItem = menu.findItem(R.id.action_can_edit_create);
MenuItem editChangeItem = menu.findItem(R.id.action_can_edit_change);
MenuItem editDeleteItem = menu.findItem(R.id.action_can_edit_delete);
MenuItem hideFileListingItem = menu.findItem(R.id.action_hide_file_listing);
MenuItem passwordItem = menu.findItem(R.id.action_password);
MenuItem expirationDateItem = menu.findItem(R.id.action_expiration_date);
MenuItem reshareItem = menu.findItem(R.id.action_can_reshare);
MenuItem sendNoteItem = menu.findItem(R.id.action_share_send_note);
if (isReshareForbidden(share)) {
reshareItem.setVisible(false);
}
reshareItem.setChecked(canReshare(share));
if (share.getShareType() == ShareType.EMAIL) {
SharingMenuHelper.setupHideFileListingMenuItem(
hideFileListingItem,
file.isFolder(),
canEdit(share),
share.getPermissions()
);
SharingMenuHelper.setupPasswordMenuItem(passwordItem, share.isPasswordProtected());
reshareItem.setVisible(false);
editCreateItem.setVisible(false);
editChangeItem.setVisible(false);
editDeleteItem.setVisible(false);
} else {
if (file.isFolder() && isEditOptionsAvailable(share)) {
/// TODO change areEditOptionsAvailable in order to delete !isFederated
editCreateItem.setChecked(canCreate(share));
editChangeItem.setChecked(canUpdate(share));
editDeleteItem.setChecked(canDelete(share));
} else {
editCreateItem.setVisible(false);
editChangeItem.setVisible(false);
editDeleteItem.setVisible(false);
}
hideFileListingItem.setVisible(false);
passwordItem.setVisible(false);
expirationDateItem.setVisible(false);
}
SharingMenuHelper.setupExpirationDateMenuItem(
menu.findItem(R.id.action_expiration_date), share.getExpirationDate(), context.getResources());
sendNoteItem.setVisible(capabilities.getVersion().isNoteOnShareSupported());
}
private boolean isEditOptionsAvailable(OCShare share) {
return !ShareType.FEDERATED.equals(share.getShareType());
}
private boolean isReshareForbidden(OCShare share) {
return ShareType.FEDERATED.equals(share.getShareType()) ||
(capabilities != null && capabilities.getFilesSharingResharing().isFalse());
}
private boolean canEdit(OCShare share) {
return (share.getPermissions() &
(OCShare.CREATE_PERMISSION_FLAG | OCShare.UPDATE_PERMISSION_FLAG | OCShare.DELETE_PERMISSION_FLAG)) > 0;
}
private boolean canCreate(OCShare share) {
return (share.getPermissions() & OCShare.CREATE_PERMISSION_FLAG) > 0;
}
private boolean canUpdate(OCShare share) {
return (share.getPermissions() & OCShare.UPDATE_PERMISSION_FLAG) > 0;
}
private boolean canDelete(OCShare share) {
return (share.getPermissions() & OCShare.DELETE_PERMISSION_FLAG) > 0;
}
private boolean canReshare(OCShare share) {
return (share.getPermissions() & OCShare.SHARE_PERMISSION_FLAG) > 0;
}
private boolean optionsItemSelected(Menu menu, MenuItem item, AppCompatCheckBox allowEditsCheckBox, OCShare share) {
switch (item.getItemId()) {
case R.id.action_can_edit_create:
case R.id.action_can_edit_change:
case R.id.action_can_edit_delete: {
item.setChecked(!item.isChecked());
if (item.isChecked() && !allowEditsCheckBox.isChecked()) {
allowEditsCheckBox.setChecked(true);
}
share.setPermissions(
updatePermissionsToShare(
share,
menu.findItem(R.id.action_can_reshare).isChecked(),
allowEditsCheckBox.isChecked(),
menu.findItem(R.id.action_can_edit_create).isChecked(),
menu.findItem(R.id.action_can_edit_change).isChecked(),
menu.findItem(R.id.action_can_edit_delete).isChecked())
);
return true;
}
case R.id.action_can_reshare: {
item.setChecked(!item.isChecked());
share.setPermissions(
updatePermissionsToShare(
share,
menu.findItem(R.id.action_can_reshare).isChecked(),
allowEditsCheckBox.isChecked(),
menu.findItem(R.id.action_can_edit_create).isChecked(),
menu.findItem(R.id.action_can_edit_change).isChecked(),
menu.findItem(R.id.action_can_edit_delete).isChecked())
);
return true;
}
case R.id.action_unshare: {
listener.unshareWith(share);
shares.remove(share);
notifyDataSetChanged();
return true;
}
case R.id.action_hide_file_listing: {
item.setChecked(!item.isChecked());
if (capabilities.getFilesFileDrop().isTrue()) {
listener.setHideFileListingPermissionsToShare(share, item.isChecked());
} else {
// not supported in ownCloud
listener.showNotSupportedByOcMessage();
}
return true;
}
case R.id.action_password: {
listener.requestPasswordForShare(share);
return true;
}
case R.id.action_expiration_date: {
ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment
.newInstance(share, share.getExpirationDate());
dialog.show(fragmentManager, ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
return true;
}
case R.id.action_share_send_note:
NoteDialogFragment dialog = NoteDialogFragment.newInstance(share);
dialog.show(fragmentManager, NoteDialogFragment.NOTE_FRAGMENT);
return true;
default:
return true;
}
}
private int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate,
boolean canEditChange, boolean canEditDelete) {
return listener.updatePermissionsToShare(
share,
canReshare,
canEdit,
canEditCreate,
canEditChange,
canEditDelete
);
}
@Override
public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
if (callContext instanceof ImageView) {
ImageView iv = (ImageView) callContext;
iv.setImageDrawable(avatarDrawable);
}
}
@Override
public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
if (callContext instanceof ImageView) {
ImageView iv = (ImageView) callContext;
return String.valueOf(iv.getTag()).equals(tag);
}
return false;
}
class UserViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.avatar)
ImageView avatar;
@BindView(R.id.name)
TextView name;
@BindView(R.id.allowEditing)
AppCompatCheckBox allowEditing;
@BindView(R.id.editShareButton)
ImageView editShareButton;
UserViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public interface ShareeListAdapterListener {
/**
* unshare with given sharee {@link OCShare}.
*
* @param share the share
*/
void unshareWith(OCShare share);
/**
* Updates the permissions of the {@link OCShare}.
*
* @param share the share to be updated
* @param canReshare reshare permission
* @param canEdit edit permission
* @param canEditCreate create permission (folders only)
* @param canEditChange change permission (folders only)
* @param canEditDelete delete permission (folders only)
* @return permissions value set
*/
int updatePermissionsToShare(OCShare share,
boolean canReshare,
boolean canEdit,
boolean canEditCreate,
boolean canEditChange,
boolean canEditDelete);
void updateNoteToShare(OCShare share, String note);
/**
* show a snackbar that this feature is not supported by ownCloud.
*/
void showNotSupportedByOcMessage();
/**
* Starts a dialog that requests a password to the user to protect a share.
*
* @param share the share for which a password shall be configured/removed
*/
void requestPasswordForShare(OCShare share);
/**
* Updates a public share on a folder to set its hide file listing permission.
* Starts a request to do it in {@link OperationsService}
*
* @param share {@link OCShare} instance which permissions will be updated.
* @param hideFileListing New state of the permission for editing the folder shared via link.
*/
void setHideFileListingPermissionsToShare(OCShare share, boolean hideFileListing);
void setHideFileDownloadPermissionToShare(OCFile file, boolean hideFileDownload);
}
}