Browse Source

Merge pull request #6358 from nextcloud/multipleShareLink

add multiple share links
Tobias Kaminsky 4 years ago
parent
commit
551628d4fe

+ 11 - 5
src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -1195,9 +1195,9 @@ public class FileDataStorageManager {
      * @param path      Path of the file.
      * @param type      Type of the share to get
      * @param shareWith Target of the share. Ignored in type is {@link ShareType#PUBLIC_LINK}
-     * @return First {@link OCShare} instance found in DB bound to the file in 'path'
+     * @return All {@link OCShare} instance found in DB bound to the file in 'path'
      */
-    public OCShare getFirstShareByPathAndType(String path, ShareType type, String shareWith) {
+    public List<OCShare> getSharesByPathAndType(String path, ShareType type, String shareWith) {
         Cursor cursor;
         if (shareWith == null) {
             shareWith = "";
@@ -1246,14 +1246,18 @@ public class FileDataStorageManager {
             }
         }
 
-        OCShare share = null;
+        List<OCShare> shares = new ArrayList<>();
+        OCShare share;
         if (cursor != null) {
             if (cursor.moveToFirst()) {
-                share = createShareInstance(cursor);
+                do {
+                    share = createShareInstance(cursor);
+                    shares.add(share);
+                } while (cursor.moveToNext());
             }
             cursor.close();
         }
-        return share;
+        return shares;
     }
 
     // test with null cursor?
@@ -1274,6 +1278,7 @@ public class FileDataStorageManager {
         share.setPasswordProtected(getInt(cursor, ProviderTableMeta.OCSHARES_IS_PASSWORD_PROTECTED) == 1);
         share.setNote(getString(cursor, ProviderTableMeta.OCSHARES_NOTE));
         share.setHideFileDownload(getInt(cursor, ProviderTableMeta.OCSHARES_HIDE_DOWNLOAD) == 1);
+        share.setShareLink(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LINK));
 
         return share;
     }
@@ -1626,6 +1631,7 @@ public class FileDataStorageManager {
             contentValues.put(ProviderTableMeta.OCSHARES_IS_PASSWORD_PROTECTED, share.isPasswordProtected() ? 1 : 0);
             contentValues.put(ProviderTableMeta.OCSHARES_NOTE, share.getNote());
             contentValues.put(ProviderTableMeta.OCSHARES_HIDE_DOWNLOAD, share.isHideFileDownload());
+            contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LINK, share.getShareLink());
 
             // adding a new share resource
             operations.add(ContentProviderOperation

+ 2 - 1
src/main/java/com/owncloud/android/db/ProviderMeta.java

@@ -31,7 +31,7 @@ import com.owncloud.android.MainApp;
  */
 public class ProviderMeta {
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 57;
+    public static final int DB_VERSION = 58;
 
     private ProviderMeta() {
         // No instance
@@ -145,6 +145,7 @@ public class ProviderMeta {
         public static final String OCSHARES_IS_PASSWORD_PROTECTED = "is_password_protected";
         public static final String OCSHARES_NOTE = "note";
         public static final String OCSHARES_HIDE_DOWNLOAD = "hide_download";
+        public static final String OCSHARES_SHARE_LINK = "share_link";
 
         public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE
                 + " collate nocase asc";

+ 12 - 36
src/main/java/com/owncloud/android/operations/CreateShareViaLinkOperation.java

@@ -23,11 +23,9 @@ package com.owncloud.android.operations;
 
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.resources.files.FileUtils;
 import com.owncloud.android.lib.resources.shares.CreateShareRemoteOperation;
-import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.operations.common.SyncOperation;
@@ -49,45 +47,23 @@ public class CreateShareViaLinkOperation extends SyncOperation {
 
     @Override
     protected RemoteOperationResult run(OwnCloudClient client) {
-        // Check if the share link already exists
-        RemoteOperation operation = new GetSharesForFileRemoteOperation(path, false, false);
-        RemoteOperationResult result = operation.execute(client);
-
-        // Create public link if doesn't exist yet
-        boolean publicShareExists = false;
-        if (result.isSuccess()) {
-            OCShare share;
-            for (int i=0 ; i<result.getData().size(); i++) {
-                share = (OCShare) result.getData().get(i);
-                if (ShareType.PUBLIC_LINK.equals(share.getShareType())) {
-                    publicShareExists = true;
-                    break;
-                }
-            }
-        }
-        if (!publicShareExists) {
-            CreateShareRemoteOperation createOp = new CreateShareRemoteOperation(
-                path,
-                    ShareType.PUBLIC_LINK,
-                    "",
-                    false,
-                password,
-                    OCShare.DEFAULT_PERMISSION
-            );
-            createOp.setGetShareDetails(true);
-            result = createOp.execute(client);
-        }
+        CreateShareRemoteOperation createOp = new CreateShareRemoteOperation(path,
+                                                                             ShareType.PUBLIC_LINK,
+                                                                             "",
+                                                                             false,
+                                                                             password,
+                                                                             OCShare.DEFAULT_PERMISSION);
+        createOp.setGetShareDetails(true);
+        RemoteOperationResult result = createOp.execute(client);
 
         if (result.isSuccess()) {
             if (result.getData().size() > 0) {
                 Object item = result.getData().get(0);
-                if (item instanceof  OCShare) {
+                if (item instanceof OCShare) {
                     updateData((OCShare) item);
                 } else {
                     ArrayList<Object> data = result.getData();
-                    result = new RemoteOperationResult(
-                        RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
-                    );
+                    result = new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND);
                     result.setData(data);
                 }
             } else {
@@ -110,8 +86,8 @@ public class CreateShareViaLinkOperation extends SyncOperation {
         getStorageManager().saveShare(share);
 
         // Update OCFile with data from share: ShareByLink  and publicLink
-        OCFile file = getStorageManager().getFileByPath(path);
-        if (file!=null) {
+        OCFile file = getStorageManager().getFileByEncryptedRemotePath(path);
+        if (file != null) {
             file.setPublicLink(share.getShareLink());
             file.setSharedViaLink(true);
             getStorageManager().saveFile(file);

+ 15 - 32
src/main/java/com/owncloud/android/operations/UnshareOperation.java

@@ -21,8 +21,6 @@
 
 package com.owncloud.android.operations;
 
-import android.content.Context;
-
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -46,44 +44,37 @@ public class UnshareOperation extends SyncOperation {
     private static final int SINGLY_SHARED = 1;
 
     private String mRemotePath;
-    private ShareType mShareType;
-    private String mShareWith;
-    private Context mContext;
+    private long shareId;
 
-    public UnshareOperation(String remotePath, ShareType shareType, String shareWith,
-                                Context context) {
+    public UnshareOperation(String remotePath, long shareId) {
         mRemotePath = remotePath;
-        mShareType = shareType;
-        mShareWith = shareWith;
-        mContext = context;
+        this.shareId = shareId;
     }
 
     @Override
     protected RemoteOperationResult run(OwnCloudClient client) {
-        RemoteOperationResult result  = null;
+        RemoteOperationResult result;
 
         // Get Share for a file
-        OCShare share = getStorageManager().getFirstShareByPathAndType(mRemotePath,
-                mShareType, mShareWith);
+        OCShare share = getStorageManager().getShareById(shareId);
 
         if (share != null) {
-            OCFile file = getStorageManager().getFileByPath(mRemotePath);
-            RemoveShareRemoteOperation operation =
-                new RemoveShareRemoteOperation((int) share.getRemoteId());
+            OCFile file = getStorageManager().getFileByEncryptedRemotePath(mRemotePath);
+            RemoveShareRemoteOperation operation = new RemoveShareRemoteOperation(share.getRemoteId());
             result = operation.execute(client);
 
             if (result.isSuccess()) {
                 Log_OC.d(TAG, "Share id = " + share.getRemoteId() + " deleted");
 
-                if (ShareType.PUBLIC_LINK.equals(mShareType)) {
+                if (ShareType.PUBLIC_LINK.equals(share.getShareType())) {
                     file.setSharedViaLink(false);
                     file.setPublicLink("");
-                } else if (ShareType.USER.equals(mShareType) || ShareType.GROUP.equals(mShareType)
-                    || ShareType.FEDERATED.equals(mShareType)){
+                } else if (ShareType.USER.equals(share.getShareType()) || ShareType.GROUP.equals(share.getShareType())
+                    || ShareType.FEDERATED.equals(share.getShareType())) {
                     // Check if it is the last share
-                    List <OCShare> sharesWith = getStorageManager().
-                            getSharesWithForAFile(mRemotePath,
-                            getStorageManager().getAccount().name);
+                    List<OCShare> sharesWith = getStorageManager().
+                        getSharesWithForAFile(mRemotePath,
+                                              getStorageManager().getAccount().name);
                     if (sharesWith.size() == SINGLY_SHARED) {
                         file.setSharedWithSharee(false);
                     }
@@ -104,15 +95,7 @@ public class UnshareOperation extends SyncOperation {
         return result;
     }
 
-    private boolean existsFile(OwnCloudClient client, String remotePath){
-        ExistenceCheckRemoteOperation existsOperation =
-                new ExistenceCheckRemoteOperation(remotePath, mContext, false);
-        RemoteOperationResult result = existsOperation.execute(client);
-        return result.isSuccess();
-    }
-
-    public ShareType getShareType() {
-        return mShareType;
+    private boolean existsFile(OwnCloudClient client, String remotePath) {
+        return new ExistenceCheckRemoteOperation(remotePath, false).execute(client).isSuccess();
     }
-
 }

+ 5 - 9
src/main/java/com/owncloud/android/operations/UpdateShareViaLinkOperation.java

@@ -27,7 +27,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.resources.files.FileUtils;
 import com.owncloud.android.lib.resources.shares.GetShareRemoteOperation;
 import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.shares.UpdateShareRemoteOperation;
 import com.owncloud.android.operations.common.SyncOperation;
 
@@ -44,25 +43,22 @@ public class UpdateShareViaLinkOperation extends SyncOperation {
     private Boolean publicUploadOnFile;
     private Boolean hideFileDownload;
     private long expirationDateInMillis;
+    private long shareId;
 
     /**
      * Constructor
      *
-     * @param path          Full path of the file/folder being shared. Mandatory argument
+     * @param path Full path of the file/folder being shared. Mandatory argument
      */
-    public UpdateShareViaLinkOperation(String path) {
+    public UpdateShareViaLinkOperation(String path, long shareId) {
         this.path = path;
         expirationDateInMillis = 0;
+        this.shareId = shareId;
     }
 
     @Override
     protected RemoteOperationResult run(OwnCloudClient client) {
-        OCShare publicShare = getStorageManager().getFirstShareByPathAndType(path, ShareType.PUBLIC_LINK, "");
-
-        if (publicShare == null) {
-            // TODO try to get remote share before failing?
-            return new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND);
-        }
+        OCShare publicShare = getStorageManager().getShareById(shareId);
 
         UpdateShareRemoteOperation updateOp = new UpdateShareRemoteOperation(publicShare.getRemoteId());
         updateOp.setPassword(password);

+ 20 - 1
src/main/java/com/owncloud/android/providers/FileContentProvider.java

@@ -747,7 +747,8 @@ public class FileContentProvider extends ContentProvider {
                        + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + TEXT
                        + ProviderTableMeta.OCSHARES_IS_PASSWORD_PROTECTED + INTEGER
                        + ProviderTableMeta.OCSHARES_NOTE + TEXT
-                       + ProviderTableMeta.OCSHARES_HIDE_DOWNLOAD + " INTEGER );");
+                       + ProviderTableMeta.OCSHARES_HIDE_DOWNLOAD + INTEGER
+                       + ProviderTableMeta.OCSHARES_SHARE_LINK + " TEXT );");
     }
 
     private void createCapabilitiesTable(SQLiteDatabase db) {
@@ -2234,6 +2235,24 @@ public class FileContentProvider extends ContentProvider {
             if (!upgraded) {
                 Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
             }
+
+            if (oldVersion < 58 && newVersion >= 58) {
+                Log_OC.i(SQL, "Entering in the #58 add public link to share table");
+                db.beginTransaction();
+                try {
+                    db.execSQL(ALTER_TABLE + ProviderTableMeta.OCSHARES_TABLE_NAME +
+                                   ADD_COLUMN + ProviderTableMeta.OCSHARES_SHARE_LINK + " TEXT ");
+
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
+            if (!upgraded) {
+                Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
+            }
         }
     }
 }

+ 7 - 7
src/main/java/com/owncloud/android/services/OperationsService.java

@@ -538,7 +538,8 @@ public class OperationsService extends Service {
                         shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1);
 
                         if (!TextUtils.isEmpty(remotePath)) {
-                            UpdateShareViaLinkOperation updateLinkOperation = new UpdateShareViaLinkOperation(remotePath);
+                            UpdateShareViaLinkOperation updateLinkOperation =
+                                new UpdateShareViaLinkOperation(remotePath, shareId);
 
                             password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD);
                             updateLinkOperation.setPassword(password);
@@ -547,7 +548,7 @@ public class OperationsService extends Service {
                             updateLinkOperation.setExpirationDateInMillis(expirationDate);
 
                             boolean hideFileDownload = operationIntent.getBooleanExtra(EXTRA_SHARE_HIDE_FILE_DOWNLOAD,
-                                false);
+                                                                                       false);
                             updateLinkOperation.setHideFileDownload(hideFileDownload);
 
                             if (operationIntent.hasExtra(EXTRA_SHARE_PUBLIC_UPLOAD)) {
@@ -593,17 +594,16 @@ public class OperationsService extends Service {
                         int permissions = operationIntent.getIntExtra(EXTRA_SHARE_PERMISSIONS, -1);
                         if (!TextUtils.isEmpty(remotePath)) {
                             operation = new CreateShareWithShareeOperation(remotePath, shareeName, shareType,
-                                    permissions);
+                                                                           permissions);
                         }
                         break;
 
                     case ACTION_UNSHARE:
                         remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
-                        shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE);
-                        String shareWith = operationIntent.getStringExtra(EXTRA_SHARE_WITH);
+                        shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1);
 
-                        if (!TextUtils.isEmpty(remotePath)) {
-                            operation = new UnshareOperation(remotePath, shareType, shareWith, this);
+                        if (shareId > 0) {
+                            operation = new UnshareOperation(remotePath, shareId);
                         }
                         break;
 

+ 1 - 0
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -771,6 +771,7 @@ public abstract class FileActivity extends DrawerActivity
         if (result.isSuccess()) {
             updateFileFromDB();
             if (sharingFragment != null) {
+                sharingFragment.refreshPublicShareFromDB();
                 sharingFragment.onUpdateShareInformation(result, getFile());
             }
         } else if (sharingFragment != null && sharingFragment.getView() != null) {

+ 33 - 0
src/main/java/com/owncloud/android/ui/adapter/PublicShareInterface.java

@@ -0,0 +1,33 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.widget.ImageView;
+
+import com.owncloud.android.lib.resources.shares.OCShare;
+
+public interface PublicShareInterface {
+    void copyLink(OCShare share);
+
+    void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink);
+}

+ 68 - 0
src/main/java/com/owncloud/android/ui/adapter/PublicShareListAdapter.java

@@ -0,0 +1,68 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class PublicShareListAdapter extends RecyclerView.Adapter<PublicShareViewHolder> {
+    private Context context;
+    private List<OCShare> shares;
+    private PublicShareInterface listener;
+
+    public PublicShareListAdapter(Context context, List<OCShare> shares, PublicShareInterface listener) {
+        this.context = context;
+        this.shares = shares;
+        this.listener = listener;
+    }
+
+    @NonNull
+    @Override
+    public PublicShareViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        FileDetailsSharePublicLinkItemBinding binding =
+            FileDetailsSharePublicLinkItemBinding.inflate(LayoutInflater.from(context), parent, false);
+
+        return new PublicShareViewHolder(binding, context);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull PublicShareViewHolder holder, int position) {
+        OCShare share = shares.get(position);
+
+        holder.bind(share, listener);
+    }
+
+    @Override
+    public int getItemCount() {
+        return shares.size();
+    }
+}

+ 66 - 0
src/main/java/com/owncloud/android/ui/adapter/PublicShareViewHolder.java

@@ -0,0 +1,66 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.view.View;
+
+import com.owncloud.android.R;
+import com.owncloud.android.databinding.FileDetailsSharePublicLinkItemBinding;
+import com.owncloud.android.lib.resources.shares.OCShare;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+class PublicShareViewHolder extends RecyclerView.ViewHolder {
+    private FileDetailsSharePublicLinkItemBinding binding;
+    private Context context;
+
+    public PublicShareViewHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+
+    public PublicShareViewHolder(FileDetailsSharePublicLinkItemBinding binding, Context context) {
+        this(binding.getRoot());
+        this.binding = binding;
+        this.context = context;
+    }
+
+    public void bind(OCShare publicShare, PublicShareInterface listener) {
+        binding.copyInternalLinkIcon
+            .getBackground()
+            .setColorFilter(context.getResources().getColor(R.color.primary_button_background_color),
+                            PorterDuff.Mode.SRC_IN);
+        binding.copyInternalLinkIcon
+            .getDrawable()
+            .mutate()
+            .setColorFilter(context.getResources().getColor(R.color.black),
+                            PorterDuff.Mode.SRC_IN);
+
+        binding.shareLinkCopyIcon.setOnClickListener(v -> listener.copyLink(publicShare));
+
+        binding.overflowMenuShareLink.setOnClickListener(
+            v -> listener.showLinkOverflowMenu(publicShare, binding.overflowMenuShareLink));
+    }
+}

+ 93 - 151
src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java

@@ -23,7 +23,6 @@
 
 package com.owncloud.android.ui.fragment;
 
-import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.SearchManager;
 import android.content.Context;
@@ -40,7 +39,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.PopupMenu;
 import android.widget.TextView;
 
 import com.google.android.material.snackbar.Snackbar;
@@ -58,6 +56,8 @@ import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OCCapability;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.adapter.PublicShareInterface;
+import com.owncloud.android.ui.adapter.PublicShareListAdapter;
 import com.owncloud.android.ui.adapter.ShareeListAdapter;
 import com.owncloud.android.ui.decoration.SimpleListItemDividerDecoration;
 import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
@@ -76,7 +76,7 @@ import javax.inject.Inject;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.appcompat.widget.AppCompatCheckBox;
+import androidx.appcompat.widget.PopupMenu;
 import androidx.appcompat.widget.SearchView;
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -87,18 +87,17 @@ import butterknife.OnClick;
 import butterknife.Unbinder;
 
 public class FileDetailSharingFragment extends Fragment implements ShareeListAdapter.ShareeListAdapterListener,
-    DisplayUtils.AvatarGenerationListener, Injectable {
+    DisplayUtils.AvatarGenerationListener,
+    PublicShareInterface,
+    Injectable {
 
     private static final String ARG_FILE = "FILE";
     private static final String ARG_USER = "USER";
-
-    // to show share with users/groups info
-    private List<OCShare> shares;
+    public static final int PERMISSION_EDITING_ALLOWED = 17;
 
     private OCFile file;
     private User user;
     private OCCapability capabilities;
-    private OCShare publicShare;
 
     private FileOperationsHelper fileOperationsHelper;
     private FileActivity fileActivity;
@@ -112,20 +111,11 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
     @BindView(R.id.shareUsersList)
     RecyclerView usersList;
 
-    @BindView(R.id.share_by_link)
-    AppCompatCheckBox shareByLink;
-
-    @BindView(R.id.share_link_copy_icon)
-    ImageView shareLinkCopyIcon;
-
-    @BindView(R.id.overflow_menu_share_link)
-    ImageView overflowMenuShareLink;
-
-    @BindView(R.id.share_by_link_allow_editing)
-    AppCompatCheckBox shareByLinkAllowEditing;
+    @BindView(R.id.publicShareList)
+    RecyclerView publicShareList;
 
-    @BindView(R.id.share_by_link_container)
-    LinearLayout shareByLinkContainer;
+    @BindView(R.id.new_public_share)
+    View addPublicShare;
 
     @BindView(R.id.shared_with_you_container)
     LinearLayout sharedWithYouContainer;
@@ -245,13 +235,11 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         ThemeUtils.themeSearchView(searchView, requireContext());
 
         if (file.canReshare()) {
-            setShareByLinkInfo(file.isSharedViaLink());
             setShareWithUserInfo();
         } else {
             searchView.setQueryHint(getResources().getString(R.string.reshare_not_allowed));
             searchView.setInputType(InputType.TYPE_NULL);
             disableSearchView(searchView);
-            shareByLinkContainer.setVisibility(View.GONE);
         }
     }
 
@@ -267,45 +255,6 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    /**
-     * Updates Share by link UI
-     *
-     * @param isShareByLink flag is share by link is enable
-     */
-    public void setShareByLinkInfo(boolean isShareByLink) {
-        shareByLink.setChecked(isShareByLink);
-        if (isShareByLink) {
-            shareLinkCopyIcon.setVisibility(View.VISIBLE);
-        } else {
-            shareLinkCopyIcon.setVisibility(View.INVISIBLE);
-        }
-        int accentColor = ThemeUtils.primaryAccentColor(getContext());
-        ThemeUtils.tintCheckbox(shareByLink, accentColor);
-        ThemeUtils.tintCheckbox(shareByLinkAllowEditing, accentColor);
-        setLinkDetailVisible(isShareByLink);
-    }
-
-    private void setLinkDetailVisible(boolean visible) {
-        if (visible) {
-                shareByLinkAllowEditing.setVisibility(View.VISIBLE);
-            overflowMenuShareLink.setVisibility(View.VISIBLE);
-        } else {
-            shareByLinkAllowEditing.setVisibility(View.INVISIBLE);
-            overflowMenuShareLink.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    /**
-     * Update Share With data
-     */
-    public void setShareWithUserInfo() {
-        // Get Users and Groups
-        shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), user.getAccountName());
-
-        // Update list of users/groups
-        updateListOfUserGroups();
-    }
-
     private void setShareWithYou() {
         if (accountManager.userOwnsFile(file, user)) {
             sharedWithYouContainer.setVisibility(View.GONE);
@@ -328,9 +277,11 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    private void updateListOfUserGroups() {
+    private void setShareWithUserInfo() {
         // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed
-
+        // to show share with users/groups info
+        List<OCShare> shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(),
+                                                                            user.toPlatformAccount().name);
         if (shares.size() > 0) {
             AccountManager accountManager = AccountManager.get(getContext());
             String userId = accountManager.getUserData(user.toPlatformAccount(),
@@ -351,15 +302,6 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    @OnClick(R.id.share_by_link)
-    public void toggleShareByLink() {
-        if (shareByLink.isChecked()) {
-            createShareLink();
-        } else {
-            fileOperationsHelper.unshareFileViaLink(file);
-        }
-    }
-
     @OnClick(R.id.copy_internal_container)
     public void copyInternalLink() {
         OwnCloudAccount account = accountManager.getCurrentOwnCloudAccount();
@@ -399,8 +341,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    @OnClick({R.id.share_link_copy_icon})
-    public void copyLinkToClipboard() {
+    public void copyLink(OCShare share) {
         if (file.isSharedViaLink()) {
             if (TextUtils.isEmpty(file.getPublicLink())) {
                 fileOperationsHelper.getFileWithLink(file);
@@ -410,55 +351,55 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
         }
     }
 
-    @OnClick(R.id.share_by_link_allow_editing)
-    public void toggleShareLinkAllowEditing() {
-        if (file.isSharedViaLink()) {
-            fileOperationsHelper.setUploadPermissionsToShare(file, shareByLinkAllowEditing.isChecked());
-        }
-    }
-
-    @OnClick(R.id.overflow_menu_share_link)
-    public void showLinkOverflowMenu() {
-        Context context = getContext();
-        if (context != null && ThemeUtils.themingEnabled(context)) {
+    public void showLinkOverflowMenu(OCShare publicShare, ImageView overflowMenuShareLink) {
+        if (ThemeUtils.themingEnabled(requireContext())) {
             // use grey as fallback for elements where custom theming is not available
-            context.getTheme().applyStyle(R.style.FallbackThemingTheme, true);
-        } else {
-            context = getActivity();
+            requireContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
         }
 
-        PopupMenu popup = new PopupMenu(context, overflowMenuShareLink);
+        PopupMenu popup = new PopupMenu(requireContext(), overflowMenuShareLink);
         popup.inflate(R.menu.fragment_file_detail_sharing_link);
-        prepareOptionsMenu(popup.getMenu());
-        popup.setOnMenuItemClickListener(this::optionsItemSelected);
+        prepareOptionsMenu(popup.getMenu(), publicShare);
+        popup.setOnMenuItemClickListener(menuItem -> optionsItemSelected(menuItem, publicShare));
         popup.show();
     }
 
-    private void prepareOptionsMenu(Menu menu) {
-        Resources res = getResources();
-        SharingMenuHelper.setupHideFileListingMenuItem(
-                menu.findItem(R.id.action_hide_file_listing),
-                file.isFolder(),
-                shareByLinkAllowEditing.isChecked(),
-                publicShare.getPermissions()
-        );
+    private void prepareOptionsMenu(Menu menu, OCShare publicShare) {
+        Resources res = requireContext().getResources();
+        SharingMenuHelper.setupHideFileListingMenuItem(menu.findItem(R.id.action_hide_file_listing),
+                                                       file.isFolder(),
+                                                       menu.findItem(R.id.action_allow_editing).isChecked(),
+                                                       publicShare.getPermissions());
+
         SharingMenuHelper.setupHideFileDownload(menu.findItem(R.id.action_hide_file_download),
-            publicShare.isHideFileDownload(), capabilities);
-        SharingMenuHelper.setupPasswordMenuItem(
-                menu.findItem(R.id.action_password),
-                publicShare.isPasswordProtected()
-        );
-        SharingMenuHelper.setupExpirationDateMenuItem(
-                menu.findItem(R.id.action_share_expiration_date),
-                publicShare.getExpirationDate(),
-                res
-        );
+                                                publicShare.isHideFileDownload(),
+                                                capabilities);
+
+        SharingMenuHelper.setupPasswordMenuItem(menu.findItem(R.id.action_password),
+                                                publicShare.isPasswordProtected());
+
+        SharingMenuHelper.setupExpirationDateMenuItem(menu.findItem(R.id.action_share_expiration_date),
+                                                      publicShare.getExpirationDate(),
+                                                      res);
 
         menu.findItem(R.id.action_share_send_note).setVisible(capabilities.getVersion().isNoteOnShareSupported());
+
+        if (publicShare.getPermissions() > PERMISSION_EDITING_ALLOWED) {
+            menu.findItem(R.id.action_allow_editing).setChecked(true);
+        } else {
+            menu.findItem(R.id.action_allow_editing).setChecked(false);
+        }
     }
 
-    private boolean optionsItemSelected(MenuItem item) {
+    public boolean optionsItemSelected(MenuItem item, OCShare publicShare) {
         switch (item.getItemId()) {
+            case R.id.action_allow_editing:
+                if (file.isSharedViaLink()) {
+                    item.setChecked(!item.isChecked());
+                    fileOperationsHelper.setUploadPermissionsToShare(file,
+                                                                     item.isChecked());
+                }
+                return true;
             case R.id.action_hide_file_listing: {
                 item.setChecked(!item.isChecked());
                 if (capabilities.getFilesFileDrop().isTrue()) {
@@ -482,14 +423,12 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
             case R.id.action_share_expiration_date: {
                 ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment
                     .newInstance(file, publicShare.getExpirationDate());
-                dialog.show(
-                    fileActivity.getSupportFragmentManager(),
-                    ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG
-                           );
+                dialog.show(fileActivity.getSupportFragmentManager(),
+                            ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG);
                 return true;
             }
             case R.id.action_share_send_link: {
-                if(shareByLink.isChecked() && file.isSharedViaLink() && !TextUtils.isEmpty(file.getPublicLink())) {
+                if (file.isSharedViaLink() && !TextUtils.isEmpty(file.getPublicLink())) {
                     FileDisplayActivity.showShareLinkDialog(fileActivity, file, file.getPublicLink());
                 } else {
                     showSendLinkTo();
@@ -500,6 +439,12 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
                 NoteDialogFragment dialog = NoteDialogFragment.newInstance(publicShare);
                 dialog.show(fileActivity.getSupportFragmentManager(), NoteDialogFragment.NOTE_FRAGMENT);
                 return true;
+            case R.id.action_add_another_public_share_link:
+                createShareLink();
+                return true;
+            case R.id.action_unshare:
+                fileOperationsHelper.unshareShare(file, publicShare);
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -545,30 +490,29 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
      * Get {@link OCShare} instance from DB and updates the UI.
      */
     private void refreshUiFromDB() {
-        if (publicShare != null) {
-            // Get edited shared by link
-            publicShare = fileDataStorageManager.getShareById(publicShare.getId());
-        }
-
         // Updates UI with new state
         setupView();
     }
 
     @Override
     public void unshareWith(OCShare share) {
-        fileOperationsHelper.unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith());
+        fileOperationsHelper.unshareShare(file, share);
     }
 
     @Override
-    public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate,
-                                        boolean canEditChange, boolean canEditDelete) {
+    public int updatePermissionsToShare(OCShare share,
+                                        boolean canReshare,
+                                        boolean canEdit,
+                                        boolean canEditCreate,
+                                        boolean canEditChange,
+                                        boolean canEditDelete) {
         SharePermissionsBuilder spb = new SharePermissionsBuilder();
         spb.setSharePermission(canReshare);
 
         if (file.isFolder()) {
             spb.setUpdatePermission(canEditChange)
-                    .setCreatePermission(canEditCreate)
-                    .setDeletePermission(canEditDelete);
+                .setCreatePermission(canEditCreate)
+                .setDeletePermission(canEditDelete);
         } else {
             spb.setUpdatePermission(canEdit);
         }
@@ -618,34 +562,32 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
      */
     public void refreshPublicShareFromDB() {
         if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities) || !file.canReshare()) {
-            shareByLinkContainer.setVisibility(View.GONE);
-        } else {
-            // Get public share
-            publicShare = fileDataStorageManager.getFirstShareByPathAndType(file.getRemotePath(),
-                                                                            ShareType.PUBLIC_LINK, "");
-
-            // Update public share section
-            updatePublicShareSection();
+            publicShareList.setVisibility(View.GONE);
+            return;
         }
-    }
-
-    /**
-     * Updates in the UI the section about public share with the information
-     * in the current public share bound to, if any.
-     */
-    private void updatePublicShareSection() {
-        if (publicShare != null && ShareType.PUBLIC_LINK.equals(publicShare.getShareType())) {
-            shareByLink.setChecked(true);
-
-            if (publicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) {
-                shareByLinkAllowEditing.setChecked(true);
-            } else {
-                shareByLinkAllowEditing.setChecked(false);
-            }
 
-            setShareByLinkInfo(true);
+        // Get public share
+        List<OCShare> shares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(),
+                                                                             ShareType.PUBLIC_LINK,
+                                                                             "");
+
+        if (shares.isEmpty()) {
+            addPublicShare.setVisibility(View.VISIBLE);
+            publicShareList.setVisibility(View.GONE);
+            ImageView icon = requireView().findViewById(R.id.copy_internal_link_icon);
+            icon.getBackground().setColorFilter(requireContext()
+                                                    .getResources()
+                                                    .getColor(R.color.primary_button_background_color),
+                                                PorterDuff.Mode.SRC_IN);
+            icon.getDrawable().mutate().setColorFilter(requireContext().getResources().getColor(R.color.black),
+                                                       PorterDuff.Mode.SRC_IN);
+            requireView().findViewById(R.id.add_new_public_share_link).setOnClickListener(v -> createShareLink());
         } else {
-            setShareByLinkInfo(false);
+            addPublicShare.setVisibility(View.GONE);
+            publicShareList.setVisibility(View.VISIBLE);
+            publicShareList.setAdapter(new PublicShareListAdapter(getContext(), shares, this));
+            publicShareList.setLayoutManager(new LinearLayoutManager(getContext()));
+            publicShareList.addItemDecoration(new SimpleListItemDividerDecoration(getContext()));
         }
     }
 

+ 16 - 29
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -69,6 +69,7 @@ import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.ui.activity.ConflictsResolveActivity;
 import com.owncloud.android.ui.activity.ExternalSiteWebView;
 import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.RichDocumentsEditorWebView;
 import com.owncloud.android.ui.activity.ShareActivity;
 import com.owncloud.android.ui.activity.TextEditorWebView;
@@ -117,6 +118,7 @@ public class FileOperationsHelper {
     private static final String FILE_EXTENSION_URL = "url";
     private static final String FILE_EXTENSION_DESKTOP = "desktop";
     private static final String FILE_EXTENSION_WEBLOC = "webloc";
+    public static final int SINGLE_LINK_SIZE = 1;
 
     private FileActivity fileActivity;
     private CurrentAccountProvider currentAccount;
@@ -476,19 +478,19 @@ public class FileOperationsHelper {
         }
     }
 
-    public void getFileWithLink(OCFile file) {
-        if (file != null) {
-            fileActivity.showLoadingDialog(fileActivity.getApplicationContext().
-                    getString(R.string.wait_a_moment));
-
-            Intent service = new Intent(fileActivity, OperationsService.class);
-            service.setAction(OperationsService.ACTION_CREATE_SHARE_VIA_LINK);
-            service.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
-            service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
-            mWaitingForOpId = fileActivity.getOperationsServiceBinder().queueNewOperation(service);
+    public void getFileWithLink(@NonNull OCFile file) {
+        List<OCShare> shares = fileActivity.getStorageManager().getSharesByPathAndType(file.getRemotePath(),
+                                                                                       ShareType.PUBLIC_LINK,
+                                                                                       "");
 
+        if (shares.size() == SINGLE_LINK_SIZE) {
+            FileActivity.copyAndShareFileLink(fileActivity, file, shares.get(0).getShareLink());
         } else {
-            Log_OC.e(TAG, "Trying to share a NULL OCFile");
+            if (fileActivity instanceof FileDisplayActivity) {
+                ((FileDisplayActivity) fileActivity).showDetails(file, 1);
+            } else {
+                showShareFile(file);
+            }
         }
     }
 
@@ -541,33 +543,18 @@ public class FileOperationsHelper {
     }
 
     /**
-     * Helper method to unshare a file publicly shared via link.
-     * Starts a request to do it in {@link OperationsService}
+     * Helper method to unshare a file publicly shared via link. Starts a request to do it in {@link OperationsService}
      *
      * @param file The file to unshare.
      */
-    public void unshareFileViaLink(OCFile file) {
-
-        // Unshare the file: Create the intent
-        Intent unshareService = new Intent(fileActivity, OperationsService.class);
-        unshareService.setAction(OperationsService.ACTION_UNSHARE);
-        unshareService.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
-        unshareService.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
-        unshareService.putExtra(OperationsService.EXTRA_SHARE_TYPE, ShareType.PUBLIC_LINK);
-        unshareService.putExtra(OperationsService.EXTRA_SHARE_WITH, "");
-
-        queueShareIntent(unshareService);
-    }
-
-    public void unshareFileWithUserOrGroup(OCFile file, ShareType shareType, String userOrGroup) {
+    public void unshareShare(OCFile file, OCShare share) {
 
         // Unshare the file: Create the intent
         Intent unshareService = new Intent(fileActivity, OperationsService.class);
         unshareService.setAction(OperationsService.ACTION_UNSHARE);
         unshareService.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount());
         unshareService.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
-        unshareService.putExtra(OperationsService.EXTRA_SHARE_TYPE, shareType);
-        unshareService.putExtra(OperationsService.EXTRA_SHARE_WITH, userOrGroup);
+        unshareService.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId());
 
         queueShareIntent(unshareService);
     }

+ 59 - 0
src/main/res/layout/file_details_share_public_link_add_new_item.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  @author Tobias Kaminsky
+  @author Andy Scherzinger
+  Copyright (C) 2020 Andy Scherzinger
+  Copyright (C) 2020 Tobias Kaminsky
+  Copyright (C) 2020 Nextcloud GmbH
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  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 AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/new_public_share"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/standard_list_item_size"
+    android:orientation="horizontal"
+    android:visibility="gone">
+
+    <ImageView
+        android:layout_width="@dimen/share_icon_size"
+        android:layout_height="@dimen/share_icon_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:background="@drawable/round_bgnd"
+        android:contentDescription="@string/share"
+        android:padding="@dimen/standard_half_padding"
+        android:src="@drawable/shared_via_link" />
+
+    <TextView
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:text="@string/share_via_link_section_title" />
+
+    <ImageView
+        android:id="@+id/add_new_public_share_link"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@string/add_new_public_share"
+        android:paddingStart="@dimen/standard_padding"
+        android:paddingEnd="@dimen/standard_padding"
+        android:src="@drawable/ic_plus" />
+</LinearLayout>

+ 72 - 0
src/main/res/layout/file_details_share_public_link_item.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  @author Tobias Kaminsky
+  @author Andy Scherzinger
+  Copyright (C) 2020 Andy Scherzinger
+  Copyright (C) 2020 Tobias Kaminsky
+  Copyright (C) 2020 Nextcloud GmbH
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  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 AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/share_by_link_container"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/standard_list_item_size"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/copy_internal_link_icon"
+        android:layout_width="@dimen/share_icon_size"
+        android:layout_height="@dimen/share_icon_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:background="@drawable/round_bgnd"
+        android:contentDescription="@string/share"
+        android:padding="@dimen/standard_half_padding"
+        android:src="@drawable/shared_via_link" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:text="@string/share_via_link_section_title" />
+
+    <ImageView
+        android:id="@+id/share_link_copy_icon"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:contentDescription="@string/copy_link"
+        android:paddingStart="@dimen/standard_half_margin"
+        android:paddingTop="@dimen/standard_quarter_margin"
+        android:paddingEnd="@dimen/standard_eighth_margin"
+        android:paddingBottom="@dimen/standard_quarter_margin"
+        android:scaleType="fitStart"
+        android:src="@drawable/ic_content_copy" />
+
+    <ImageView
+        android:id="@+id/overflow_menu_share_link"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:contentDescription="@string/overflow_menu"
+        android:paddingStart="@dimen/standard_padding"
+        android:paddingEnd="@dimen/standard_padding"
+        android:src="@drawable/ic_dots_vertical" />
+
+</LinearLayout>

+ 28 - 88
src/main/res/layout/file_details_sharing_fragment.xml

@@ -18,7 +18,6 @@
   License along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -33,12 +32,12 @@
 
         <androidx.appcompat.widget.SearchView
             android:id="@+id/searchView"
+            style="@style/ownCloud.SearchView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/standard_eighth_margin"
+            android:layout_marginStart="@dimen/standard_quarter_margin"
             android:layout_marginEnd="@dimen/standard_margin"
-            android:hint="@string/share_search"
-            style="@style/ownCloud.SearchView"/>
+            android:hint="@string/share_search" />
 
         <LinearLayout
             android:id="@+id/shared_with_you_container"
@@ -52,8 +51,8 @@
 
             <ImageView
                 android:id="@+id/shared_with_you_avatar"
-                android:layout_width="40dp"
-                android:layout_height="40dp"
+                android:layout_width="@dimen/user_icon_size"
+                android:layout_height="@dimen/user_icon_size"
                 android:contentDescription="@string/avatar"
                 android:src="@drawable/ic_user" />
 
@@ -99,68 +98,15 @@
             </LinearLayout>
         </LinearLayout>
 
-        <LinearLayout
-            android:id="@+id/share_by_link_container"
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/publicShareList"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/standard_half_margin"
-            android:orientation="horizontal"
-            android:paddingLeft="@dimen/standard_padding"
-            android:paddingTop="@dimen/standard_padding"
-            android:paddingRight="@dimen/standard_padding">
-
-            <com.google.android.flexbox.FlexboxLayout
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:orientation="horizontal"
-                app:alignContent="stretch"
-                app:alignItems="stretch"
-                app:flexWrap="wrap">
-
-                <androidx.appcompat.widget.AppCompatCheckBox
-                    android:id="@+id/share_by_link"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
-                    android:text="@string/share_via_link_section_title" />
-
-                <ImageView
-                    android:id="@+id/share_link_copy_icon"
-                    android:layout_width="wrap_content"
-                    android:layout_height="32dp"
-                    android:layout_gravity="start|center"
-                    android:layout_weight="1"
-                    android:contentDescription="@string/copy_link"
-                    android:paddingStart="@dimen/standard_half_margin"
-                    android:paddingTop="@dimen/standard_quarter_margin"
-                    android:paddingEnd="@dimen/standard_eighth_margin"
-                    android:paddingBottom="@dimen/standard_quarter_margin"
-                    android:scaleType="fitStart"
-                    android:src="@drawable/ic_content_copy" />
-
-                <androidx.appcompat.widget.AppCompatCheckBox
-                    android:id="@+id/share_by_link_allow_editing"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
-                    android:ellipsize="middle"
-                    android:text="@string/edit_permission_label"
-                    android:textSize="16sp" />
-            </com.google.android.flexbox.FlexboxLayout>
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:divider="@drawable/divider"
+            android:dividerHeight="1dp" />
 
-            <ImageView
-                android:id="@+id/overflow_menu_share_link"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end|center"
-                android:layout_marginStart="@dimen/standard_half_margin"
-                android:layout_weight="0"
-                android:contentDescription="@string/overflow_menu"
-                android:paddingStart="@dimen/standard_half_padding"
-                android:paddingEnd="@dimen/zero"
-                android:src="@drawable/ic_dots_vertical"/>
-        </LinearLayout>
+        <include layout="@layout/file_details_share_public_link_add_new_item" />
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/shareUsersList"
@@ -174,48 +120,42 @@
             android:id="@+id/copy_internal_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/standard_margin"
+            android:orientation="horizontal"
             android:paddingStart="@dimen/zero"
+            android:paddingTop="@dimen/standard_half_padding"
             android:paddingEnd="@dimen/standard_padding"
-            android:orientation="horizontal">
+            android:paddingBottom="@dimen/standard_padding">
 
             <ImageView
                 android:id="@+id/copy_internal_link_icon"
-                android:layout_width="40dp"
-                android:layout_height="40dp"
-                android:layout_gravity="center_horizontal"
-                android:contentDescription="@string/share"
-                android:src="@drawable/ic_external"
-                android:paddingTop="@dimen/copy_internal_link_padding"
-                android:paddingRight="@dimen/copy_internal_link_padding"
-                android:paddingEnd="@dimen/copy_internal_link_padding"
-                android:paddingBottom="@dimen/copy_internal_link_padding"
-                android:paddingLeft="@dimen/copy_internal_link_padding"
-                android:paddingStart="@dimen/copy_internal_link_padding"
-                android:layout_marginBottom="@dimen/standard_half_margin"
-                android:layout_marginEnd="@dimen/standard_margin"
-                android:layout_marginLeft="@dimen/standard_margin"
-                android:layout_marginRight="@dimen/standard_margin"
+                android:layout_width="@dimen/share_icon_size"
+                android:layout_height="@dimen/share_icon_size"
+                android:layout_gravity="top"
                 android:layout_marginStart="@dimen/standard_margin"
-                android:layout_marginTop="@dimen/standard_half_margin"
-                android:background="@drawable/round_bgnd" />
+                android:layout_marginEnd="@dimen/standard_margin"
+                android:background="@drawable/round_bgnd"
+                android:contentDescription="@string/share"
+                android:padding="@dimen/standard_half_padding"
+                android:src="@drawable/ic_external" />
 
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/standard_half_margin"
                 android:orientation="vertical">
 
                 <TextView
                     android:id="@+id/shareInternalLink"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:textColor="@color/text_color"
-                    android:text="@string/share_internal_link" />
+                    android:text="@string/share_internal_link"
+                    android:textColor="@color/text_color" />
 
                 <TextView
                     android:id="@+id/shareInternalLinkText"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content" />
+                    android:layout_height="wrap_content"
+                    tools:text="@string/share_internal_link_to_folder_text" />
             </LinearLayout>
 
         </LinearLayout>

+ 19 - 3
src/main/res/menu/fragment_file_detail_sharing_link.xml

@@ -22,6 +22,14 @@
     xmlns:tools="http://schemas.android.com/tools"
     tools:ignore="AppCompatResource">
 
+    <item
+        android:id="@+id/action_allow_editing"
+        android:showAsAction="never"
+        android:title="@string/allow_editing"
+        android:checkable="true"
+        android:textSize="16sp"
+        app:showAsAction="never" />
+
     <item
         android:id="@+id/action_hide_file_listing"
         android:showAsAction="never"
@@ -49,11 +57,19 @@
         android:showAsAction="never"
         android:title="@string/share_via_link_send_link_label"
         app:showAsAction="never" />
-
     <item
         android:id="@+id/action_share_send_note"
         android:showAsAction="never"
         android:title="@string/share_send_note"
-        app:showAsAction="never"/>
-
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_unshare"
+        android:showAsAction="never"
+        android:title="@string/unshare"
+        app:showAsAction="never" />
+    <item
+        android:id="@+id/action_add_another_public_share_link"
+        android:showAsAction="never"
+        android:title="@string/add_another_public_share_link"
+        app:showAsAction="never" />
 </menu>

+ 0 - 1
src/main/res/values/dims.xml

@@ -135,7 +135,6 @@
     <dimen name="permission_dialog_text_size">18sp</dimen>
     <dimen name="button_corner_radius">24dp</dimen>
     <integer name="media_grid_width">4</integer>
-    <dimen name="copy_internal_link_padding">10dp</dimen>
     <dimen name="drawer_header_height">130dp</dimen>
     <dimen name="drawer_header_logo_width">170dp</dimen>
     <dimen name="account_action_button_margin">12dp</dimen>

+ 4 - 0
src/main/res/values/strings.xml

@@ -926,4 +926,8 @@
     <string name="conflict_dialog_error">Error creating conflict dialog!</string>
     <string name="qr_could_not_be_read">QR Code could not be read!</string>
     <string name="note_icon_hint">Note icon</string>
+    <string name="add_another_public_share_link">Add another link</string>
+    <string name="unshare">Unshare</string>
+    <string name="allow_editing">Allow editing</string>
+    <string name="add_new_public_share">Add new public share link</string>
 </resources>