瀏覽代碼

Show shared user

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 6 年之前
父節點
當前提交
bf86cf6a41

+ 1 - 1
scripts/analysis/findbugs-results.txt

@@ -1 +1 @@
-481
+483

+ 12 - 0
src/main/java/com/owncloud/android/authentication/AccountUtils.java

@@ -25,9 +25,11 @@ import android.content.Context;
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.preference.PreferenceManager;
+import android.text.TextUtils;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
+import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
@@ -344,4 +346,14 @@ public final class AccountUtils {
         // will assume it succeeds, not a big deal otherwise
         return newAccount;
     }
+
+    /**
+     * Checks if an account owns the file (file's ownerId is the same as account name)
+     * @param file File to check
+     * @param account account to compare
+     * @return false if ownerId is not set or owner is a different account
+     */
+    public static boolean accountOwnsFile(OCFile file, Account account) {
+        return !TextUtils.isEmpty(file.getOwnerId()) && account.name.split("@")[0].equals(file.getOwnerId());
+    }
 }

+ 8 - 0
src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -205,6 +205,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
         cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
         cv.put(ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT, file.getUnreadCommentsCount());
+        cv.put(ProviderTableMeta.FILE_OWNER_ID, file.getOwnerId());
+        cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, file.getOwnerDisplayName());
 
         boolean sameRemotePath = fileExists(file.getRemotePath());
         if (sameRemotePath ||
@@ -443,6 +445,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_FAVORITE, folder.isFavorite());
         cv.put(ProviderTableMeta.FILE_IS_ENCRYPTED, folder.isEncrypted());
         cv.put(ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT, folder.getUnreadCommentsCount());
+        cv.put(ProviderTableMeta.FILE_OWNER_ID, folder.getOwnerId());
+        cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, folder.getOwnerDisplayName());
 
         return cv;
     }
@@ -479,6 +483,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_MOUNT_TYPE, file.getMountType().ordinal());
         cv.put(ProviderTableMeta.FILE_HAS_PREVIEW, file.isPreviewAvailable() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT, file.getUnreadCommentsCount());
+        cv.put(ProviderTableMeta.FILE_OWNER_ID, file.getOwnerId());
+        cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, file.getOwnerDisplayName());
 
         return cv;
     }
@@ -975,6 +981,8 @@ public class FileDataStorageManager {
                     c.getColumnIndex(ProviderTableMeta.FILE_MOUNT_TYPE))]);
             file.setPreviewAvailable(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_HAS_PREVIEW)) == 1);
             file.setUnreadCommentsCount(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT)));
+            file.setOwnerId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_OWNER_ID)));
+            file.setOwnerDisplayName(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME)));
         }
 
         return file;

+ 3 - 3
src/main/java/com/owncloud/android/datamodel/OCFile.java

@@ -84,9 +84,9 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
     @Getter @Setter private boolean favorite;
     @Getter @Setter private boolean encrypted;
     @Getter @Setter private WebdavEntry.MountType mountType;
-    @Getter
-    @Setter
-    private int unreadCommentsCount;
+    @Getter @Setter private int unreadCommentsCount;
+    @Getter @Setter private String ownerId;
+    @Getter @Setter private String ownerDisplayName;
 
     /**
      * URI to the local path of the file contents, if stored in the device; cached after first call

+ 8 - 6
src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java

@@ -64,6 +64,7 @@ import com.owncloud.android.utils.MimeTypeUtil;
 
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.httpclient.methods.GetMethod;
+import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -858,7 +859,7 @@ public final class ThumbnailsCacheManager {
             return Math.round(r.getDimension(R.dimen.file_avatar_size));
         }
 
-        private @Nullable
+        private @NotNull
         Drawable doAvatarInBackground() {
             Bitmap avatar = null;
 
@@ -923,7 +924,6 @@ public final class ThumbnailsCacheManager {
                             // everything else
                             mClient.exhaustResponse(get.getResponseBodyAsStream());
                             break;
-
                     }
                 } catch (Exception e) {
                     try {
@@ -936,15 +936,17 @@ public final class ThumbnailsCacheManager {
                         get.releaseConnection();
                     }
                 }
+            }
 
+            if (avatar == null) {
                 try {
                     return TextDrawable.createAvatar(mAccount.name, mAvatarRadius);
-                } catch (Exception e) {
-                    Log_OC.e(TAG, "Error generating fallback avatar");
+                } catch (Exception e1) {
+                    return mResources.getDrawable(R.drawable.ic_user);
                 }
+            } else {
+                return BitmapUtils.bitmapToCircularBitmapDrawable(mResources, avatar);
             }
-
-            return BitmapUtils.bitmapToCircularBitmapDrawable(mResources, avatar);
         }
     }
 

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

@@ -32,7 +32,7 @@ import com.owncloud.android.MainApp;
 public class ProviderMeta {
 
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 42;
+    public static final int DB_VERSION = 43;
 
     private ProviderMeta() {
     }
@@ -106,6 +106,8 @@ public class ProviderMeta {
         public static final String FILE_MOUNT_TYPE = "mount_type";
         public static final String FILE_HAS_PREVIEW = "has_preview";
         public static final String FILE_UNREAD_COMMENTS_COUNT = "unread_comments_count";
+        public static final String FILE_OWNER_ID = "owner_id";
+        public static final String FILE_OWNER_DISPLAY_NAME = "owner_display_name";
 
         public static final String[] FILE_ALL_COLUMNS = {
             _ID, FILE_PARENT, FILE_NAME, FILE_CREATION, FILE_MODIFIED,

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

@@ -698,7 +698,9 @@ public class FileContentProvider extends ContentProvider {
                        + ProviderTableMeta.FILE_SHARED_WITH_SHAREE + INTEGER
                        + ProviderTableMeta.FILE_MOUNT_TYPE + INTEGER
                        + ProviderTableMeta.FILE_HAS_PREVIEW + INTEGER
-                       + ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT + " INTEGER);"
+                       + ProviderTableMeta.FILE_UNREAD_COMMENTS_COUNT + INTEGER
+                       + ProviderTableMeta.FILE_OWNER_ID + TEXT
+                       + ProviderTableMeta.FILE_OWNER_DISPLAY_NAME + " TEXT);"
         );
     }
 
@@ -1860,6 +1862,26 @@ public class FileContentProvider extends ContentProvider {
             if (!upgraded) {
                 Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
             }
+
+            if (oldVersion < 43 && newVersion >= 43) {
+                Log_OC.i(SQL, "Entering in the #43 add ownerId and owner display name to file table");
+                db.beginTransaction();
+                try {
+                    db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
+                            ADD_COLUMN + ProviderTableMeta.FILE_OWNER_ID + " TEXT ");
+                    db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
+                            ADD_COLUMN + ProviderTableMeta.FILE_OWNER_DISPLAY_NAME + " TEXT ");
+
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
+            if (!upgraded) {
+                Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
+            }
         }
 
         @Override

+ 182 - 93
src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

@@ -22,67 +22,76 @@
 package com.owncloud.android.ui.adapter;
 
 
-import android.accounts.Account;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Filter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.datamodel.VirtualFolderType;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.db.ProviderMeta;
-import com.owncloud.android.files.services.FileDownloader;
-import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.lib.common.operations.RemoteOperation;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
-import com.owncloud.android.lib.resources.files.model.RemoteFile;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.lib.resources.shares.ShareType;
-import com.owncloud.android.operations.RefreshFolderOperation;
-import com.owncloud.android.operations.RemoteOperationFailedException;
-import com.owncloud.android.services.OperationsService;
-import com.owncloud.android.ui.activity.ComponentsGetter;
-import com.owncloud.android.ui.fragment.ExtendedListFragment;
-import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.FileSortOrder;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-import com.owncloud.android.utils.ThemeUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.Vector;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-/**
+    import android.accounts.Account;
+    import android.content.ContentValues;
+    import android.content.Context;
+    import android.content.res.Resources;
+    import android.graphics.Bitmap;
+    import android.graphics.Color;
+    import android.graphics.drawable.Drawable;
+    import android.os.Handler;
+    import android.os.Looper;
+    import android.text.TextUtils;
+    import android.view.LayoutInflater;
+    import android.view.View;
+    import android.view.ViewGroup;
+    import android.widget.Filter;
+    import android.widget.ImageView;
+    import android.widget.LinearLayout;
+    import android.widget.TextView;
+
+    import com.bumptech.glide.Glide;
+    import com.bumptech.glide.request.target.BitmapImageViewTarget;
+    import com.owncloud.android.R;
+    import com.owncloud.android.authentication.AccountUtils;
+    import com.owncloud.android.datamodel.FileDataStorageManager;
+    import com.owncloud.android.datamodel.OCFile;
+    import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+    import com.owncloud.android.datamodel.VirtualFolderType;
+    import com.owncloud.android.db.PreferenceManager;
+    import com.owncloud.android.db.ProviderMeta;
+    import com.owncloud.android.files.services.FileDownloader;
+    import com.owncloud.android.files.services.FileUploader;
+    import com.owncloud.android.lib.common.operations.RemoteOperation;
+    import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+    import com.owncloud.android.lib.common.utils.Log_OC;
+    import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
+    import com.owncloud.android.lib.resources.files.model.RemoteFile;
+    import com.owncloud.android.lib.resources.shares.OCShare;
+    import com.owncloud.android.lib.resources.shares.ShareType;
+    import com.owncloud.android.operations.RefreshFolderOperation;
+    import com.owncloud.android.operations.RemoteOperationFailedException;
+    import com.owncloud.android.services.OperationsService;
+    import com.owncloud.android.ui.TextDrawable;
+    import com.owncloud.android.ui.activity.ComponentsGetter;
+    import com.owncloud.android.ui.fragment.ExtendedListFragment;
+    import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
+    import com.owncloud.android.utils.DisplayUtils;
+    import com.owncloud.android.utils.FileSortOrder;
+    import com.owncloud.android.utils.FileStorageUtils;
+    import com.owncloud.android.utils.MimeTypeUtil;
+    import com.owncloud.android.utils.ThemeUtils;
+
+    import java.io.File;
+    import java.util.ArrayList;
+    import java.util.HashSet;
+    import java.util.List;
+    import java.util.Locale;
+    import java.util.Set;
+    import java.util.Vector;
+
+    import androidx.annotation.NonNull;
+    import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+    import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+    import androidx.recyclerview.widget.RecyclerView;
+    import butterknife.BindView;
+    import butterknife.ButterKnife;
+
+    /**
  * This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance.
  */
-public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
+    implements DisplayUtils.AvatarGenerationListener {
 
     private static final int showFilenameColumnThreshold = 4;
     private final FileDownloader.FileDownloaderBinder downloaderBinder;
@@ -308,6 +317,27 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
             if (holder instanceof OCFileListItemViewHolder) {
                 OCFileListItemViewHolder itemViewHolder = (OCFileListItemViewHolder) holder;
 
+                if (file.isSharedWithMe() && !multiSelect && !gridView && !mHideItemOptions) {
+                    itemViewHolder.sharedAvatar.setVisibility(View.VISIBLE);
+
+                    Resources resources = mContext.getResources();
+
+                    float avatarRadius = resources.getDimension(R.dimen.list_item_avatar_icon_radius);
+
+                    if (file.getOwnerId().contains("@")) {
+                        showFederatedShareAvatar(file, avatarRadius, resources, itemViewHolder);
+                    } else {
+                        itemViewHolder.sharedAvatar.setTag(file.getOwnerId());
+                        DisplayUtils.setAvatar(mAccount, file.getOwnerId(), this, avatarRadius, resources,
+                                               itemViewHolder.sharedAvatar, mContext);
+                    }
+
+                    itemViewHolder.sharedAvatar.setOnClickListener(view ->
+                                                                       ocFileListFragmentInterface.showShareDetailView(file));
+                } else {
+                    itemViewHolder.sharedAvatar.setVisibility(View.GONE);
+                }
+
                 if (onlyOnDevice) {
                     File localFile = new File(file.getStoragePath());
 
@@ -393,6 +423,39 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         }
     }
 
+        private void showFederatedShareAvatar(OCFile file, float avatarRadius, Resources resources,
+                                              OCFileListItemViewHolder itemViewHolder) {
+            // maybe federated share
+            String userId = file.getOwnerId().split("@")[0];
+            String server = file.getOwnerId().split("@")[1];
+
+            String url = "https://" + server + "/avatar/" + userId + "/" +
+                DisplayUtils.convertDpToPixel(avatarRadius, mContext);
+
+            Drawable placeholder;
+            try {
+                placeholder = TextDrawable.createAvatarByUserId(userId, avatarRadius);
+            } catch (Exception e) {
+                Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e);
+                placeholder = resources.getDrawable(R.drawable.account_circle_white);
+            }
+
+            itemViewHolder.sharedAvatar.setTag(null);
+            Glide.with(mContext).load(url)
+                .asBitmap()
+                .placeholder(placeholder)
+                .error(placeholder)
+                .into(new BitmapImageViewTarget(itemViewHolder.sharedAvatar) {
+                    @Override
+                    protected void setResource(Bitmap resource) {
+                        RoundedBitmapDrawable circularBitmapDrawable =
+                            RoundedBitmapDrawableFactory.create(mContext.getResources(), resource);
+                        circularBitmapDrawable.setCircular(true);
+                        itemViewHolder.sharedAvatar.setImageDrawable(circularBitmapDrawable);
+                    }
+                });
+        }
+
     private void setThumbnail(OCFile file, ImageView thumbnailView) {
         if (file.isFolder()) {
             thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
@@ -513,9 +576,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         if (gridViewHolder instanceof OCFileListItemViewHolder || file.getUnreadCommentsCount() == 0) {
             sharedIconView.setVisibility(View.VISIBLE);
 
-            if (file.isSharedWithSharee() || file.isSharedWithMe()) {
+            if (file.isSharedWithSharee()) {
                 sharedIconView.setImageResource(R.drawable.shared_via_users);
                 sharedIconView.setContentDescription(mContext.getString(R.string.shared_icon_shared));
+            } else if (file.isSharedWithMe()) {
+                sharedIconView.setVisibility(View.GONE);
             } else if (file.isSharedViaLink()) {
                 sharedIconView.setImageResource(R.drawable.shared_via_link);
                 sharedIconView.setContentDescription(mContext.getString(R.string.shared_icon_shared_via_link));
@@ -523,7 +588,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
                 sharedIconView.setImageResource(R.drawable.ic_unshared);
                 sharedIconView.setContentDescription(mContext.getString(R.string.shared_icon_share));
             }
-            sharedIconView.setOnClickListener(view -> ocFileListFragmentInterface.onShareIconClick(file));
+            if (AccountUtils.accountOwnsFile(file, mAccount)) {
+                sharedIconView.setOnClickListener(view -> ocFileListFragmentInterface.onShareIconClick(file));
+            } else {
+                sharedIconView.setOnClickListener(view -> ocFileListFragmentInterface.showShareDetailView(file));
+            }
         } else {
             sharedIconView.setVisibility(View.GONE);
         }
@@ -732,6 +801,16 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         return mFilesFilter;
     }
 
+    @Override
+    public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
+        ((ImageView) callContext).setImageDrawable(avatarDrawable);
+    }
+
+    @Override
+    public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
+        return ((ImageView) callContext).getTag().equals(tag);
+    }
+
     private class FilesFilter extends Filter {
 
         @Override
@@ -825,59 +904,69 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         gridView = bool;
     }
 
-    private static class OCFileListItemViewHolder extends OCFileListGridItemViewHolder {
-        private final TextView fileSize;
-        private final TextView lastModification;
-        private final ImageView overflowMenu;
+        static class OCFileListItemViewHolder extends OCFileListGridItemViewHolder {
+        @BindView(R.id.file_size)
+        public TextView fileSize;
+
+        @BindView(R.id.last_mod)
+        public TextView lastModification;
+
+        @BindView(R.id.overflow_menu)
+        public ImageView overflowMenu;
+
+        @BindView(R.id.sharedAvatar)
+        public ImageView sharedAvatar;
 
         private OCFileListItemViewHolder(View itemView) {
             super(itemView);
-
-            fileSize = itemView.findViewById(R.id.file_size);
-            lastModification = itemView.findViewById(R.id.last_mod);
-            overflowMenu = itemView.findViewById(R.id.overflow_menu);
+            ButterKnife.bind(this, itemView);
         }
     }
 
-    static class OCFileListGridItemViewHolder extends OCFileListGridImageViewHolder {
-        private final TextView fileName;
+        static class OCFileListGridItemViewHolder extends OCFileListGridImageViewHolder {
+        @BindView(R.id.Filename) public TextView fileName;
 
         private OCFileListGridItemViewHolder(View itemView) {
             super(itemView);
-
-            fileName = itemView.findViewById(R.id.Filename);
+            ButterKnife.bind(this, itemView);
         }
     }
 
-    static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder {
-        private final ImageView thumbnail;
-        private final ImageView favorite;
-        private final ImageView localFileIndicator;
-        private final ImageView shared;
-        private final ImageView checkbox;
-        final ImageView unreadComments;
-        private final LinearLayout itemLayout;
+        static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder {
+       @BindView(R.id.thumbnail)
+       public ImageView thumbnail;
+
+       @BindView(R.id.favorite_action)
+       public ImageView favorite;
+
+       @BindView(R.id.localFileIndicator)
+       public ImageView localFileIndicator;
+
+       @BindView(R.id.sharedIcon)
+       public ImageView shared;
+
+       @BindView(R.id.custom_checkbox)
+       public ImageView checkbox;
+
+       @BindView(R.id.ListItemLayout)
+       public LinearLayout itemLayout;
+
+       @BindView(R.id.unreadComments)
+        public ImageView unreadComments;
 
         private OCFileListGridImageViewHolder(View itemView) {
             super(itemView);
-
-            thumbnail = itemView.findViewById(R.id.thumbnail);
-            favorite = itemView.findViewById(R.id.favorite_action);
-            localFileIndicator = itemView.findViewById(R.id.localFileIndicator);
-            shared = itemView.findViewById(R.id.sharedIcon);
-            checkbox = itemView.findViewById(R.id.custom_checkbox);
-            unreadComments = itemView.findViewById(R.id.unreadComments);
-            itemLayout = itemView.findViewById(R.id.ListItemLayout);
+            ButterKnife.bind(this, itemView);
         }
     }
 
-    private static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder {
-        private final TextView footerText;
+        static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder {
+        @BindView(R.id.footerText)
+        public TextView footerText;
 
         private OCFileListFooterViewHolder(View itemView) {
             super(itemView);
-
-            footerText = itemView.findViewById(R.id.footerText);
+            ButterKnife.bind(this, itemView);
         }
     }
 }

+ 41 - 4
src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java

@@ -24,6 +24,7 @@ import android.accounts.Account;
 import android.app.SearchManager;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -38,6 +39,7 @@ import android.widget.TextView;
 
 import com.google.android.material.snackbar.Snackbar;
 import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -73,7 +75,8 @@ import butterknife.ButterKnife;
 import butterknife.OnClick;
 import butterknife.Unbinder;
 
-public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener {
+public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener,
+    DisplayUtils.AvatarGenerationListener {
 
     private static final String ARG_FILE = "FILE";
     private static final String ARG_ACCOUNT = "ACCOUNT";
@@ -86,6 +89,10 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt
     private OCCapability capabilities;
     private OCShare publicShare;
 
+    private FileOperationsHelper fileOperationsHelper;
+    private FileDisplayActivity fileDisplayActivity;
+    private FileDataStorageManager fileDataStorageManager;
+
     private Unbinder unbinder;
 
     @BindView(R.id.searchView)
@@ -112,9 +119,14 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt
     @BindView(R.id.share_by_link_container)
     LinearLayout shareByLinkContainer;
 
-    private FileOperationsHelper fileOperationsHelper;
-    private FileDisplayActivity fileDisplayActivity;
-    private FileDataStorageManager fileDataStorageManager;
+    @BindView(R.id.shared_with_you_container)
+    LinearLayout sharedWithYouContainer;
+
+    @BindView(R.id.shared_with_you_avatar)
+    ImageView sharedWithYouAvatar;
+
+    @BindView(R.id.shared_with_you_username)
+    TextView sharedWithYouUsername;
 
     public static FileDetailSharingFragment newInstance(OCFile file, Account account) {
         FileDetailSharingFragment fragment = new FileDetailSharingFragment();
@@ -198,6 +210,7 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt
     private void setupView() {
         setShareByLinkInfo(file.isSharedViaLink());
         setShareWithUserInfo();
+        setShareWithYou();
         FileDetailSharingFragmentHelper.setupSearchView(
             (SearchManager) fileDisplayActivity.getSystemService(Context.SEARCH_SERVICE), searchView,
             fileDisplayActivity.getComponentName());
@@ -246,6 +259,20 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt
         updateListOfUserGroups();
     }
 
+    private void setShareWithYou() {
+        if (AccountUtils.accountOwnsFile(file, account)) {
+            sharedWithYouContainer.setVisibility(View.GONE);
+        } else {
+            sharedWithYouUsername.setText(
+                String.format(getString(R.string.shared_with_you_by), file.getOwnerDisplayName()));
+
+            DisplayUtils.setAvatar(account, file.getOwnerId(), this, getResources().getDimension(
+                R.dimen.file_list_item_avatar_icon_radius), getResources(), sharedWithYouAvatar,
+                getContext());
+            sharedWithYouAvatar.setVisibility(View.VISIBLE);
+        }
+    }
+
     private void updateListOfUserGroups() {
         // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed
 
@@ -546,4 +573,14 @@ public class FileDetailSharingFragment extends Fragment implements UserListAdapt
         outState.putParcelable(FileActivity.EXTRA_FILE, file);
         outState.putParcelable(FileActivity.EXTRA_ACCOUNT, account);
     }
+
+    @Override
+    public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
+        sharedWithYouAvatar.setImageDrawable(avatarDrawable);
+    }
+
+    @Override
+    public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
+        return false;
+    }
 }

+ 2 - 0
src/main/java/com/owncloud/android/utils/FileStorageUtils.java

@@ -207,6 +207,8 @@ public final class FileStorageUtils {
         file.setMountType(remote.getMountType());
         file.setPreviewAvailable(remote.isHasPreview());
         file.setUnreadCommentsCount(remote.getUnreadCommentsCount());
+        file.setOwnerId(remote.getOwnerId());
+        file.setOwnerDisplayName(remote.getOwnerDisplayName());
 
         return file;
     }

+ 39 - 5
src/main/res/layout/file_details_sharing_fragment.xml

@@ -34,12 +34,46 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:id="@+id/searchView"
-            android:hint="@string/share_search"
             android:layout_marginStart="@dimen/standard_eighth_margin"
             android:layout_marginLeft="@dimen/standard_eighth_margin"
             android:layout_marginEnd="@dimen/standard_margin"
             android:layout_marginRight="@dimen/standard_margin"
-            style="@style/ownCloud.SearchView" />
+            style="@style/ownCloud.SearchView"
+            android:hint="@string/share_search"/>
+
+        <LinearLayout
+            android:id="@+id/shared_with_you_container"
+            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">
+
+            <ImageView
+                android:id="@+id/shared_with_you_avatar"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:contentDescription="@string/avatar"
+                app:srcCompat="@drawable/ic_user"/>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/shared_with_you_username"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingLeft="@dimen/standard_padding"
+                    android:paddingTop="@dimen/standard_half_padding"
+                    android:paddingRight="@dimen/standard_padding"
+                    android:text="@string/shared_with_you_by"
+                    android:textSize="16sp"/>
+            </LinearLayout>
+        </LinearLayout>
 
         <LinearLayout
             android:id="@+id/share_by_link_container"
@@ -106,7 +140,7 @@
                 android:paddingLeft="@dimen/standard_half_padding"
                 android:paddingEnd="@dimen/zero"
                 android:paddingRight="@dimen/zero"
-                android:src="@drawable/ic_dots_vertical" />
+                android:src="@drawable/ic_dots_vertical"/>
         </LinearLayout>
 
         <androidx.recyclerview.widget.RecyclerView
@@ -121,11 +155,11 @@
             android:id="@+id/shareNoUsers"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingTop="@dimen/standard_half_padding"
             android:paddingLeft="@dimen/standard_padding"
+            android:paddingTop="@dimen/standard_half_padding"
             android:paddingRight="@dimen/standard_padding"
             android:text="@string/share_no_users"
-            android:textSize="16sp" />
+            android:textSize="16sp"/>
 
     </LinearLayout>
 

+ 15 - 2
src/main/res/layout/list_item.xml

@@ -171,13 +171,26 @@
             android:paddingStart="@dimen/standard_half_padding"
             android:src="@drawable/ic_unshared" />
 
+        <ImageView
+            android:id="@+id/sharedAvatar"
+            android:layout_width="@dimen/file_icon_size"
+            android:layout_height="@dimen/file_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/sharedIcon"
+            android:layout_toRightOf="@id/sharedIcon"
+            android:contentDescription="@string/shared_avatar_desc"
+            android:clickable="true"
+            android:focusable="true"
+            android:src="@drawable/ic_user"
+            android:visibility="gone" />
+
         <ImageView
             android:id="@+id/custom_checkbox"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/sharedIcon"
-            android:layout_toRightOf="@id/sharedIcon"
+            android:layout_toEndOf="@id/sharedAvatar"
+            android:layout_toRightOf="@id/sharedAvatar"
             android:clickable="false"
             android:contentDescription="@string/checkbox"
             android:focusable="false"

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

@@ -49,6 +49,7 @@
     <dimen name="list_item_avatar_icon_margin">20dp</dimen>
     <dimen name="list_item_avatar_text_margin">20dp</dimen>
     <dimen name="list_item_share_right_margin">12dp</dimen>
+    <dimen name="file_list_item_avatar_icon_radius">10dp</dimen>
     <dimen name="account_action_layout_height">72dp</dimen>
     <dimen name="zero">0dp</dimen>
     <dimen name="account_item_layout_height">72dp</dimen>

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

@@ -873,4 +873,6 @@
     <string name="io_error">IO error</string>
     <string name="operation_canceled">Operation has been canceled</string>
     <string name="authentication_exception">Authentication Exception</string>
+    <string name="shared_avatar_desc">Avatar from shared user</string>
+    <string name="shared_with_you_by">Shared with you by %1$s</string>
 </resources>