Browse Source

Merge pull request #4784 from nextcloud/hideAutoUploadEntries

Show/Hide auto upload list items
Tobias Kaminsky 5 years ago
parent
commit
2c57e37097

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

@@ -1 +1 @@
-418
+407

+ 7 - 2
spotbugs-filter.xml

@@ -17,7 +17,8 @@
     <Match>
         <Or>
             <Class name="~.*\.Dagger.*" />
-            <Class name="~com.nextcloud.client.di\..*_.*" />
+            <Class name="~com\.nextcloud\.client\.di\..*_.*" />
+			<Class name="~dagger\.android\..*" />
         </Or>
     </Match>
     <!-- Dagger generated code uses internal APIs -->
@@ -30,7 +31,7 @@
     <Match>
         <Or>
             <Class name="~.*BindingImpl"/>
-            <Class name="~.*\.DataBinderMapperImpl"/>
+            <Class name="~.*\.DataBinderMapperImpl"/>	
         </Or>
     </Match>
 
@@ -39,6 +40,10 @@
 	    <Or>
 	        <Package name="~io\.noties\..*" />
 			<Package name="~third_parties\.ezvcard_android\..*" />
+			<Package name="~com\.afollestad\.sectionedrecyclerview\..*" />
+			<Package name="~butterknife\..*" />
+			<Package name="~de\.cotech\..*" />
+			<Package name="~pl\.droidsonroids\..*" />
 		</Or>
 	</Match>
 	 <Match>

+ 31 - 13
src/main/java/com/owncloud/android/datamodel/SyncedFolder.java

@@ -23,14 +23,12 @@ package com.owncloud.android.datamodel;
 
 import java.io.Serializable;
 
-import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Setter;
 
 /**
  * Synced folder entity containing all information per synced folder.
  */
-@AllArgsConstructor
 public class SyncedFolder implements Serializable, Cloneable {
     public static final long UNPERSISTED_ID = Long.MIN_VALUE;
     public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1;
@@ -39,14 +37,15 @@ public class SyncedFolder implements Serializable, Cloneable {
     @Getter @Setter private long id;
     @Getter @Setter private String localPath;
     @Getter @Setter private String remotePath;
-    @Getter @Setter private Boolean wifiOnly;
-    @Getter @Setter private Boolean chargingOnly;
-    @Getter @Setter private Boolean subfolderByDate;
+    @Getter @Setter private boolean wifiOnly;
+    @Getter @Setter private boolean chargingOnly;
+    @Getter @Setter private boolean subfolderByDate;
     @Getter @Setter private String account;
-    @Getter @Setter private Integer uploadAction;
+    @Getter @Setter private int uploadAction;
     @Getter private boolean enabled;
     @Getter private long enabledTimestampMs;
     @Getter @Setter private MediaFolderType type;
+    @Getter @Setter private boolean hidden;
 
     /**
      * constructor for new, to be persisted entity.
@@ -61,12 +60,21 @@ public class SyncedFolder implements Serializable, Cloneable {
      * @param enabled         flag if synced folder config is active
      * @param timestampMs     the current timestamp in milliseconds
      * @param type            the type of the folder
+     * @param hidden          hide item flag
      */
-    public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
-                        Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
-                        long timestampMs, MediaFolderType type) {
+    public SyncedFolder(String localPath,
+                        String remotePath,
+                        boolean wifiOnly,
+                        boolean chargingOnly,
+                        boolean subfolderByDate,
+                        String account,
+                        int uploadAction,
+                        boolean enabled,
+                        long timestampMs,
+                        MediaFolderType type,
+                        boolean hidden) {
         this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction,
-             enabled, timestampMs, type);
+             enabled, timestampMs, type, hidden);
     }
 
     /**
@@ -74,9 +82,18 @@ public class SyncedFolder implements Serializable, Cloneable {
      *
      * @param id id
      */
-    protected SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
-                           Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
-                           long timestampMs, MediaFolderType type) {
+    protected SyncedFolder(long id,
+                           String localPath,
+                           String remotePath,
+                           boolean wifiOnly,
+                           boolean chargingOnly,
+                           boolean subfolderByDate,
+                           String account,
+                           int uploadAction,
+                           boolean enabled,
+                           long timestampMs,
+                           MediaFolderType type,
+                           boolean hidden) {
         this.id = id;
         this.localPath = localPath;
         this.remotePath = remotePath;
@@ -87,6 +104,7 @@ public class SyncedFolder implements Serializable, Cloneable {
         this.uploadAction = uploadAction;
         this.setEnabled(enabled, timestampMs);
         this.type = type;
+        this.hidden = hidden;
     }
 
     /**

+ 29 - 10
src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java

@@ -53,24 +53,43 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
      * @param folderName      the UI info for the folder's name
      * @param numberOfFiles   the UI info for number of files within the folder
      * @param type            the type of the folder
+     * @param hidden          hide item flag
      */
-    public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
-                                   Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
-                                   long timestampMs, List<String> filePaths, String folderName, long numberOfFiles,
-                                   MediaFolderType type)
-    {
+    public SyncedFolderDisplayItem(long id,
+                                   String localPath,
+                                   String remotePath,
+                                   boolean wifiOnly,
+                                   boolean chargingOnly,
+                                   boolean subfolderByDate,
+                                   String account,
+                                   int uploadAction,
+                                   boolean enabled,
+                                   long timestampMs,
+                                   List<String> filePaths,
+                                   String folderName,
+                                   long numberOfFiles,
+                                   MediaFolderType type,
+                                   boolean hidden) {
         super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled,
-              timestampMs, type);
+              timestampMs, type, hidden);
         this.filePaths = filePaths;
         this.folderName = folderName;
         this.numberOfFiles = numberOfFiles;
     }
 
-    public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
-                                   Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
-                                   long timestampMs, String folderName, MediaFolderType type) {
+    public SyncedFolderDisplayItem(long id,
+                                   String localPath,
+                                   String remotePath,
+                                   boolean wifiOnly,
+                                   boolean chargingOnly,
+                                   boolean subfolderByDate,
+                                   String account,
+                                   int uploadAction,
+                                   boolean enabled,
+                                   long timestampMs,
+                                   String folderName, MediaFolderType type, boolean hidden) {
         super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled,
-              timestampMs, type);
+              timestampMs, type, hidden);
         this.folderName = folderName;
     }
 }

+ 12 - 9
src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java

@@ -338,25 +338,27 @@ public class SyncedFolderProvider extends Observable {
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
             String remotePath = cursor.getString(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
-            Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(
+            boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
-            Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
+            boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
-            Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
+            boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
             String accountName = cursor.getString(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
-            Integer uploadAction = cursor.getInt(cursor.getColumnIndex(
+            int uploadAction = cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
-            Boolean enabled = cursor.getInt(cursor.getColumnIndex(
+            boolean enabled = cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
             long enabledTimestampMs = cursor.getLong(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS));
             MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex(
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)));
+            boolean hidden = cursor.getInt(cursor.getColumnIndex(
+                ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1;
 
             syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
-                    accountName, uploadAction, enabled, enabledTimestampMs, type);
+                    accountName, uploadAction, enabled, enabledTimestampMs, type, hidden);
         }
         return syncedFolder;
     }
@@ -372,14 +374,15 @@ public class SyncedFolderProvider extends Observable {
         ContentValues cv = new ContentValues();
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH, syncedFolder.getLocalPath());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath());
-        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.getWifiOnly());
-        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.getChargingOnly());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.isWifiOnly());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.isChargingOnly());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs());
-        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden());
 
         return cv;
     }

+ 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 = 50;
+    public static final int DB_VERSION = 51;
 
     private ProviderMeta() {
         // No instance
@@ -225,6 +225,7 @@ public class ProviderMeta {
         public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date";
         public static final String SYNCED_FOLDER_ACCOUNT = "account";
         public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option";
+        public static final String SYNCED_FOLDER_HIDDEN = "hidden";
 
         // Columns of external links table
         public static final String EXTERNAL_LINKS_ICON_URL = "icon_url";

+ 3 - 3
src/main/java/com/owncloud/android/jobs/FilesSyncJob.java

@@ -192,10 +192,10 @@ public class FilesSyncJob extends Job {
 
                 remotePath = resources.getString(R.string.syncedFolder_remote_folder);
             } else {
-                needsCharging = syncedFolder.getChargingOnly();
-                needsWifi = syncedFolder.getWifiOnly();
+                needsCharging = syncedFolder.isChargingOnly();
+                needsWifi = syncedFolder.isWifiOnly();
                 uploadAction = syncedFolder.getUploadAction();
-                subfolderByDate = syncedFolder.getSubfolderByDate();
+                subfolderByDate = syncedFolder.isSubfolderByDate();
                 remotePath = syncedFolder.getRemotePath();
             }
 

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

@@ -833,7 +833,8 @@ public class FileContentProvider extends ContentProvider {
                        + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date
                        + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + "  TEXT, "             // account
                        + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, "     // upload action
-                       + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER );"             // type
+                       + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER, "              // type
+                       + ProviderTableMeta.SYNCED_FOLDER_HIDDEN + " INTEGER );"           // hidden
         );
     }
 
@@ -2045,6 +2046,24 @@ public class FileContentProvider extends ContentProvider {
             if (!upgraded) {
                 Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
             }
+
+            if (oldVersion < 51 && newVersion >= 51) {
+                Log_OC.i(SQL, "Entering in the #51 add show/hide to folderSync table");
+                db.beginTransaction();
+                try {
+                    db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME +
+                                   ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_HIDDEN + " INTEGER ");
+
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
+            if (!upgraded) {
+                Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
+            }
         }
 
         @Override

+ 218 - 98
src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java

@@ -21,7 +21,6 @@
 
 package com.owncloud.android.ui.activity;
 
-import android.accounts.Account;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.NotificationManager;
@@ -38,9 +37,12 @@ import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.google.android.material.button.MaterialButton;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.core.Clock;
 import com.nextcloud.client.device.PowerManagementService;
@@ -50,6 +52,7 @@ import com.nextcloud.java.util.Optional;
 import com.owncloud.android.BuildConfig;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.MediaFolder;
 import com.owncloud.android.datamodel.MediaFolderType;
 import com.owncloud.android.datamodel.MediaProvider;
@@ -88,6 +91,9 @@ import androidx.fragment.app.FragmentManager;
 import androidx.fragment.app.FragmentTransaction;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
 
 import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
 import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID;
@@ -103,12 +109,30 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
     private static final String SYNCED_FOLDER_PREFERENCES_DIALOG_TAG = "SYNCED_FOLDER_PREFERENCES_DIALOG";
     private static final String TAG = SyncedFoldersActivity.class.getSimpleName();
 
-    private RecyclerView mRecyclerView;
-    private SyncedFolderAdapter mAdapter;
-    private LinearLayout mProgress;
-    private TextView mEmpty;
-    private SyncedFolderProvider mSyncedFolderProvider;
-    private SyncedFolderPreferencesDialogFragment mSyncedFolderPreferencesDialogFragment;
+    @BindView(R.id.empty_list_view)
+    public LinearLayout emptyContentContainer;
+
+    @BindView(R.id.empty_list_icon)
+    public ImageView emptyContentIcon;
+
+    @BindView(R.id.empty_list_progress)
+    public ProgressBar emptyContentProgressBar;
+
+    @BindView(R.id.empty_list_view_headline)
+    public TextView emptyContentHeadline;
+
+    @BindView(R.id.empty_list_view_text)
+    public TextView emptyContentMessage;
+
+    @BindView(R.id.empty_list_view_action)
+    public MaterialButton emptyContentActionButton;
+
+    @BindView(android.R.id.list)
+    public RecyclerView mRecyclerView;
+
+    private SyncedFolderAdapter adapter;
+    private SyncedFolderProvider syncedFolderProvider;
+    private SyncedFolderPreferencesDialogFragment syncedFolderPreferencesDialogFragment;
     private boolean showSidebar = true;
 
     private String path;
@@ -126,6 +150,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
         }
 
         setContentView(R.layout.synced_folders_layout);
+        ButterKnife.bind(this);
 
         if (getIntent() != null && getIntent().getExtras() != null) {
             final String accountName = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT);
@@ -219,49 +244,56 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
      * sets up the UI elements and loads all media/synced folders.
      */
     private void setupContent() {
-        mRecyclerView = findViewById(android.R.id.list);
-
-        mProgress = findViewById(android.R.id.progress);
-        mEmpty = findViewById(android.R.id.empty);
-
         final int gridWidth = getResources().getInteger(R.integer.media_grid_width);
         boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light);
-        mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion);
-        mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock);
+        adapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion);
+        syncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock);
+        emptyContentIcon.setImageResource(R.drawable.nav_synced_folders);
+        emptyContentActionButton.setBackgroundColor(ThemeUtils.primaryColor(this));
+        emptyContentActionButton.setTextColor(ThemeUtils.fontColor(this));
 
         final GridLayoutManager lm = new GridLayoutManager(this, gridWidth);
-        mAdapter.setLayoutManager(lm);
+        adapter.setLayoutManager(lm);
         int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing);
         mRecyclerView.addItemDecoration(new MediaGridItemDecoration(spacing));
         mRecyclerView.setLayoutManager(lm);
-        mRecyclerView.setAdapter(mAdapter);
+        mRecyclerView.setAdapter(adapter);
 
         load(gridWidth * 2, false);
     }
 
+    @OnClick(R.id.empty_list_view_action)
+    public void showHiddenItems() {
+        if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() > adapter.getSectionCount()) {
+            adapter.toggleHiddenItemsVisibility();
+            emptyContentContainer.setVisibility(View.GONE);
+            mRecyclerView.setVisibility(View.VISIBLE);
+        }
+    }
+
     /**
      * loads all media/synced folders, adds them to the recycler view adapter and shows the list.
      *
      * @param perFolderMediaItemLimit the amount of media items to be loaded/shown per media folder
      */
     private void load(final int perFolderMediaItemLimit, boolean force) {
-        if (mAdapter.getItemCount() > 0 && !force) {
+        if (adapter.getItemCount() > 0 && !force) {
             return;
         }
-        setListShown(false);
+        showLoadingContent();
         final List<MediaFolder> mediaFolders = MediaProvider.getImageFolders(getContentResolver(),
                 perFolderMediaItemLimit, this, false);
         mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit,
                 this, false));
 
-        List<SyncedFolder> syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders();
+        List<SyncedFolder> syncedFolderArrayList = syncedFolderProvider.getSyncedFolders();
         List<SyncedFolder> currentAccountSyncedFoldersList = new ArrayList<>();
         User user = getUserAccountManager().getUser();
         for (SyncedFolder syncedFolder : syncedFolderArrayList) {
             if (syncedFolder.getAccount().equals(user.getAccountName())) {
                 // delete non-existing & disabled synced folders
                 if (!new File(syncedFolder.getLocalPath()).exists() && !syncedFolder.isEnabled()) {
-                    mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId());
+                    syncedFolderProvider.deleteSyncedFolder(syncedFolder.getId());
                 } else {
                     currentAccountSyncedFoldersList.add(syncedFolder);
                 }
@@ -271,14 +303,14 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
         List<SyncedFolderDisplayItem> syncFolderItems = sortSyncedFolderItems(
                 mergeFolderData(currentAccountSyncedFoldersList, mediaFolders));
 
-        mAdapter.setSyncFolderItems(syncFolderItems);
-        mAdapter.notifyDataSetChanged();
-        setListShown(true);
+        adapter.setSyncFolderItems(syncFolderItems);
+        adapter.notifyDataSetChanged();
+        showList();
 
         if (!TextUtils.isEmpty(path)) {
-            int section = mAdapter.getSectionByLocalPathAndType(path, type);
+            int section = adapter.getSectionByLocalPathAndType(path, type);
             if (section >= 0) {
-                onSyncFolderSettingsClick(section, mAdapter.get(section));
+                onSyncFolderSettingsClick(section, adapter.get(section));
             }
         }
     }
@@ -382,9 +414,9 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 syncedFolder.getId(),
                 syncedFolder.getLocalPath(),
                 syncedFolder.getRemotePath(),
-                syncedFolder.getWifiOnly(),
-                syncedFolder.getChargingOnly(),
-                syncedFolder.getSubfolderByDate(),
+                syncedFolder.isWifiOnly(),
+                syncedFolder.isChargingOnly(),
+                syncedFolder.isSubfolderByDate(),
                 syncedFolder.getAccount(),
                 syncedFolder.getUploadAction(),
                 syncedFolder.isEnabled(),
@@ -392,7 +424,8 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 filePaths,
                 localFolder.getName(),
                 files.length,
-                syncedFolder.getType());
+                syncedFolder.getType(),
+                syncedFolder.isHidden());
     }
 
     /**
@@ -408,9 +441,9 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 syncedFolder.getId(),
                 syncedFolder.getLocalPath(),
                 syncedFolder.getRemotePath(),
-                syncedFolder.getWifiOnly(),
-                syncedFolder.getChargingOnly(),
-                syncedFolder.getSubfolderByDate(),
+                syncedFolder.isWifiOnly(),
+                syncedFolder.isChargingOnly(),
+                syncedFolder.isSubfolderByDate(),
                 syncedFolder.getAccount(),
                 syncedFolder.getUploadAction(),
                 syncedFolder.isEnabled(),
@@ -418,7 +451,8 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 mediaFolder.filePaths,
                 mediaFolder.folderName,
                 mediaFolder.numberOfFiles,
-                mediaFolder.type);
+                mediaFolder.type,
+                syncedFolder.isHidden());
     }
 
     /**
@@ -443,7 +477,8 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 mediaFolder.filePaths,
                 mediaFolder.folderName,
                 mediaFolder.numberOfFiles,
-                mediaFolder.type);
+                mediaFolder.type,
+                false);
     }
 
     private File[] getFileList(File localFolder) {
@@ -489,15 +524,35 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
     }
 
     /**
-     * show/hide recycler view list or the empty message / progress info.
-     *
-     * @param shown flag if list should be shown
+     * show recycler view list or the empty message info (in case list is empty).
      */
-    private void setListShown(boolean shown) {
+    private void showList() {
         if (mRecyclerView != null) {
-            mRecyclerView.setVisibility(shown ? View.VISIBLE : View.GONE);
-            mProgress.setVisibility(shown ? View.GONE : View.VISIBLE);
-            mEmpty.setVisibility(shown && mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
+            mRecyclerView.setVisibility(View.VISIBLE);
+            emptyContentProgressBar.setVisibility(View.GONE);
+
+            checkAndShowEmptyListContent();
+        }
+    }
+
+    private void checkAndShowEmptyListContent() {
+        if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() > adapter.getSectionCount()) {
+            emptyContentContainer.setVisibility(View.VISIBLE);
+            int hiddenFoldersCount = adapter.getHiddenFolderCount();
+
+            showEmptyContent(getString(R.string.drawer_synced_folders),
+                             getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders,
+                                                              hiddenFoldersCount,
+                                                              hiddenFoldersCount),
+                             getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders,
+                                                              hiddenFoldersCount,
+                                                              hiddenFoldersCount));
+        } else if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() == 0) {
+            emptyContentContainer.setVisibility(View.VISIBLE);
+            showEmptyContent(getString(R.string.drawer_synced_folders),
+                             getString(R.string.synced_folders_no_results));
+        } else {
+            emptyContentContainer.setVisibility(View.GONE);
         }
     }
 
@@ -523,8 +578,8 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
                 Log.d(TAG, "Show custom folder dialog");
                 SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem(
                     SyncedFolder.UNPERSISTED_ID, null, null, true, false,
-                    false, getAccount().name,
-                    FileUploader.LOCAL_BEHAVIOUR_FORGET, false, clock.getCurrentTime(), null, MediaFolderType.CUSTOM);
+                    false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false,
+                    clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false);
                 onSyncFolderSettingsClick(0, emptyCustomFolder);
             }
 
@@ -554,10 +609,10 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
     @Override
     public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) {
         if (syncedFolderDisplayItem.getId() > UNPERSISTED_ID) {
-            mSyncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(),
-                    syncedFolderDisplayItem.isEnabled());
+            syncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(),
+                                                           syncedFolderDisplayItem.isEnabled());
         } else {
-            long storedId = mSyncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem);
+            long storedId = syncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem);
             if (storedId != -1) {
                 syncedFolderDisplayItem.setId(storedId);
             }
@@ -576,22 +631,72 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
         FragmentTransaction ft = fm.beginTransaction();
         ft.addToBackStack(null);
 
-        mSyncedFolderPreferencesDialogFragment = SyncedFolderPreferencesDialogFragment.newInstance(
+        syncedFolderPreferencesDialogFragment = SyncedFolderPreferencesDialogFragment.newInstance(
                 syncedFolderDisplayItem, section);
-        mSyncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG);
+        syncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG);
+    }
+
+    @Override
+    public void onVisibilityToggleClick(int section, SyncedFolderDisplayItem syncedFolder) {
+        syncedFolder.setHidden(!syncedFolder.isHidden());
+
+        saveOrUpdateSyncedFolder(syncedFolder);
+        adapter.setSyncFolderItem(section, syncedFolder);
+
+        checkAndShowEmptyListContent();
+    }
+
+    private void showEmptyContent(String headline, String message) {
+        showEmptyContent(headline, message, false);
+        emptyContentActionButton.setVisibility(View.GONE);
+    }
+
+    private void showEmptyContent(String headline, String message, String action) {
+        showEmptyContent(headline, message, false);
+        emptyContentActionButton.setText(action);
+        emptyContentActionButton.setVisibility(View.VISIBLE);
+        emptyContentMessage.setVisibility(View.GONE);
+    }
+
+    private void showLoadingContent() {
+        showEmptyContent(
+            getString(R.string.drawer_synced_folders),
+            getString(R.string.synced_folders_loading_folders),
+            true
+        );
+        emptyContentActionButton.setVisibility(View.GONE);
+    }
+
+    private void showEmptyContent(String headline, String message, boolean loading) {
+        if (emptyContentContainer != null) {
+            emptyContentContainer.setVisibility(View.VISIBLE);
+            mRecyclerView.setVisibility(View.GONE);
+
+            emptyContentHeadline.setText(headline);
+            emptyContentMessage.setText(message);
+            emptyContentMessage.setVisibility(View.VISIBLE);
+
+            if (loading) {
+                emptyContentProgressBar.setVisibility(View.VISIBLE);
+                emptyContentIcon.setVisibility(View.GONE);
+            } else {
+                emptyContentProgressBar.setVisibility(View.GONE);
+                emptyContentIcon.setVisibility(View.VISIBLE);
+            }
+        }
     }
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_REMOTE_FOLDER
-                && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) {
+                && resultCode == RESULT_OK && syncedFolderPreferencesDialogFragment != null) {
             OCFile chosenFolder = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
-            mSyncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath());
+            syncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath());
         }
         if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_LOCAL_FOLDER
-                && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) {
+                && resultCode == RESULT_OK && syncedFolderPreferencesDialogFragment != null) {
             String localPath = data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
-            mSyncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath);
+            syncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath);
         } else {
             super.onActivityResult(requestCode, resultCode, data);
         }
@@ -604,59 +709,75 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
         if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) {
             SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem(
                     SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(),
-                    syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(),
-                    syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(),
-                    clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType());
-            long storedId = mSyncedFolderProvider.storeSyncedFolder(newCustomFolder);
-            if (storedId != -1) {
-                newCustomFolder.setId(storedId);
-                if (newCustomFolder.isEnabled()) {
-                    FilesSyncHelper.insertAllDBEntriesForSyncedFolder(newCustomFolder);
-                }
-            }
-            mAdapter.addSyncFolderItem(newCustomFolder);
+                    syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(),
+                    syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(),
+                    clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden());
+
+            saveOrUpdateSyncedFolder(newCustomFolder);
+            adapter.addSyncFolderItem(newCustomFolder);
         } else {
-            SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection());
-            item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder
-                    .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder
-                    .getUploadAction(), syncedFolder.getEnabled());
-
-            if (syncedFolder.getId() == UNPERSISTED_ID) {
-                // newly set up folder sync config
-                long storedId = mSyncedFolderProvider.storeSyncedFolder(item);
-                if (storedId != -1) {
-                    item.setId(storedId);
-                    if (item.isEnabled()) {
-                        FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
-                    }
-                }
-            } else {
-                // existing synced folder setup to be updated
-                mSyncedFolderProvider.updateSyncFolder(item);
-                if (item.isEnabled()) {
-                    FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
-                }
-            }
+            SyncedFolderDisplayItem item = adapter.get(syncedFolder.getSection());
+            updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(),
+                                   syncedFolder.getRemotePath(), syncedFolder
+                    .isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), syncedFolder
+                    .getUploadAction(), syncedFolder.isEnabled());
 
-            mAdapter.setSyncFolderItem(syncedFolder.getSection(), item);
+            saveOrUpdateSyncedFolder(item);
+
+            // TODO test if notifiyItemChanged is suffiecient (should improve performance)
+            adapter.notifyDataSetChanged();
         }
 
-        mSyncedFolderPreferencesDialogFragment = null;
+        syncedFolderPreferencesDialogFragment = null;
 
-        if (syncedFolder.getEnabled()) {
+        if (syncedFolder.isEnabled()) {
             showBatteryOptimizationInfo();
         }
     }
 
+    private void saveOrUpdateSyncedFolder(SyncedFolderDisplayItem item) {
+        if (item.getId() == UNPERSISTED_ID) {
+            // newly set up folder sync config
+            storeSyncedFolder(item);
+        } else {
+            // existing synced folder setup to be updated
+            syncedFolderProvider.updateSyncFolder(item);
+            if (item.isEnabled()) {
+                FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
+            } else {
+                String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
+
+                ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().
+                    getContentResolver());
+                arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
+            }
+        }
+    }
+
+    private void storeSyncedFolder(SyncedFolderDisplayItem item) {
+        ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().
+            getContentResolver());
+        long storedId = syncedFolderProvider.storeSyncedFolder(item);
+        if (storedId != -1) {
+            item.setId(storedId);
+            if (item.isEnabled()) {
+                FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item);
+            } else {
+                String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId();
+                arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey);
+            }
+        }
+    }
+
     @Override
     public void onCancelSyncedFolderPreference() {
-        mSyncedFolderPreferencesDialogFragment = null;
+        syncedFolderPreferencesDialogFragment = null;
     }
 
     @Override
     public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) {
-        mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId());
-        mAdapter.removeItem(syncedFolder.getSection());
+        syncedFolderProvider.deleteSyncedFolder(syncedFolder.getId());
+        adapter.removeItem(syncedFolder.getSection());
     }
 
     /**
@@ -670,16 +791,17 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
      * @param subfolderByDate created sub folders
      * @param uploadAction    upload action
      * @param enabled         is sync enabled
-     * @return the updated item
      */
-    private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem item,
+    private void updateSyncedFolderItem(SyncedFolderDisplayItem item,
+                                                           long id,
                                                            String localPath,
                                                            String remotePath,
-                                                           Boolean wifiOnly,
-                                                           Boolean chargingOnly,
-                                                           Boolean subfolderByDate,
+                                                           boolean wifiOnly,
+                                                           boolean chargingOnly,
+                                                           boolean subfolderByDate,
                                                            Integer uploadAction,
-                                                           Boolean enabled) {
+                                                           boolean enabled) {
+        item.setId(id);
         item.setLocalPath(localPath);
         item.setRemotePath(remotePath);
         item.setWifiOnly(wifiOnly);
@@ -687,7 +809,6 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
         item.setSubfolderByDate(subfolderByDate);
         item.setUploadAction(uploadAction);
         item.setEnabled(enabled, clock.getCurrentTime());
-        return item;
     }
 
     @Override
@@ -714,7 +835,6 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
     @Override
     protected void onResume() {
         super.onResume();
-
         setDrawerMenuItemChecked(R.id.nav_synced_folders);
     }
 

+ 252 - 72
src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java

@@ -23,11 +23,14 @@ package com.owncloud.android.ui.adapter;
 
 import android.content.Context;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
@@ -54,64 +57,174 @@ import butterknife.ButterKnife;
  */
 public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> {
 
-    private final Context mContext;
+    private final Context context;
     private final Clock clock;
-    private final int mGridWidth;
-    private final int mGridTotal;
-    private final ClickListener mListener;
-    private final List<SyncedFolderDisplayItem> mSyncFolderItems;
-    private final boolean mLight;
+    private final int gridWidth;
+    private final int gridTotal;
+    private final ClickListener clickListener;
+    private final List<SyncedFolderDisplayItem> syncFolderItems;
+    private final List<SyncedFolderDisplayItem> filteredSyncFolderItems;
+    private final boolean light;
+    private final int VIEW_TYPE_EMPTY = Integer.MAX_VALUE;
+    private boolean hideItems;
 
     public SyncedFolderAdapter(Context context, Clock clock, int gridWidth, ClickListener listener, boolean light) {
-        mContext = context;
+        this.context = context;
         this.clock = clock;
-        mGridWidth = gridWidth;
-        mGridTotal = gridWidth * 2;
-        mListener = listener;
-        mSyncFolderItems = new ArrayList<>();
-        mLight = light;
+        this.gridWidth = gridWidth;
+        gridTotal = gridWidth * 2;
+        clickListener = listener;
+        syncFolderItems = new ArrayList<>();
+        filteredSyncFolderItems = new ArrayList<>();
+        this.light = light;
+        this.hideItems = true;
 
         shouldShowHeadersForEmptySections(true);
+        shouldShowFooters(true);
+    }
+
+    public void toggleHiddenItemsVisibility() {
+        hideItems = !hideItems;
+        filteredSyncFolderItems.clear();
+        filteredSyncFolderItems.addAll(filterHiddenItems(syncFolderItems, hideItems));
+        notifyDataSetChanged();
     }
 
     public void setSyncFolderItems(List<SyncedFolderDisplayItem> syncFolderItems) {
-        mSyncFolderItems.clear();
-        mSyncFolderItems.addAll(syncFolderItems);
+        this.syncFolderItems.clear();
+        this.syncFolderItems.addAll(syncFolderItems);
+
+        this.filteredSyncFolderItems.clear();
+        this.filteredSyncFolderItems.addAll(filterHiddenItems(this.syncFolderItems, hideItems));
     }
 
     public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderItem) {
-        mSyncFolderItems.set(location, syncFolderItem);
+        if (hideItems && syncFolderItem.isHidden() && filteredSyncFolderItems.contains(syncFolderItem)) {
+            filteredSyncFolderItems.remove(location);
+        } else {
+            if (filteredSyncFolderItems.contains(syncFolderItem)) {
+                filteredSyncFolderItems.set(filteredSyncFolderItems.indexOf(syncFolderItem), syncFolderItem);
+            } else {
+                filteredSyncFolderItems.add(syncFolderItem);
+            }
+        }
+
+        if (syncFolderItems.contains(syncFolderItem)) {
+            syncFolderItems.set(syncFolderItems.indexOf(syncFolderItem), syncFolderItem);
+        } else {
+            syncFolderItems.add(syncFolderItem);
+        }
+
         notifyDataSetChanged();
     }
 
     public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) {
-        mSyncFolderItems.add(syncFolderItem);
-        notifyDataSetChanged();
+        syncFolderItems.add(syncFolderItem);
+
+        // add item for display when either all items should be shown (!hideItems)
+        // or if item should be shown (!.isHidden())
+        if (!hideItems || !syncFolderItem.isHidden()) {
+            filteredSyncFolderItems.add(syncFolderItem);
+            notifyDataSetChanged();
+        }
     }
 
     public void removeItem(int section) {
-        mSyncFolderItems.remove(section);
-        notifyDataSetChanged();
+        if (filteredSyncFolderItems.contains(syncFolderItems.get(section))) {
+            filteredSyncFolderItems.remove(syncFolderItems.get(section));
+            notifyDataSetChanged();
+        }
+        syncFolderItems.remove(section);
+    }
+
+    /**
+     * Filter for hidden items
+     *
+     * @param items Collection of items to filter
+     * @return Non-hidden items
+     */
+    private List<SyncedFolderDisplayItem> filterHiddenItems(List<SyncedFolderDisplayItem> items, boolean hide) {
+        if (!hide) {
+            return items;
+        } else {
+            List<SyncedFolderDisplayItem> result = new ArrayList<>();
+
+            for (SyncedFolderDisplayItem item : items) {
+                if (!item.isHidden() && !result.contains(item)) {
+                    result.add(item);
+                }
+            }
+
+            return result;
+        }
     }
 
     @Override
     public int getSectionCount() {
-        return mSyncFolderItems.size();
+        if (filteredSyncFolderItems.size() > 0) {
+            return filteredSyncFolderItems.size() + 1;
+        } else {
+            return 0;
+        }
+    }
+
+    public int getUnfilteredSectionCount() {
+        if (syncFolderItems.size() > 0) {
+            return syncFolderItems.size() + 1;
+        } else {
+            return 0;
+        }
     }
 
     @Override
     public int getItemCount(int section) {
-        List<String> filePaths = mSyncFolderItems.get(section).getFilePaths();
+        if (section < filteredSyncFolderItems.size()) {
+            List<String> filePaths = filteredSyncFolderItems.get(section).getFilePaths();
 
-        if (filePaths != null) {
-            return mSyncFolderItems.get(section).getFilePaths().size();
+            if (filePaths != null) {
+                return filteredSyncFolderItems.get(section).getFilePaths().size();
+            } else {
+                return 1;
+            }
         } else {
             return 1;
         }
     }
 
     public SyncedFolderDisplayItem get(int section) {
-        return mSyncFolderItems.get(section);
+        return filteredSyncFolderItems.get(section);
+    }
+
+    @Override
+    public int getItemViewType(int section, int relativePosition, int absolutePosition) {
+        if (isLastSection(section)) {
+            return VIEW_TYPE_EMPTY;
+        } else {
+            return VIEW_TYPE_ITEM;
+        }
+    }
+
+    @Override
+    public int getHeaderViewType(int section) {
+        if (isLastSection(section)) {
+            return VIEW_TYPE_EMPTY;
+        } else {
+            return VIEW_TYPE_HEADER;
+        }
+    }
+
+    @Override
+    public int getFooterViewType(int section) {
+        if (isLastSection(section) && showFooter()) {
+            return VIEW_TYPE_FOOTER;
+        } else {
+            // only show footer after last item and only if folders have been hidden
+            return VIEW_TYPE_EMPTY;
+        }
+    }
+
+    private boolean showFooter() {
+        return syncFolderItems.size() > filteredSyncFolderItems.size();
     }
 
     /**
@@ -122,9 +235,9 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
      * @return the section index of the looked up synced folder, <code>-1</code> if not present
      */
     public int getSectionByLocalPathAndType(String localPath, int type) {
-        for (int i = 0; i < mSyncFolderItems.size(); i++) {
-            if (mSyncFolderItems.get(i).getLocalPath().equalsIgnoreCase(localPath) &&
-                    mSyncFolderItems.get(i).getType().getId().equals(type)) {
+        for (int i = 0; i < filteredSyncFolderItems.size(); i++) {
+            if (filteredSyncFolderItems.get(i).getLocalPath().equalsIgnoreCase(localPath) &&
+                filteredSyncFolderItems.get(i).getType().getId().equals(type)) {
                 return i;
             }
         }
@@ -134,70 +247,96 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
 
     @Override
     public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int section, boolean expanded) {
-        HeaderViewHolder holder = (HeaderViewHolder) commonHolder;
+        if (section < filteredSyncFolderItems.size()) {
+            HeaderViewHolder holder = (HeaderViewHolder) commonHolder;
+            holder.mainHeaderContainer.setVisibility(View.VISIBLE);
 
-        holder.mainHeaderContainer.setVisibility(View.VISIBLE);
+            holder.title.setText(filteredSyncFolderItems.get(section).getFolderName());
 
-        holder.title.setText(mSyncFolderItems.get(section).getFolderName());
+            if (MediaFolderType.VIDEO == filteredSyncFolderItems.get(section).getType()) {
+                holder.type.setImageResource(R.drawable.video_32dp);
+            } else if (MediaFolderType.IMAGE == filteredSyncFolderItems.get(section).getType()) {
+                holder.type.setImageResource(R.drawable.image_32dp);
+            } else {
+                holder.type.setImageResource(R.drawable.folder_star_32dp);
+            }
 
-        if (MediaFolderType.VIDEO == mSyncFolderItems.get(section).getType()) {
-            holder.type.setImageResource(R.drawable.video_32dp);
-        } else if (MediaFolderType.IMAGE == mSyncFolderItems.get(section).getType()) {
-            holder.type.setImageResource(R.drawable.image_32dp);
-        } else {
-            holder.type.setImageResource(R.drawable.folder_star_32dp);
+            holder.syncStatusButton.setVisibility(View.VISIBLE);
+            holder.syncStatusButton.setTag(section);
+            holder.syncStatusButton.setOnClickListener(v -> {
+                filteredSyncFolderItems.get(section).setEnabled(
+                    !filteredSyncFolderItems.get(section).isEnabled(),
+                    clock.getCurrentTime()
+                );
+                setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
+                clickListener.onSyncStatusToggleClick(section, filteredSyncFolderItems.get(section));
+            });
+            setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
+
+            if (light) {
+                holder.menuButton.setVisibility(View.GONE);
+            } else {
+                holder.menuButton.setVisibility(View.VISIBLE);
+                holder.menuButton.setTag(section);
+                holder.menuButton.setOnClickListener(v -> onOverflowIconClicked(section,
+                                                                                filteredSyncFolderItems.get(section),
+                                                                                v));
+            }
         }
+    }
+
+    private void onOverflowIconClicked(int section, SyncedFolderDisplayItem item, View view) {
+        PopupMenu popup = new PopupMenu(context, view);
+        popup.inflate(R.menu.synced_folders_adapter);
+        popup.setOnMenuItemClickListener(i -> optionsItemSelected(i, section, item));
+        popup.getMenu()
+            .findItem(R.id.action_auto_upload_folder_toggle_visibility)
+            .setChecked(item.isHidden());
 
-        holder.syncStatusButton.setVisibility(View.VISIBLE);
-        holder.syncStatusButton.setTag(section);
-        holder.syncStatusButton.setOnClickListener(v -> {
-            mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime());
-            setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
-            mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section));
-        });
-        setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
-
-        holder.syncStatusButton.setVisibility(View.VISIBLE);
-        holder.syncStatusButton.setTag(section);
-        holder.syncStatusButton.setOnClickListener(v -> {
-            mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime());
-            setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
-            mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section));
-        });
-        setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled());
-
-        if (mLight) {
-            holder.menuButton.setVisibility(View.GONE);
+        popup.show();
+    }
+
+    private boolean optionsItemSelected(MenuItem menuItem, int section, SyncedFolderDisplayItem item) {
+        if (menuItem.getItemId() == R.id.action_auto_upload_folder_toggle_visibility) {
+            clickListener.onVisibilityToggleClick(section, item);
         } else {
-            holder.menuButton.setVisibility(View.VISIBLE);
-            holder.menuButton.setTag(section);
-            holder.menuButton.setOnClickListener(v -> mListener.onSyncFolderSettingsClick(section,
-                    mSyncFolderItems.get(section)));
+            // default: R.id.action_create_custom_folder
+            clickListener.onSyncFolderSettingsClick(section, item);
         }
+        return true;
     }
 
     @Override
     public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
-        // not needed
+        if (isLastSection(section) && showFooter()) {
+            FooterViewHolder footerHolder = (FooterViewHolder) holder;
+            footerHolder.title.setOnClickListener(v -> toggleHiddenItemsVisibility());
+            footerHolder.title.setText(
+                context.getResources().getQuantityString(
+                    R.plurals.synced_folders_show_hidden_folders,
+                    getHiddenFolderCount(),
+                    getHiddenFolderCount()
+                )
+            );
+        }
     }
 
-
     @Override
     public void onBindViewHolder(SectionedViewHolder commonHolder, int section, int relativePosition,
                                  int absolutePosition) {
-        if (mSyncFolderItems.get(section).getFilePaths() != null) {
+        if (section < filteredSyncFolderItems.size() && filteredSyncFolderItems.get(section).getFilePaths() != null) {
             MainViewHolder holder = (MainViewHolder) commonHolder;
 
-            File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition));
+            File file = new File(filteredSyncFolderItems.get(section).getFilePaths().get(relativePosition));
 
             ThumbnailsCacheManager.MediaThumbnailGenerationTask task =
-                    new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, mContext);
+                    new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, context);
 
             ThumbnailsCacheManager.AsyncMediaThumbnailDrawable asyncDrawable =
                     new ThumbnailsCacheManager.AsyncMediaThumbnailDrawable(
-                            mContext.getResources(),
-                            ThumbnailsCacheManager.mDefaultImg,
-                            task
+                        context.getResources(),
+                        ThumbnailsCacheManager.mDefaultImg,
+                        task
                     );
             holder.image.setImageDrawable(asyncDrawable);
 
@@ -206,11 +345,11 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
             // set proper tag
             holder.image.setTag(file.hashCode());
 
-            holder.itemView.setTag(relativePosition % mGridWidth);
+            holder.itemView.setTag(relativePosition % gridWidth);
 
-            if (mSyncFolderItems.get(section).getNumberOfFiles() > mGridTotal && relativePosition >= mGridTotal - 1) {
+            if (filteredSyncFolderItems.get(section).getNumberOfFiles() > gridTotal && relativePosition >= gridTotal - 1) {
                 holder.counterValue.setText(String.format(Locale.US, "%d",
-                    mSyncFolderItems.get(section).getNumberOfFiles() - mGridTotal));
+                                                          filteredSyncFolderItems.get(section).getNumberOfFiles() - gridTotal));
                 holder.counterBar.setVisibility(View.VISIBLE);
                 holder.thumbnailDarkener.setVisibility(View.VISIBLE);
             } else {
@@ -226,15 +365,34 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
         if (viewType == VIEW_TYPE_HEADER) {
             View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_item_header, parent, false);
             return new HeaderViewHolder(v);
+        } else if (viewType == VIEW_TYPE_FOOTER) {
+            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_footer, parent, false);
+            return new FooterViewHolder(v);
+        } else if (viewType == VIEW_TYPE_EMPTY) {
+            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_empty, parent, false);
+            return new EmptyViewHolder(v);
         } else {
             View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_sync_item, parent, false);
             return new MainViewHolder(v);
         }
     }
 
+    private boolean isLastSection(int section) {
+        return section >= getSectionCount() - 1;
+    }
+
+    public int getHiddenFolderCount() {
+        if (syncFolderItems != null && filteredSyncFolderItems != null) {
+            return syncFolderItems.size() - filteredSyncFolderItems.size();
+        } else {
+            return 0;
+        }
+    }
+
     public interface ClickListener {
         void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem);
         void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem);
+        void onVisibilityToggleClick(int section, SyncedFolderDisplayItem item);
     }
 
     static class HeaderViewHolder extends SectionedViewHolder {
@@ -259,7 +417,29 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
         }
     }
 
+    static class FooterViewHolder extends SectionedViewHolder {
+        @BindView(R.id.footer_container)
+        public LinearLayout mainFooterContainer;
+
+        @BindView(R.id.footer_text)
+        public TextView title;
+
+        private FooterViewHolder(View itemView) {
+            super(itemView);
+            ButterKnife.bind(this, itemView);
+        }
+    }
+
+    static class EmptyViewHolder extends SectionedViewHolder {
+        private EmptyViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
     static class MainViewHolder extends SectionedViewHolder {
+        @BindView(R.id.grid_item_container)
+        public FrameLayout item_container;
+
         @BindView(R.id.thumbnail)
         public ImageView image;
 
@@ -281,7 +461,7 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
     private void setSyncButtonActiveIcon(ImageButton syncStatusButton, boolean enabled) {
         if (enabled) {
             syncStatusButton.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_cloud_sync_on,
-                    ThemeUtils.primaryColor(mContext)));
+                    ThemeUtils.primaryColor(context)));
         } else {
             syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off);
         }

+ 8 - 8
src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java

@@ -202,7 +202,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
         ThemeUtils.themeDialogActionButton(mSave);
 
         // Set values
-        setEnabled(mSyncedFolder.getEnabled());
+        setEnabled(mSyncedFolder.isEnabled());
 
         if (!TextUtils.isEmpty(mSyncedFolder.getLocalPath())) {
             mLocalFolderPath.setText(
@@ -223,11 +223,11 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
             mRemoteFolderSummary.setText(R.string.choose_remote_folder);
         }
 
-        mUploadOnWifiCheckbox.setChecked(mSyncedFolder.getWifiOnly());
+        mUploadOnWifiCheckbox.setChecked(mSyncedFolder.isWifiOnly());
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            mUploadOnChargingCheckbox.setChecked(mSyncedFolder.getChargingOnly());
+            mUploadOnChargingCheckbox.setChecked(mSyncedFolder.isChargingOnly());
         }
-        mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.getSubfolderByDate());
+        mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate());
 
         mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]);
     }
@@ -344,7 +344,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
                 new OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        mSyncedFolder.setWifiOnly(!mSyncedFolder.getWifiOnly());
+                        mSyncedFolder.setWifiOnly(!mSyncedFolder.isWifiOnly());
                         mUploadOnWifiCheckbox.toggle();
                     }
                 });
@@ -355,7 +355,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
                     new OnClickListener() {
                         @Override
                         public void onClick(View v) {
-                            mSyncedFolder.setChargingOnly(!mSyncedFolder.getChargingOnly());
+                            mSyncedFolder.setChargingOnly(!mSyncedFolder.isChargingOnly());
                             mUploadOnChargingCheckbox.toggle();
                         }
                     });
@@ -365,7 +365,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
                 new OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        mSyncedFolder.setSubfolderByDate(!mSyncedFolder.getSubfolderByDate());
+                        mSyncedFolder.setSubfolderByDate(!mSyncedFolder.isSubfolderByDate());
                         mUploadUseSubfoldersCheckbox.toggle();
                     }
                 });
@@ -401,7 +401,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
         view.findViewById(R.id.sync_enabled).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                setEnabled(!mSyncedFolder.getEnabled());
+                setEnabled(!mSyncedFolder.isEnabled());
             }
         });
 

+ 60 - 146
src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java

@@ -27,70 +27,76 @@ import com.owncloud.android.datamodel.MediaFolderType;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
 import com.owncloud.android.files.services.FileUploader;
 
+import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.Setter;
 
 /**
  * Parcelable for {@link SyncedFolderDisplayItem} objects to transport them from/to dialog fragments.
  */
 @NoArgsConstructor
 public class SyncedFolderParcelable implements Parcelable {
-    private String mFolderName;
-    private String mLocalPath;
-    private String mRemotePath;
-    private Boolean mWifiOnly = false;
-    private Boolean mChargingOnly = false;
-    private Boolean mEnabled = false;
-    private Boolean mSubfolderByDate = false;
-    private Integer mUploadAction;
-    private MediaFolderType mType;
-    private long mId;
-    private String mAccount;
-    private int mSection;
+    @Getter @Setter private String folderName;
+    @Getter @Setter private String localPath;
+    @Getter @Setter private String remotePath;
+    @Getter @Setter private boolean wifiOnly = false;
+    @Getter @Setter private boolean chargingOnly = false;
+    @Getter @Setter private boolean enabled = false;
+    @Getter @Setter private boolean subfolderByDate = false;
+    @Getter private Integer uploadAction;
+    @Getter @Setter private MediaFolderType type;
+    @Getter @Setter private boolean hidden = false;
+    @Getter @Setter private long id;
+    @Getter @Setter private String account;
+    @Getter @Setter private int section;
 
     public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) {
-        mId = syncedFolderDisplayItem.getId();
-        mFolderName = syncedFolderDisplayItem.getFolderName();
-        mLocalPath = syncedFolderDisplayItem.getLocalPath();
-        mRemotePath = syncedFolderDisplayItem.getRemotePath();
-        mWifiOnly = syncedFolderDisplayItem.getWifiOnly();
-        mChargingOnly = syncedFolderDisplayItem.getChargingOnly();
-        mEnabled = syncedFolderDisplayItem.isEnabled();
-        mSubfolderByDate = syncedFolderDisplayItem.getSubfolderByDate();
-        mType = syncedFolderDisplayItem.getType();
-        mAccount = syncedFolderDisplayItem.getAccount();
-        mUploadAction = syncedFolderDisplayItem.getUploadAction();
-        mSection = section;
+        id = syncedFolderDisplayItem.getId();
+        folderName = syncedFolderDisplayItem.getFolderName();
+        localPath = syncedFolderDisplayItem.getLocalPath();
+        remotePath = syncedFolderDisplayItem.getRemotePath();
+        wifiOnly = syncedFolderDisplayItem.isWifiOnly();
+        chargingOnly = syncedFolderDisplayItem.isChargingOnly();
+        enabled = syncedFolderDisplayItem.isEnabled();
+        subfolderByDate = syncedFolderDisplayItem.isSubfolderByDate();
+        type = syncedFolderDisplayItem.getType();
+        account = syncedFolderDisplayItem.getAccount();
+        uploadAction = syncedFolderDisplayItem.getUploadAction();
+        this.section = section;
+        hidden = syncedFolderDisplayItem.isHidden();
     }
 
     private SyncedFolderParcelable(Parcel read) {
-        mId = read.readLong();
-        mFolderName = read.readString();
-        mLocalPath = read.readString();
-        mRemotePath = read.readString();
-        mWifiOnly = read.readInt()!= 0;
-        mChargingOnly = read.readInt() != 0;
-        mEnabled = read.readInt() != 0;
-        mSubfolderByDate = read.readInt() != 0;
-        mType = MediaFolderType.getById(read.readInt());
-        mAccount = read.readString();
-        mUploadAction = read.readInt();
-        mSection = read.readInt();
+        id = read.readLong();
+        folderName = read.readString();
+        localPath = read.readString();
+        remotePath = read.readString();
+        wifiOnly = read.readInt()!= 0;
+        chargingOnly = read.readInt() != 0;
+        enabled = read.readInt() != 0;
+        subfolderByDate = read.readInt() != 0;
+        type = MediaFolderType.getById(read.readInt());
+        account = read.readString();
+        uploadAction = read.readInt();
+        section = read.readInt();
+        hidden = read.readInt() != 0;
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(mId);
-        dest.writeString(mFolderName);
-        dest.writeString(mLocalPath);
-        dest.writeString(mRemotePath);
-        dest.writeInt(mWifiOnly ? 1 : 0);
-        dest.writeInt(mChargingOnly ? 1 : 0);
-        dest.writeInt(mEnabled ? 1 : 0);
-        dest.writeInt(mSubfolderByDate ? 1 : 0);
-        dest.writeInt(mType.getId());
-        dest.writeString(mAccount);
-        dest.writeInt(mUploadAction);
-        dest.writeInt(mSection);
+        dest.writeLong(id);
+        dest.writeString(folderName);
+        dest.writeString(localPath);
+        dest.writeString(remotePath);
+        dest.writeInt(wifiOnly ? 1 : 0);
+        dest.writeInt(chargingOnly ? 1 : 0);
+        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(subfolderByDate ? 1 : 0);
+        dest.writeInt(type.getId());
+        dest.writeString(account);
+        dest.writeInt(uploadAction);
+        dest.writeInt(section);
+        dest.writeInt(hidden ? 1 : 0);
     }
 
     public static final Creator<SyncedFolderParcelable> CREATOR =
@@ -112,76 +118,8 @@ public class SyncedFolderParcelable implements Parcelable {
         return 0;
     }
 
-    public String getFolderName() {
-        return mFolderName;
-    }
-
-    public void setFolderName(String mFolderName) {
-        this.mFolderName = mFolderName;
-    }
-
-    public String getLocalPath() {
-        return mLocalPath;
-    }
-
-    public void setLocalPath(String mLocalPath) {
-        this.mLocalPath = mLocalPath;
-    }
-
-    public String getRemotePath() {
-        return mRemotePath;
-    }
-
-    public void setRemotePath(String mRemotePath) {
-        this.mRemotePath = mRemotePath;
-    }
-
-    public Boolean getWifiOnly() {
-        return mWifiOnly;
-    }
-
-    public void setWifiOnly(Boolean mWifiOnly) {
-        this.mWifiOnly = mWifiOnly;
-    }
-
-    public Boolean getChargingOnly() {
-        return mChargingOnly;
-    }
-
-    public void setChargingOnly(Boolean mChargingOnly) {
-        this.mChargingOnly = mChargingOnly;
-    }
-
-    public Boolean getEnabled() {
-        return mEnabled;
-    }
-
-    public void setEnabled(boolean mEnabled) {
-        this.mEnabled = mEnabled;
-    }
-
-    public Boolean getSubfolderByDate() {
-        return mSubfolderByDate;
-    }
-
-    public void setSubfolderByDate(Boolean mSubfolderByDate) {
-        this.mSubfolderByDate = mSubfolderByDate;
-    }
-
-    public MediaFolderType getType() {
-        return mType;
-    }
-
-    public void setType(MediaFolderType mType) {
-        this.mType = mType;
-    }
-
-    public Integer getUploadAction() {
-        return mUploadAction;
-    }
-
     public Integer getUploadActionInteger() {
-        switch (mUploadAction) {
+        switch (uploadAction) {
             case FileUploader.LOCAL_BEHAVIOUR_FORGET:
                 return 0;
             case FileUploader.LOCAL_BEHAVIOUR_MOVE:
@@ -192,41 +130,17 @@ public class SyncedFolderParcelable implements Parcelable {
         return 0;
     }
 
-    public void setUploadAction(String mUploadAction) {
-        switch (mUploadAction) {
+    public void setUploadAction(String uploadAction) {
+        switch (uploadAction) {
             case "LOCAL_BEHAVIOUR_FORGET":
-                this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_FORGET;
+                this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_FORGET;
                 break;
             case "LOCAL_BEHAVIOUR_MOVE":
-                this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_MOVE;
+                this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_MOVE;
                 break;
             case "LOCAL_BEHAVIOUR_DELETE":
-                this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_DELETE;
+                this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_DELETE;
                 break;
         }
     }
-
-    public long getId() {
-        return mId;
-    }
-
-    public void setId(long mId) {
-        this.mId = mId;
-    }
-
-    public String getAccount() {
-        return mAccount;
-    }
-
-    public void setAccount(String mAccount) {
-        this.mAccount = mAccount;
-    }
-
-    public int getSection() {
-        return mSection;
-    }
-
-    public void setSection(int mSection) {
-        this.mSection = mSection;
-    }
 }

+ 14 - 2
src/main/res/layout/empty_list.xml

@@ -19,6 +19,7 @@
   License along with this program. If not, see <http://www.gnu.org/licenses/>.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:id="@+id/empty_list_view"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
@@ -50,7 +51,7 @@
         android:ellipsize="end"
         android:gravity="center"
         android:maxLines="2"
-        android:paddingBottom="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
         android:paddingTop="@dimen/standard_padding"
         android:text="@string/file_list_loading"
         android:textSize="26sp"/>
@@ -62,7 +63,18 @@
         android:layout_gravity="center_horizontal"
         android:ellipsize="end"
         android:gravity="center"
+        android:paddingTop="@dimen/standard_half_padding"
+        android:paddingBottom="@dimen/standard_half_padding"
         android:text="@string/file_list_empty"
         android:visibility="gone"/>
 
-</LinearLayout>
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/empty_list_view_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:theme="@style/Button.Primary"
+        app:cornerRadius="@dimen/button_corner_radius"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:visibility="gone"/>
+
+</LinearLayout>

+ 6 - 0
src/main/res/layout/synced_folders_empty.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/zero"
+    android:layout_height="@dimen/zero">
+
+</LinearLayout>

+ 39 - 0
src/main/res/layout/synced_folders_footer.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2019 Andy Scherzinger
+
+  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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/footer_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:showDividers="none">
+
+    <TextView
+        android:id="@+id/footer_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/standard_double_margin"
+        android:layout_marginBottom="@dimen/min_list_item_size"
+        android:gravity="center"
+        android:padding="@dimen/standard_padding"
+        tools:text="Show 3 hidden folders"
+        android:textColor="@color/secondary_text_color" />
+
+</LinearLayout>

+ 1 - 33
src/main/res/layout/synced_folders_layout.xml

@@ -50,39 +50,7 @@
             android:orientation="vertical"
             app:layout_behavior="@string/appbar_scrolling_view_behavior">
 
-            <LinearLayout
-                android:id="@android:id/progress"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:orientation="vertical"
-                android:visibility="gone">
-
-                <ProgressBar
-                    android:id="@+id/syncedFoldersProgressBar"
-                    android:layout_width="@dimen/synced_folders_progress_bar_layout_width"
-                    android:layout_height="@dimen/synced_folders_progress_bar_layout_height"
-                    android:layout_gravity="center_horizontal"/>
-
-                <TextView
-                    android:id="@+id/progressText"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_horizontal"
-                    android:layout_margin="@dimen/standard_half_margin"
-                    android:text="@string/synced_folders_loading_folders"
-                    android:textSize="26sp"/>
-            </LinearLayout>
-
-            <TextView
-                android:id="@android:id/empty"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:layout_margin="@dimen/standard_margin"
-                android:gravity="center"
-                android:text="@string/synced_folders_no_results"
-                android:visibility="gone"/>
+            <include layout="@layout/empty_list" />
 
             <androidx.recyclerview.widget.RecyclerView
                 android:id="@android:id/list"

+ 29 - 0
src/main/res/menu/synced_folders_adapter.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2019 Andy Scherzinger
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <https://www.gnu.org/licenses/>.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/action_auto_upload_folder_toggle_visibility"
+        android:title="@string/autoupload_hide_folder"
+        android:checkable="true"/>
+
+    <item
+        android:id="@+id/action_auto_upload_folder_settings"
+        android:title="@string/autoupload_configure" />
+</menu>

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

@@ -133,8 +133,6 @@
     <dimen name="synced_folders_item_type_layout_width">32dp</dimen>
     <dimen name="synced_folders_item_type_layout_height">32dp</dimen>
     <dimen name="synced_folders_item_type_layout_right_end_margin">24dp</dimen>
-    <dimen name="synced_folders_progress_bar_layout_width">72dp</dimen>
-    <dimen name="synced_folders_progress_bar_layout_height">72dp</dimen>
     <dimen name="synced_folders_recycler_view_layout_margin">-3dp</dimen>
     <dimen name="toolbar_user_information_layout_margin">12dp</dimen>
     <dimen name="bottom_sheet_text_size">16sp</dimen>

+ 7 - 1
src/main/res/values/strings.xml

@@ -554,7 +554,11 @@
     <string name="choose_remote_folder">Choose remote folder…</string>
     <string name="choose_local_folder">Choose local folder…</string>
     <string name="synced_folders_loading_folders">Loading folders…</string>
-    <string name="synced_folders_no_results">No media folders found.</string>
+    <string name="synced_folders_no_results">No media folders found</string>
+    <plurals name="synced_folders_show_hidden_folders">
+        <item quantity="one">Show %1$d hidden folder</item>
+        <item quantity="other">Show %1$d hidden folders</item>
+    </plurals>
     <string name="synced_folders_preferences">Preferences for auto uploading</string>
     <string name="synced_folders_new_info">Instant uploading has been revamped completely. Re-configure your auto upload from within the main menu.\n\nEnjoy the new and extended auto uploading.</string>
     <string name="synced_folders_preferences_folder_path">For %1$s</string>
@@ -627,6 +631,8 @@
     <!-- Auto upload -->
     <string name="autoupload_custom_folder">Set up a custom folder</string>
     <string name="autoupload_create_new_custom_folder">Create new custom folder setup</string>
+    <string name="autoupload_hide_folder">Hide folder</string>
+    <string name="autoupload_configure">Configure</string>
     <string name="synced_folders_configure_folders">Configure folders</string>
 
     <string name="empty" translatable="false" />

+ 3 - 12
src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java

@@ -32,8 +32,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import third_parties.daveKoeller.AlphanumComparator;
-
 import static org.junit.Assert.assertTrue;
 
 public class SyncedFoldersActivityTest {
@@ -123,14 +121,6 @@ public class SyncedFoldersActivityTest {
         return test(sortedList, SyncedFoldersActivity.sortSyncedFolderItems(unsortedList));
     }
 
-    private List<SyncedFolderDisplayItem> sort(List<SyncedFolderDisplayItem> sortedList) {
-        List<SyncedFolderDisplayItem> unsortedList = shuffle(sortedList);
-
-        Collections.sort(unsortedList, new AlphanumComparator<>());
-
-        return unsortedList;
-    }
-
     private boolean test(List<SyncedFolderDisplayItem> target, List<SyncedFolderDisplayItem> actual) {
 
         for (int i = 0; i < target.size(); i++) {
@@ -167,7 +157,7 @@ public class SyncedFoldersActivityTest {
         return true;
     }
 
-    private SyncedFolderDisplayItem create(String folderName, Boolean enabled) {
+    private SyncedFolderDisplayItem create(String folderName, boolean enabled) {
         return new SyncedFolderDisplayItem(1,
                                            "localPath",
                                            "remotePath",
@@ -181,6 +171,7 @@ public class SyncedFoldersActivityTest {
                                            new ArrayList<String>(),
                                            folderName,
                                            2,
-                                           MediaFolderType.IMAGE);
+                                           MediaFolderType.IMAGE,
+                                           false);
     }
 }