Browse Source

Sync all downloaded files in background

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 6 years ago
parent
commit
6cf499828c
30 changed files with 351 additions and 510 deletions
  1. 0 2
      src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java
  2. 0 1
      src/main/java/com/owncloud/android/MainApp.java
  3. 7 13
      src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  4. 14 17
      src/main/java/com/owncloud/android/datamodel/OCFile.java
  5. 6 4
      src/main/java/com/owncloud/android/db/ProviderMeta.java
  6. 0 36
      src/main/java/com/owncloud/android/files/FileMenuFilter.java
  7. 75 67
      src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java
  8. 3 28
      src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java
  9. 7 1
      src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java
  10. 0 1
      src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java
  11. 0 1
      src/main/java/com/owncloud/android/operations/UploadFileOperation.java
  12. 20 2
      src/main/java/com/owncloud/android/providers/FileContentProvider.java
  13. 0 1
      src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
  14. 0 3
      src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
  15. 1 1
      src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java
  16. 0 8
      src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java
  17. 0 10
      src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  18. 0 28
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  19. 4 4
      src/main/java/com/owncloud/android/utils/FilesSyncHelper.java
  20. BIN
      src/main/res/drawable-hdpi/ic_available_offline.png
  21. BIN
      src/main/res/drawable-mdpi/ic_available_offline.png
  22. BIN
      src/main/res/drawable-xhdpi/ic_available_offline.png
  23. BIN
      src/main/res/drawable-xxhdpi/ic_available_offline.png
  24. 0 11
      src/main/res/layout/grid_image.xml
  25. 0 11
      src/main/res/layout/grid_item.xml
  26. 208 220
      src/main/res/layout/list_item.xml
  27. 4 16
      src/main/res/menu/file_actions_menu.xml
  28. 0 10
      src/main/res/menu/file_details_actions_menu.xml
  29. 0 9
      src/main/res/values/dims.xml
  30. 2 5
      src/main/res/values/strings.xml

+ 0 - 2
src/androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java

@@ -85,7 +85,6 @@ public class OCFileUnitTest {
         mFile.setModificationTimestampAtLastSyncForData(MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA);
         mFile.setLastSyncDateForProperties(LAST_SYNC_DATE_FOR_PROPERTIES);
         mFile.setLastSyncDateForData(LAST_SYNC_DATE_FOR_DATA);
-        mFile.setAvailableOffline(true);
         mFile.setEtag(ETAG);
         mFile.setSharedViaLink(true);
         mFile.setSharedWithSharee(true);
@@ -120,7 +119,6 @@ public class OCFileUnitTest {
         );
         assertThat(fileReadFromParcel.getLastSyncDateForProperties(), is(LAST_SYNC_DATE_FOR_PROPERTIES));
         assertThat(fileReadFromParcel.getLastSyncDateForData(), is(LAST_SYNC_DATE_FOR_DATA));
-        assertThat(fileReadFromParcel.isAvailableOffline(), is(true));
         assertThat(fileReadFromParcel.getEtag(), is(ETAG));
         assertThat(fileReadFromParcel.isSharedViaLink(), is(true));
         assertThat(fileReadFromParcel.isSharedWithSharee(), is(true));

+ 0 - 1
src/main/java/com/owncloud/android/MainApp.java

@@ -181,7 +181,6 @@ public class MainApp extends MultiDexApplication {
                 .build()
                 .schedule();
 
-
         // register global protection with pass code
         registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
 

+ 7 - 13
src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -194,8 +194,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
+        cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
@@ -433,8 +433,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isAvailableOffline() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
+        cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, folder.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
@@ -464,8 +464,8 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, account.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
+        cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
@@ -956,8 +956,8 @@ public class FileDataStorageManager {
                     c.getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
             file.setLastSyncDateForProperties(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
             file.setLastSyncDateForData(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
-            file.setAvailableOffline(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1);
             file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
+            file.setEtagOnServer(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_ON_SERVER)));
             file.setSharedViaLink(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1);
             file.setSharedWithSharee(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1);
             file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
@@ -1383,22 +1383,16 @@ public class FileDataStorageManager {
                         ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
                         file.getLastSyncDateForData()
                 );
-                cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
+                cv.put(ProviderTableMeta.FILE_ETAG_ON_SERVER, file.getEtagOnServer());
                 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
                 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
                 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
                 cv.put(ProviderTableMeta.FILE_FAVORITE, file.isFavorite());
-                cv.put(
-                        ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
-                        file.isUpdateThumbnailNeeded() ? 1 : 0
-                );
-                cv.put(
-                        ProviderTableMeta.FILE_IS_DOWNLOADING,
-                        file.isDownloading() ? 1 : 0
-                );
+                cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.isUpdateThumbnailNeeded() ? 1 : 0);
+                cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
 
                 boolean existsByPath = fileExists(file.getRemotePath());

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

@@ -54,18 +54,11 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
 
     private static final String TAG = OCFile.class.getSimpleName();
 
-    @Getter
-    @Setter
-    private long fileId; // android internal ID of the file
+    @Getter  @Setter private long fileId; // android internal ID of the file
     @Getter @Setter private long parentId;
     @Getter @Setter private long fileLength;
-    @Getter
-    @Setter
-    private long creationTimestamp; // UNIX timestamp of the time the file was created
-
-    @Getter
-    @Setter
-    private long modificationTimestamp; // UNIX timestamp of the file modification time
+    @Getter @Setter private long creationTimestamp; // UNIX timestamp of the time the file was created
+    @Getter @Setter private long modificationTimestamp; // UNIX timestamp of the file modification time
     /** UNIX timestamp of the modification time, corresponding to the value returned by the server
      * in the last synchronization of THE CONTENTS of this file.
      */
@@ -79,6 +72,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
     @Getter @Setter private boolean availableOffline;
     @Getter @Setter private boolean previewAvailable;
     @Getter private String etag;
+    @Getter private String etagOnServer;
     @Getter @Setter private boolean sharedViaLink;
     @Getter @Setter private String publicLink;
     @Getter @Setter private String permissions;
@@ -142,10 +136,10 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         localPath = source.readString();
         mimeType = source.readString();
         needsUpdatingWhileSaving = source.readInt() == 0;
-        availableOffline = source.readInt() == 1;
         lastSyncDateForProperties = source.readLong();
         lastSyncDateForData = source.readLong();
         etag = source.readString();
+        etagOnServer = source.readString();
         sharedViaLink = source.readInt() == 1;
         publicLink = source.readString();
         permissions = source.readString();
@@ -172,10 +166,10 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         dest.writeString(localPath);
         dest.writeString(mimeType);
         dest.writeInt(needsUpdatingWhileSaving ? 1 : 0);
-        dest.writeInt(availableOffline ? 1 : 0);
         dest.writeLong(lastSyncDateForProperties);
         dest.writeLong(lastSyncDateForData);
         dest.writeString(etag);
+        dest.writeString(etagOnServer);
         dest.writeInt(sharedViaLink ? 1 : 0);
         dest.writeString(publicLink);
         dest.writeString(permissions);
@@ -392,9 +386,9 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         modificationTimestampAtLastSyncForData = 0;
         lastSyncDateForProperties = 0;
         lastSyncDateForData = 0;
-        availableOffline = false;
         needsUpdatingWhileSaving = false;
         etag = null;
+        etagOnServer = null;
         sharedViaLink = false;
         publicLink = null;
         permissions = null;
@@ -458,10 +452,9 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
     @NonNull
     @Override
     public String toString() {
-        String asString = "[fileId=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, " +
-            "parentId=%s, availableOffline=%s etag=%s favourite=%s]";
-        return String.format(asString, fileId, getFileName(), mimeType, isDown(),
-            localPath, remotePath, parentId, availableOffline,
+        String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, " +
+                "parentId=%s, etag=%s, favourite=%s]";
+        return String.format(asString, fileId, getFileName(), mimeType, isDown(), localPath, remotePath, parentId,
             etag, favorite);
     }
 
@@ -469,6 +462,10 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
         this.etag = etag != null ? etag : "";
     }
 
+    public void setEtagOnServer(String etag) {
+        this.etagOnServer = etag != null ? etag : "";
+    }
+
     public long getLocalModificationTimestamp() {
         if (localPath != null && localPath.length() > 0) {
             File f = new File(localPath);

+ 6 - 4
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 = 40;
+    public static final int DB_VERSION = 41;
 
     private ProviderMeta() {
     }
@@ -92,6 +92,7 @@ public class ProviderMeta {
         public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";
         public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";
         public static final String FILE_ETAG = "etag";
+        public static final String FILE_ETAG_ON_SERVER = "etag_on_server";
         public static final String FILE_SHARED_VIA_LINK = "share_by_link";
         public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users";
         public static final String FILE_PUBLIC_LINK = "public_link";
@@ -109,10 +110,11 @@ public class ProviderMeta {
         public static final String[] FILE_ALL_COLUMNS = {
             _ID, FILE_PARENT, FILE_NAME, FILE_CREATION, FILE_MODIFIED,
             FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, FILE_CONTENT_LENGTH, FILE_CONTENT_TYPE, FILE_STORAGE_PATH,
-            FILE_PATH, FILE_ACCOUNT_OWNER, FILE_LAST_SYNC_DATE, FILE_LAST_SYNC_DATE_FOR_DATA, FILE_KEEP_IN_SYNC,
-            FILE_ETAG, FILE_SHARED_VIA_LINK, FILE_SHARED_WITH_SHAREE, FILE_PUBLIC_LINK, FILE_PERMISSIONS,
+            FILE_PATH, FILE_ACCOUNT_OWNER, FILE_LAST_SYNC_DATE, FILE_LAST_SYNC_DATE_FOR_DATA, FILE_ETAG,
+            FILE_ETAG_ON_SERVER, FILE_SHARED_VIA_LINK, FILE_SHARED_WITH_SHAREE, FILE_PUBLIC_LINK, FILE_PERMISSIONS,
             FILE_REMOTE_ID, FILE_UPDATE_THUMBNAIL, FILE_IS_DOWNLOADING, FILE_ETAG_IN_CONFLICT, FILE_FAVORITE,
-            FILE_IS_ENCRYPTED, FILE_MOUNT_TYPE, FILE_HAS_PREVIEW, FILE_UNREAD_COMMENTS_COUNT};
+            FILE_IS_ENCRYPTED, FILE_MOUNT_TYPE, FILE_HAS_PREVIEW, FILE_UNREAD_COMMENTS_COUNT
+        };
 
         public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc";
 

+ 0 - 36
src/main/java/com/owncloud/android/files/FileMenuFilter.java

@@ -178,8 +178,6 @@ public class FileMenuFilter {
         filterSync(toShow, toHide, synchronizing);
         filterShareFile(toShow, toHide, capability);
         filterDetails(toShow, toHide);
-        filterKeepAvailableOffline(toShow, toHide, synchronizing);
-        filterDontKeepAvailableOffline(toShow, toHide, synchronizing);
         filterFavorite(toShow, toHide, synchronizing);
         filterUnfavorite(toShow, toHide, synchronizing);
         filterEncrypt(toShow, toHide, endToEndEncryptionEnabled);
@@ -207,22 +205,6 @@ public class FileMenuFilter {
         }
     }
 
-    private void filterKeepAvailableOffline(List<Integer> toShow, List<Integer> toHide, boolean synchronizing) {
-        if (!allFiles() || synchronizing || allKeptAvailableOffline()) {
-            toHide.add(R.id.action_keep_files_offline);
-        } else {
-            toShow.add(R.id.action_keep_files_offline);
-        }
-    }
-
-    private void filterDontKeepAvailableOffline(List<Integer> toShow, List<Integer> toHide, boolean synchronizing) {
-        if (!allFiles() || synchronizing || allNotKeptAvailableOffline()) {
-            toHide.add(R.id.action_unset_keep_files_offline);
-        } else {
-            toShow.add(R.id.action_unset_keep_files_offline);
-        }
-    }
-
     private void filterFavorite(List<Integer> toShow, List<Integer> toHide, boolean synchronizing) {
         if (mFiles.isEmpty() || synchronizing || allFavorites()) {
             toHide.add(R.id.action_favorite);
@@ -497,15 +479,6 @@ public class FileMenuFilter {
         return false;
     }
 
-    private boolean allKeptAvailableOffline() {
-        for (OCFile file : mFiles) {
-            if (!file.isAvailableOffline()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private boolean allFavorites() {
         for (OCFile file : mFiles) {
             if (!file.isFavorite()) {
@@ -523,13 +496,4 @@ public class FileMenuFilter {
         }
         return true;
     }
-
-    private boolean allNotKeptAvailableOffline() {
-        for (OCFile file : mFiles) {
-            if (file.isAvailableOffline()) {
-                return false;
-            }
-        }
-        return true;
-    }
 }

+ 75 - 67
src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java

@@ -21,8 +21,6 @@ package com.owncloud.android.jobs;
 
 import android.accounts.Account;
 import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
 import android.os.Build;
 import android.os.PowerManager;
 
@@ -34,15 +32,15 @@ import com.owncloud.android.MainApp;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.CheckEtagRemoteOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
-import com.owncloud.android.ui.activity.ConflictsResolveActivity;
 import com.owncloud.android.utils.ConnectivityUtils;
+import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.PowerUtils;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.io.File;
 import java.util.Set;
 
 import androidx.annotation.NonNull;
@@ -51,12 +49,11 @@ public class OfflineSyncJob extends Job {
     public static final String TAG = "OfflineSyncJob";
 
     private static final String WAKELOCK_TAG_SEPARATION = ":";
-    private List<OfflineFile> offlineFileList = new ArrayList<>();
 
     @NonNull
     @Override
     protected Result onRunJob(@NonNull Params params) {
-        final Context context = MainApp.getAppContext();
+        final Context context = getContext();
 
         PowerManager.WakeLock wakeLock = null;
         if (!PowerUtils.isPowerSaveMode(context) &&
@@ -71,58 +68,27 @@ public class OfflineSyncJob extends Job {
 
             if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-                wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainApp.getAuthority() +
+
+                if (powerManager != null) {
+                    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainApp.getAuthority() +
                         WAKELOCK_TAG_SEPARATION + TAG);
-                wakeLock.acquire(10 * 60 * 1000);
+                    wakeLock.acquire(10 * 60 * 1000);
+                }
             }
 
-            Cursor cursorOnKeptInSync = context.getContentResolver().query(
-                    ProviderMeta.ProviderTableMeta.CONTENT_URI,
-                    null,
-                    ProviderMeta.ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?",
-                    new String[]{String.valueOf(1)},
-                    null
-            );
-
-            if (cursorOnKeptInSync != null) {
-                if (cursorOnKeptInSync.moveToFirst()) {
-
-                    String localPath;
-                    String accountName;
-                    Account account;
-                    do {
-                        localPath = cursorOnKeptInSync.getString(cursorOnKeptInSync
-                                .getColumnIndex(ProviderMeta.ProviderTableMeta.FILE_STORAGE_PATH));
-                        accountName = cursorOnKeptInSync.getString(cursorOnKeptInSync
-                                .getColumnIndex(ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER));
-
-                        account = new Account(accountName, MainApp.getAccountType(getContext()));
-                        if (!AccountUtils.exists(account, context) || localPath == null || localPath.length() <= 0) {
-                            continue;
-                        }
+            Account[] accounts = AccountUtils.getAccounts(context);
 
-                        offlineFileList.add(new OfflineFile(localPath, account));
+            for (Account account : accounts) {
+                FileDataStorageManager storageManager = new FileDataStorageManager(account,
+                        getContext().getContentResolver());
 
-                    } while (cursorOnKeptInSync.moveToNext());
+                OCFile ocRoot = storageManager.getFileByPath("/");
 
+                if (ocRoot.getStoragePath() == null) {
+                    break;
                 }
-                cursorOnKeptInSync.close();
-            }
 
-            FileDataStorageManager storageManager;
-            for (OfflineFile offlineFile : offlineFileList) {
-                storageManager = new FileDataStorageManager(offlineFile.getAccount(), context.getContentResolver());
-                OCFile file = storageManager.getFileByLocalPath(offlineFile.getLocalPath());
-                SynchronizeFileOperation sfo =
-                        new SynchronizeFileOperation(file, null, offlineFile.getAccount(), true, context);
-                RemoteOperationResult result = sfo.execute(storageManager, context);
-                if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) {
-                    Intent i = new Intent(context, ConflictsResolveActivity.class);
-                    i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
-                    i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, offlineFile.getAccount());
-                    context.startActivity(i);
-                }
+                recursive(new File(ocRoot.getStoragePath()), storageManager, account);
             }
 
             if (wakeLock != null) {
@@ -133,30 +99,72 @@ public class OfflineSyncJob extends Job {
         return Result.SUCCESS;
     }
 
+    private void recursive(File folder, FileDataStorageManager storageManager, Account account) {
+        String downloadFolder = FileStorageUtils.getSavePath(account.name);
+        String folderName = folder.getAbsolutePath().replaceFirst(downloadFolder, "") + "/";
+        Log_OC.d(TAG, folderName + ": enter");
 
-    private class OfflineFile {
-        private String localPath;
-        private Account account;
-
-        private OfflineFile(String localPath, Account account) {
-            this.localPath = localPath;
-            this.account = account;
+        // exit
+        if (folder.listFiles() == null) {
+            return;
         }
 
-        public String getLocalPath() {
-            return localPath;
+        OCFile ocFolder = storageManager.getFileByPath(folderName);
+        Log_OC.d(TAG, folderName + ": currentEtag: " + ocFolder.getEtag());
+
+        // check for etag change, if false, skip
+        CheckEtagRemoteOperation checkEtagOperation = new CheckEtagRemoteOperation(ocFolder.getRemotePath(),
+                                                                                   ocFolder.getEtagOnServer());
+        RemoteOperationResult result = checkEtagOperation.execute(account, getContext());
+
+        // eTag changed, sync file
+        switch (result.getCode()) {
+            case ETAG_UNCHANGED:
+                Log_OC.d(TAG, folderName + ": eTag unchanged");
+                return;
+
+            case FILE_NOT_FOUND:
+                boolean removalResult = storageManager.removeFolder(ocFolder, true, true);
+                if (!removalResult) {
+                    Log_OC.e(TAG, "removal of " + ocFolder.getStoragePath() + " failed: file not found");
+                }
+                return;
+
+            default:
+            case ETAG_CHANGED:
+                Log_OC.d(TAG, folderName + ": eTag changed");
+                break;
         }
 
-        public void setLocalPath(String localPath) {
-            this.localPath = localPath;
+        // iterate over downloaded files
+        File[] files = folder.listFiles(File::isFile);
+
+        if (files != null) {
+            for (File file : files) {
+                OCFile ocFile = storageManager.getFileByLocalPath(file.getPath());
+                SynchronizeFileOperation synchronizeFileOperation = new SynchronizeFileOperation(ocFile.getRemotePath(),
+                        account, true, getContext());
+
+                synchronizeFileOperation.execute(storageManager, getContext());
+            }
         }
 
-        public Account getAccount() {
-            return account;
+        // recursive into folder
+        File[] subfolders = folder.listFiles(File::isDirectory);
+
+        if (subfolders != null) {
+            for (File subfolder : subfolders) {
+                recursive(subfolder, storageManager, account);
+            }
         }
 
-        public void setAccount(Account account) {
-            this.account = account;
+        // update eTag
+        try {
+            String updatedEtag = (String) result.getData().get(0);
+            ocFolder.setEtagOnServer(updatedEtag);
+            storageManager.saveFile(ocFolder);
+        } catch (Exception e) {
+            Log_OC.e(TAG, "Failed to update etag on " + folder.getAbsolutePath(), e);
         }
     }
 }

+ 3 - 28
src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java

@@ -196,7 +196,6 @@ public class RefreshFolderOperation extends RemoteOperation {
             if (mRemoteFolderChanged) {
                 result = fetchAndSyncRemoteFolder(client);
             } else {
-                fetchKeptInSyncFilesToSyncFromLocalData();
                 mChildren = mStorageManager.getFolderContent(mLocalFolder, false);
             }
 
@@ -399,13 +398,6 @@ public class RefreshFolderOperation extends RemoteOperation {
             // check and fix, if needed, local storage path
             FileStorageUtils.searchForLocalFileInDefaultPath(updatedFile, mAccount);
 
-            // prepare content synchronization for kept-in-sync files
-            if (updatedFile.isAvailableOffline()) {
-                mFilesToSyncContents.add(
-                        new SynchronizeFileOperation(localFile, remoteFile, mAccount, true, mContext)
-                );
-            }
-
             // update file name for encrypted files
             if (metadata != null) {
                 updateFileNameForEncryptedFile(metadata, updatedFile);
@@ -456,7 +448,6 @@ public class RefreshFolderOperation extends RemoteOperation {
     private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, OCFile updatedFile, boolean remoteFolderChanged) {
         if (localFile != null) {
             updatedFile.setFileId(localFile.getFileId());
-            updatedFile.setAvailableOffline(localFile.isAvailableOffline());
             updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
             updatedFile.setModificationTimestampAtLastSyncForData(
                     localFile.getModificationTimestampAtLastSyncForData()
@@ -488,6 +479,9 @@ public class RefreshFolderOperation extends RemoteOperation {
             // remote eTag will not be updated unless file CONTENTS are synchronized
             updatedFile.setEtag("");
         }
+
+        // eTag on Server is used for thumbnail validation
+        updatedFile.setEtagOnServer(remoteFile.getEtag());
     }
 
     @NonNull
@@ -592,24 +586,5 @@ public class RefreshFolderOperation extends RemoteOperation {
 
         intent.setPackage(mContext.getPackageName());
         mContext.sendStickyBroadcast(intent);
-        //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
     }
-
-
-    private void fetchKeptInSyncFilesToSyncFromLocalData() {
-        List<OCFile> children = mStorageManager.getFolderContent(mLocalFolder, false);
-        for (OCFile child : children) {
-            if (!child.isFolder() && child.isAvailableOffline() && !child.isInConflict()) {
-                SynchronizeFileOperation operation = new SynchronizeFileOperation(
-                        child,
-                        child,  // cheating with the remote file to get an update to server; to refactor
-                        mAccount,
-                        true,
-                        mContext
-                );
-                mFilesToSyncContents.add(operation);
-            }
-        }
-    }
-
 }

+ 7 - 1
src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java

@@ -246,7 +246,6 @@ public class SynchronizeFileOperation extends SyncOperation {
                     } else {
                         // TODO CHECK: is this really useful in some point in the code?
                         mServerFile.setFavorite(mLocalFile.isFavorite());
-                        mServerFile.setAvailableOffline(mLocalFile.isAvailableOffline());
                         mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
                         mServerFile.setStoragePath(mLocalFile.getStoragePath());
                         mServerFile.setParentId(mLocalFile.getParentId());
@@ -265,6 +264,13 @@ public class SynchronizeFileOperation extends SyncOperation {
                 if (result.getCode() != ResultCode.SYNC_CONFLICT) {
                     getStorageManager().saveConflict(mLocalFile, null);
                 }
+            } else {
+                // remote file does not exist, deleting local copy
+                boolean deleteResult = getStorageManager().removeFile(mLocalFile, true, true);
+
+                if (!deleteResult) {
+                    Log_OC.e(TAG, "Removal of local copy failed (remote file does not exist any longer).");
+                }
             }
 
         }

+ 0 - 1
src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@ -308,7 +308,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
         updatedFile.setLastSyncDateForProperties(mCurrentSyncTime);
         if (localFile != null) {
             updatedFile.setFileId(localFile.getFileId());
-            updatedFile.setAvailableOffline(localFile.isAvailableOffline());
             updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
             updatedFile.setModificationTimestampAtLastSyncForData(
                     localFile.getModificationTimestampAtLastSyncForData()

+ 0 - 1
src/main/java/com/owncloud/android/operations/UploadFileOperation.java

@@ -1055,7 +1055,6 @@ public class UploadFileOperation extends SyncOperation {
                 mFile.getModificationTimestampAtLastSyncForData()
         );
         newFile.setEtag(mFile.getEtag());
-        newFile.setAvailableOffline(mFile.isAvailableOffline());
         newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
         newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
         newFile.setStoragePath(mFile.getStoragePath());

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

@@ -735,15 +735,15 @@ public class FileContentProvider extends ContentProvider {
                 + ProviderTableMeta.FILE_STORAGE_PATH + TEXT
                 + ProviderTableMeta.FILE_ACCOUNT_OWNER + TEXT
                 + ProviderTableMeta.FILE_LAST_SYNC_DATE + INTEGER
-                + ProviderTableMeta.FILE_KEEP_IN_SYNC + INTEGER
                 + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + INTEGER
                 + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + INTEGER
                 + ProviderTableMeta.FILE_ETAG + TEXT
+                       + ProviderTableMeta.FILE_ETAG_ON_SERVER + TEXT
                 + ProviderTableMeta.FILE_SHARED_VIA_LINK + INTEGER
                 + ProviderTableMeta.FILE_PUBLIC_LINK + TEXT
                 + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
                 + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
-                + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + INTEGER //boolean
+                       + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + INTEGER //boolean
                 + ProviderTableMeta.FILE_IS_DOWNLOADING + INTEGER //boolean
                 + ProviderTableMeta.FILE_FAVORITE + INTEGER // boolean
                 + ProviderTableMeta.FILE_IS_ENCRYPTED + INTEGER // boolean
@@ -1863,6 +1863,24 @@ public class FileContentProvider extends ContentProvider {
             if (!upgraded) {
                 Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
             }
+
+            if (oldVersion < 41 && newVersion >= 41) {
+                Log_OC.i(SQL, "Entering in the #41 add eTagOnServer");
+                db.beginTransaction();
+                try {
+                    db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME +
+                        ADD_COLUMN + ProviderTableMeta.FILE_ETAG_ON_SERVER + " TEXT ");
+
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
+            if (!upgraded) {
+                Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion));
+            }
         }
 
         @Override

+ 0 - 1
src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java

@@ -440,7 +440,6 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View
 
             itemView.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
             itemView.findViewById(R.id.favorite_action).setVisibility(View.GONE);
-            itemView.findViewById(R.id.keptOfflineIcon).setVisibility(View.GONE);
             itemView.findViewById(R.id.localFileIndicator).setVisibility(View.GONE);
         }
     }

+ 0 - 3
src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

@@ -359,7 +359,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
             }
 
             gridViewHolder.favorite.setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
-            gridViewHolder.offlineIcon.setVisibility(file.isAvailableOffline() ? View.VISIBLE : View.GONE);
 
             if (multiSelect) {
                 gridViewHolder.checkbox.setVisibility(View.VISIBLE);
@@ -848,7 +847,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
     static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder {
         private final ImageView thumbnail;
         private final ImageView favorite;
-        private final ImageView offlineIcon;
         private final ImageView localFileIndicator;
         private final ImageView shared;
         private final ImageView checkbox;
@@ -860,7 +858,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 
             thumbnail = itemView.findViewById(R.id.thumbnail);
             favorite = itemView.findViewById(R.id.favorite_action);
-            offlineIcon = itemView.findViewById(R.id.keptOfflineIcon);
             localFileIndicator = itemView.findViewById(R.id.localFileIndicator);
             shared = itemView.findViewById(R.id.sharedIcon);
             checkbox = itemView.findViewById(R.id.custom_checkbox);

+ 1 - 1
src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java

@@ -79,7 +79,7 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
         for (OCFile file: files) {
             containsFolder |= file.isFolder();
             containsDown |= file.isDown();
-            containsFavorite |= file.isAvailableOffline();
+            containsFavorite |= file.isFavorite();
         }
 
         if (files.size() == SINGLE_SELECTION) {

+ 0 - 8
src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -419,14 +419,6 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
             }
-            case R.id.action_keep_files_offline: {
-                mContainerActivity.getFileOperationsHelper().toggleOfflineFile(getFile(), true);
-                return true;
-            }
-            case R.id.action_unset_keep_files_offline: {
-                mContainerActivity.getFileOperationsHelper().toggleOfflineFile(getFile(), false);
-                return true;
-            }
             case R.id.action_encrypted: {
                 // TODO implement or remove
                 return true;

+ 0 - 10
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -1030,16 +1030,6 @@ public class OCFileListFragment extends ExtendedListFragment implements
                 ((FileDisplayActivity) mContainerActivity).cancelTransference(checkedFiles);
                 return true;
             }
-            case R.id.action_keep_files_offline: {
-                mContainerActivity.getFileOperationsHelper().toggleOfflineFiles(checkedFiles, true);
-                exitSelectionMode();
-                return true;
-            }
-            case R.id.action_unset_keep_files_offline: {
-                mContainerActivity.getFileOperationsHelper().toggleOfflineFiles(checkedFiles, false);
-                exitSelectionMode();
-                return true;
-            }
             case R.id.action_favorite: {
                 mContainerActivity.getFileOperationsHelper().toggleFavoriteFiles(checkedFiles, true);
                 return true;

+ 0 - 28
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -834,34 +834,6 @@ public class FileOperationsHelper {
         }
     }
 
-    public void toggleOfflineFiles(Collection<OCFile> files, boolean isAvailableOffline) {
-        List<OCFile> alreadyRightStateList = new ArrayList<>();
-        for (OCFile file : files) {
-            if (file.isAvailableOffline() == isAvailableOffline) {
-                alreadyRightStateList.add(file);
-            }
-        }
-
-        files.removeAll(alreadyRightStateList);
-
-        for (OCFile file : files) {
-            toggleOfflineFile(file, isAvailableOffline);
-        }
-    }
-
-
-    public void toggleOfflineFile(OCFile file, boolean isAvailableOffline) {
-        if (file.isAvailableOffline() != isAvailableOffline) {
-            file.setAvailableOffline(isAvailableOffline);
-            mFileActivity.getStorageManager().saveFile(file);
-
-            /// immediate content synchronization
-            if (file.isAvailableOffline()) {
-                syncFile(file);
-            }
-        }
-    }
-
     public void renameFile(OCFile file, String newFilename) {
         // RenameFile
         Intent service = new Intent(mFileActivity, OperationsService.class);

+ 4 - 4
src/main/java/com/owncloud/android/utils/FilesSyncHelper.java

@@ -261,10 +261,10 @@ public final class FilesSyncHelper {
         Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(OfflineSyncJob.TAG);
         if (jobRequests.isEmpty()) {
             new JobRequest.Builder(OfflineSyncJob.TAG)
-                    .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
-                    .setUpdateCurrent(false)
-                    .build()
-                    .schedule();
+                .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
+                .setUpdateCurrent(false)
+                .build()
+                .schedule();
         }
     }
 

BIN
src/main/res/drawable-hdpi/ic_available_offline.png


BIN
src/main/res/drawable-mdpi/ic_available_offline.png


BIN
src/main/res/drawable-xhdpi/ic_available_offline.png


BIN
src/main/res/drawable-xxhdpi/ic_available_offline.png


+ 0 - 11
src/main/res/layout/grid_image.xml

@@ -85,17 +85,6 @@
             android:contentDescription="@string/synced_icon"
             android:src="@drawable/ic_synced" />
 
-        <ImageView
-            android:id="@+id/keptOfflineIcon"
-            android:layout_width="@dimen/grid_image_kept_offline_icon_layout_width"
-            android:layout_height="@dimen/grid_image_kept_offline_icon_layout_height"
-            android:layout_gravity="bottom|end"
-            android:layout_marginBottom="@dimen/standard_quarter_margin"
-            android:layout_marginEnd="@dimen/grid_image_kept_offline_icon_layout_right_end_margin"
-            android:layout_marginRight="@dimen/grid_image_kept_offline_icon_layout_right_end_margin"
-            android:contentDescription="@string/available_offline_icon"
-            android:src="@drawable/ic_available_offline" />
-
         <ImageView
             android:id="@+id/custom_checkbox"
             android:layout_width="wrap_content"

+ 0 - 11
src/main/res/layout/grid_item.xml

@@ -83,17 +83,6 @@
             android:src="@drawable/ic_synced"
             android:contentDescription="@string/synced_icon"/>
 
-        <ImageView
-            android:id="@+id/keptOfflineIcon"
-            android:layout_width="@dimen/grid_item_kept_offline_icon_layout_width"
-            android:layout_height="@dimen/grid_item_kept_offline_icon_layout_height"
-            android:layout_gravity="bottom|end"
-            android:layout_marginBottom="@dimen/standard_quarter_margin"
-            android:layout_marginRight="@dimen/grid_item_kept_offline_icon_layout_right_end_margin"
-            android:layout_marginEnd="@dimen/grid_item_kept_offline_icon_layout_right_end_margin"
-            android:src="@drawable/ic_available_offline"
-            android:contentDescription="@string/available_offline_icon"/>
-
         <ImageView
             android:id="@+id/custom_checkbox"
             android:layout_width="wrap_content"

+ 208 - 220
src/main/res/layout/list_item.xml

@@ -1,220 +1,208 @@
-<?xml version="1.0" encoding="UTF-8"?><!--
-  ownCloud Android client application
-
-  Copyright (C) 2012  Bartek Przybylski
-  Copyright (C) 2015 ownCloud Inc.
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License version 2,
-  as published by the Free Software Foundation.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ListItemLayout"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/standard_list_item_size"
-    android:background="@drawable/list_selector"
-    android:descendantFocusability="blocksDescendants"
-    android:foreground="?android:attr/selectableItemBackground"
-    android:baselineAligned="false"
-    android:orientation="horizontal">
-
-    <RelativeLayout
-        android:layout_width="@dimen/standard_list_item_size"
-        android:layout_height="@dimen/standard_list_item_size"
-        android:paddingBottom="@dimen/standard_padding"
-        android:paddingEnd="@dimen/standard_quarter_padding"
-        android:paddingLeft="@dimen/zero"
-        android:paddingRight="@dimen/standard_quarter_padding"
-        android:paddingStart="@dimen/zero"
-        android:paddingTop="@dimen/standard_padding">
-
-        <ImageView
-            android:id="@+id/thumbnail"
-            android:layout_width="@dimen/file_icon_size"
-            android:layout_height="@dimen/file_icon_size"
-            android:layout_centerInParent="true"
-            android:layout_marginLeft="@dimen/standard_half_margin"
-            android:layout_marginStart="@dimen/standard_half_margin"
-            android:contentDescription="@null"
-            android:src="@drawable/folder" />
-
-        <ImageView
-            android:id="@+id/favorite_action"
-            android:layout_width="@dimen/list_item_favorite_action_layout_width"
-            android:layout_height="@dimen/list_item_favorite_action_layout_height"
-            android:layout_alignParentEnd="true"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentTop="true"
-            android:layout_marginEnd="@dimen/standard_quarter_margin"
-            android:layout_marginRight="@dimen/standard_quarter_margin"
-            android:contentDescription="@string/favorite"
-            android:src="@drawable/badge_favorite" />
-
-        <ImageView
-            android:id="@+id/keptOfflineIcon"
-            android:layout_width="@dimen/list_item_kept_offline_icon_layout_width"
-            android:layout_height="@dimen/list_item_kept_offline_icon_layout_height"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentEnd="true"
-            android:layout_alignParentRight="true"
-            android:layout_marginEnd="@dimen/list_item_kept_offline_icon_layout_right_end_margin"
-            android:layout_marginRight="@dimen/list_item_kept_offline_icon_layout_right_end_margin"
-            android:contentDescription="@string/available_offline_icon"
-            android:src="@drawable/ic_available_offline" />
-
-        <ImageView
-            android:id="@+id/localFileIndicator"
-            android:layout_width="@dimen/list_item_local_file_indicator_layout_width"
-            android:layout_height="@dimen/list_item_local_file_indicator_layout_height"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentEnd="true"
-            android:layout_alignParentRight="true"
-            android:layout_marginEnd="@dimen/standard_quarter_margin"
-            android:layout_marginRight="@dimen/standard_quarter_margin"
-            android:contentDescription="@string/downloader_download_succeeded_ticker"
-            android:scaleType="fitCenter"
-            android:src="@drawable/ic_synced" />
-
-    </RelativeLayout>
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="top"
-        android:orientation="vertical"
-        android:paddingTop="@dimen/standard_padding">
-
-        <TextView
-            android:id="@+id/Filename"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:ellipsize="middle"
-            android:singleLine="true"
-            android:text="@string/placeholder_filename"
-            android:textColor="@color/textColor"
-            android:textSize="@dimen/two_line_primary_text_size" />
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <TextView
-                android:id="@+id/file_size"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/placeholder_fileSize"
-                android:textColor="@color/list_item_lastmod_and_filesize_text"
-                android:textSize="@dimen/two_line_secondary_text_size" />
-
-            <TextView
-                android:id="@+id/file_separator"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="end"
-                android:paddingEnd="@dimen/standard_quarter_padding"
-                android:paddingLeft="@dimen/zero"
-                android:paddingRight="@dimen/standard_quarter_padding"
-                android:paddingStart="@dimen/zero"
-                android:text="@string/info_separator"
-                android:textColor="@color/list_item_lastmod_and_filesize_text"
-                android:textSize="@dimen/two_line_secondary_text_size" />
-
-            <TextView
-                android:id="@+id/last_mod"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="end"
-                android:text="@string/placeholder_media_time"
-                android:textColor="@color/list_item_lastmod_and_filesize_text"
-                android:textSize="@dimen/two_line_secondary_text_size" />
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:paddingEnd="@dimen/zero"
-        android:paddingLeft="@dimen/standard_half_padding"
-        android:paddingRight="@dimen/zero"
-        android:paddingStart="@dimen/standard_half_padding">
-
-        <ImageView
-            android:id="@+id/unreadComments"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:clickable="true"
-            android:contentDescription="@string/unread_comments"
-            android:focusable="true"
-            android:paddingEnd="@dimen/list_item_share_right_margin"
-            android:paddingLeft="@dimen/standard_half_padding"
-            android:paddingRight="@dimen/list_item_share_right_margin"
-            android:paddingStart="@dimen/standard_half_padding"
-            android:src="@drawable/ic_comment"
-            android:visibility="gone"/>
-
-        <ImageView
-            android:id="@+id/sharedIcon"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/unreadComments"
-            android:layout_toRightOf="@id/unreadComments"
-            android:clickable="true"
-            android:contentDescription="@string/shared_icon_share"
-            android:focusable="true"
-            android:paddingEnd="@dimen/list_item_share_right_margin"
-            android:paddingLeft="@dimen/standard_half_padding"
-            android:paddingRight="@dimen/list_item_share_right_margin"
-            android:paddingStart="@dimen/standard_half_padding"
-            android:src="@drawable/ic_unshared" />
-
-        <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:clickable="false"
-            android:contentDescription="@string/checkbox"
-            android:focusable="false"
-            android:paddingEnd="@dimen/alternate_padding"
-            android:paddingLeft="@dimen/standard_half_padding"
-            android:paddingRight="@dimen/alternate_padding"
-            android:paddingStart="@dimen/standard_half_padding"
-            android:src="@drawable/ic_checkbox_blank_outline" />
-
-        <ImageView
-            android:id="@+id/overflow_menu"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/custom_checkbox"
-            android:layout_toRightOf="@id/custom_checkbox"
-            android:clickable="true"
-            android:contentDescription="@string/overflow_menu"
-            android:focusable="true"
-            android:paddingEnd="@dimen/alternate_padding"
-            android:paddingLeft="@dimen/standard_half_padding"
-            android:paddingRight="@dimen/alternate_padding"
-            android:paddingStart="@dimen/standard_half_padding"
-            android:src="@drawable/ic_dots_vertical" />
-
-    </RelativeLayout>
-
-</LinearLayout>
+<?xml version="1.0" encoding="UTF-8"?><!--
+  ownCloud Android client application
+
+  Copyright (C) 2012  Bartek Przybylski
+  Copyright (C) 2015 ownCloud Inc.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License version 2,
+  as published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ListItemLayout"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/standard_list_item_size"
+    android:background="@drawable/list_selector"
+    android:descendantFocusability="blocksDescendants"
+    android:foreground="?android:attr/selectableItemBackground"
+    android:baselineAligned="false"
+    android:orientation="horizontal">
+
+    <RelativeLayout
+        android:layout_width="@dimen/standard_list_item_size"
+        android:layout_height="@dimen/standard_list_item_size"
+        android:paddingBottom="@dimen/standard_padding"
+        android:paddingEnd="@dimen/standard_quarter_padding"
+        android:paddingLeft="@dimen/zero"
+        android:paddingRight="@dimen/standard_quarter_padding"
+        android:paddingStart="@dimen/zero"
+        android:paddingTop="@dimen/standard_padding">
+
+        <ImageView
+            android:id="@+id/thumbnail"
+            android:layout_width="@dimen/file_icon_size"
+            android:layout_height="@dimen/file_icon_size"
+            android:layout_centerInParent="true"
+            android:layout_marginLeft="@dimen/standard_half_margin"
+            android:layout_marginStart="@dimen/standard_half_margin"
+            android:contentDescription="@null"
+            android:src="@drawable/folder" />
+
+        <ImageView
+            android:id="@+id/favorite_action"
+            android:layout_width="@dimen/list_item_favorite_action_layout_width"
+            android:layout_height="@dimen/list_item_favorite_action_layout_height"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginEnd="@dimen/standard_quarter_margin"
+            android:layout_marginRight="@dimen/standard_quarter_margin"
+            android:contentDescription="@string/favorite"
+            android:src="@drawable/badge_favorite" />
+
+            <ImageView
+                android:id="@+id/localFileIndicator"
+                android:layout_width="@dimen/list_item_local_file_indicator_layout_width"
+                android:layout_height="@dimen/list_item_local_file_indicator_layout_height"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_marginEnd="@dimen/standard_quarter_margin"
+                android:layout_marginRight="@dimen/standard_quarter_margin"
+                android:contentDescription="@string/downloader_download_succeeded_ticker"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_synced" />
+
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="top"
+        android:orientation="vertical"
+        android:paddingTop="@dimen/standard_padding">
+
+        <TextView
+            android:id="@+id/Filename"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:ellipsize="middle"
+            android:singleLine="true"
+            android:text="@string/placeholder_filename"
+            android:textColor="@color/textColor"
+            android:textSize="@dimen/two_line_primary_text_size" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/file_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/placeholder_fileSize"
+                android:textColor="@color/list_item_lastmod_and_filesize_text"
+                android:textSize="@dimen/two_line_secondary_text_size" />
+
+            <TextView
+                android:id="@+id/file_separator"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="end"
+                android:paddingEnd="@dimen/standard_quarter_padding"
+                android:paddingLeft="@dimen/zero"
+                android:paddingRight="@dimen/standard_quarter_padding"
+                android:paddingStart="@dimen/zero"
+                android:text="@string/info_separator"
+                android:textColor="@color/list_item_lastmod_and_filesize_text"
+                android:textSize="@dimen/two_line_secondary_text_size" />
+
+            <TextView
+                android:id="@+id/last_mod"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="end"
+                android:text="@string/placeholder_media_time"
+                android:textColor="@color/list_item_lastmod_and_filesize_text"
+                android:textSize="@dimen/two_line_secondary_text_size" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="@dimen/zero"
+        android:paddingLeft="@dimen/standard_half_padding"
+        android:paddingRight="@dimen/zero"
+        android:paddingStart="@dimen/standard_half_padding">
+
+        <ImageView
+            android:id="@+id/unreadComments"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerVertical="true"
+            android:clickable="true"
+            android:contentDescription="@string/unread_comments"
+            android:focusable="true"
+            android:paddingEnd="@dimen/list_item_share_right_margin"
+            android:paddingLeft="@dimen/standard_half_padding"
+            android:paddingRight="@dimen/list_item_share_right_margin"
+            android:paddingStart="@dimen/standard_half_padding"
+            android:src="@drawable/ic_comment"
+            android:visibility="gone"/>
+
+        <ImageView
+            android:id="@+id/sharedIcon"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/unreadComments"
+            android:layout_toRightOf="@id/unreadComments"
+            android:clickable="true"
+            android:contentDescription="@string/shared_icon_share"
+            android:focusable="true"
+            android:paddingEnd="@dimen/list_item_share_right_margin"
+            android:paddingLeft="@dimen/standard_half_padding"
+            android:paddingRight="@dimen/list_item_share_right_margin"
+            android:paddingStart="@dimen/standard_half_padding"
+            android:src="@drawable/ic_unshared" />
+
+        <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:clickable="false"
+            android:contentDescription="@string/checkbox"
+            android:focusable="false"
+            android:paddingEnd="@dimen/alternate_padding"
+            android:paddingLeft="@dimen/standard_half_padding"
+            android:paddingRight="@dimen/alternate_padding"
+            android:paddingStart="@dimen/standard_half_padding"
+            android:src="@drawable/ic_checkbox_blank_outline" />
+
+        <ImageView
+            android:id="@+id/overflow_menu"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/custom_checkbox"
+            android:layout_toRightOf="@id/custom_checkbox"
+            android:clickable="true"
+            android:contentDescription="@string/overflow_menu"
+            android:focusable="true"
+            android:paddingEnd="@dimen/alternate_padding"
+            android:paddingLeft="@dimen/standard_half_padding"
+            android:paddingRight="@dimen/alternate_padding"
+            android:paddingStart="@dimen/standard_half_padding"
+            android:src="@drawable/ic_dots_vertical" />
+
+    </RelativeLayout>
+
+</LinearLayout>

+ 4 - 16
src/main/res/menu/file_actions_menu.xml

@@ -24,13 +24,13 @@
 
     <item
         android:id="@+id/action_favorite"
-        android:title="@string/favorite_real"
+        android:title="@string/favorite"
         app:showAsAction="never"
         android:showAsAction="never" />
-    
+
     <item
         android:id="@+id/action_unset_favorite"
-        android:title="@string/unset_favorite_real"
+        android:title="@string/unset_favorite"
         app:showAsAction="never"
         android:showAsAction="never" />
 
@@ -70,7 +70,7 @@
         app:showAsAction="never"
         android:showAsAction="never"
         android:orderInCategory="1"/>
-    
+
     <item
         android:id="@+id/action_send_share_file"
         android:title="@string/action_send_share"
@@ -113,18 +113,6 @@
         app:showAsAction="never"
         android:showAsAction="never" />
 
-    <item
-        android:id="@+id/action_keep_files_offline"
-        android:title="@string/favorite"
-        app:showAsAction="never"
-        android:showAsAction="never" />
-
-    <item
-        android:id="@+id/action_unset_keep_files_offline"
-        android:title="@string/unfavorite"
-        app:showAsAction="never"
-        android:showAsAction="never" />
-
     <item
         android:id="@+id/action_encrypted"
         android:title="@string/encrypted"

+ 0 - 10
src/main/res/menu/file_details_actions_menu.xml

@@ -53,16 +53,6 @@
         android:title="@string/common_cancel_sync"
         app:showAsAction="never"
         android:showAsAction="never" />
-    <item
-        android:id="@+id/action_keep_files_offline"
-        android:title="@string/favorite"
-        app:showAsAction="never"
-        android:showAsAction="never" />
-    <item
-        android:id="@+id/action_unset_keep_files_offline"
-        android:title="@string/unfavorite"
-        app:showAsAction="never"
-        android:showAsAction="never" />
     <item
         android:id="@+id/action_encrypted"
         android:title="@string/encrypted"

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

@@ -114,22 +114,13 @@
     <dimen name="grid_image_shared_icon_layout_top_margin">24dp</dimen>
     <dimen name="grid_image_local_file_indicator_layout_width">16dp</dimen>
     <dimen name="grid_image_local_file_indicator_layout_height">16dp</dimen>
-    <dimen name="grid_image_kept_offline_icon_layout_width">16dp</dimen>
-    <dimen name="grid_image_kept_offline_icon_layout_height">16dp</dimen>
-    <dimen name="grid_image_kept_offline_icon_layout_right_end_margin">24dp</dimen>
     <dimen name="grid_item_shared_icon_layout_top_margin">24dp</dimen>
     <dimen name="grid_item_local_file_indicator_layout_width">16dp</dimen>
     <dimen name="grid_item_local_file_indicator_layout_height">16dp</dimen>
-    <dimen name="grid_item_kept_offline_icon_layout_width">16dp</dimen>
-    <dimen name="grid_item_kept_offline_icon_layout_height">16dp</dimen>
-    <dimen name="grid_item_kept_offline_icon_layout_right_end_margin">24dp</dimen>
     <dimen name="grid_sync_item_layout_next_text_size">22sp</dimen>
     <dimen name="grid_sync_item_layout_counter_text_size">22sp</dimen>
     <dimen name="list_item_favorite_action_layout_width">16dp</dimen>
     <dimen name="list_item_favorite_action_layout_height">16dp</dimen>
-    <dimen name="list_item_kept_offline_icon_layout_width">12dp</dimen>
-    <dimen name="list_item_kept_offline_icon_layout_height">12dp</dimen>
-    <dimen name="list_item_kept_offline_icon_layout_right_end_margin">20dp</dimen>
     <dimen name="list_item_local_file_indicator_layout_width">12dp</dimen>
     <dimen name="list_item_local_file_indicator_layout_height">12dp</dimen>
     <dimen name="preview_error_image_layout_width">72dp</dimen>

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

@@ -268,10 +268,8 @@
     <string name="auth_access_failed">Access failed: %1$s</string>
     <string name="auth_illegal_login_used">Illegal login data URL used</string>
 
-    <string name="favorite">Set as available offline</string>
-    <string name="unfavorite">Unset as available offline</string>
-    <string name="favorite_real">Add to favorites</string>
-    <string name="unset_favorite_real">Remove from favourites</string>
+    <string name="favorite">Add to favorites</string>
+    <string name="unset_favorite">Remove from favourites</string>
     <string name="encrypted">Set as encrypted</string>
     <string name="unset_encrypted">Unset encryption</string>
     <string name="common_rename">Rename</string>
@@ -677,7 +675,6 @@
     <string name="user_icon">User</string>
     <string name="favorite_icon">Favorite</string>
     <string name="synced_icon">Synced</string>
-    <string name="available_offline_icon">Available offline</string>
     <string name="checkbox">Checkbox</string>
     <string name="shared_icon_share">share</string>
     <string name="shared_icon_shared_via_link">shared via link</string>