Просмотр исходного кода

Extra options for uploading subfolders

Adds some extra options for uploading photos into various patterns of
subfolders. yyyy or yyyy/MM or yyyy/MM/dd supported.

Currently just adds an extra field for this, and has to be used in
conjunction with the "use subfolder" checkbox, the new option is greyed
out and disabled when "use subfolder" is not checked.

Signed-off-by: Dean Birch <dean.birch0@gmail.com>
Dean Birch 1 год назад
Родитель
Сommit
dcabaf2a7d

+ 3 - 1
app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt

@@ -57,5 +57,7 @@ data class SyncedFolderEntity(
     @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_TYPE)
     val type: Int?,
     @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_HIDDEN)
-    val hidden: Int?
+    val hidden: Int?,
+    @ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE)
+    val subFolderRule: Int?
 )

+ 6 - 1
app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt

@@ -32,6 +32,7 @@ import androidx.work.WorkerParameters
 import com.nextcloud.client.account.UserAccountManager
 import com.nextcloud.client.device.PowerManagementService
 import com.nextcloud.client.network.ConnectivityService
+import com.nextcloud.client.preferences.SubFolderRule
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.ArbitraryDataProvider
 import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
@@ -203,12 +204,15 @@ class FilesSyncWork(
         val lastModificationTime = calculateLastModificationTime(file, syncedFolder, sFormatter)
         val remoteFolder: String
         val useSubfolders: Boolean
+        val subFolderRule: SubFolderRule
         if (lightVersion) {
             useSubfolders = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders)
             remoteFolder = resources.getString(R.string.syncedFolder_remote_folder)
+            subFolderRule = SubFolderRule.YEAR_MONTH
         } else {
             useSubfolders = syncedFolder.isSubfolderByDate
             remoteFolder = syncedFolder.remotePath
+            subFolderRule = syncedFolder.subfolderRule
         }
         return FileStorageUtils.getInstantUploadFilePath(
             file,
@@ -216,7 +220,8 @@ class FilesSyncWork(
             remoteFolder,
             syncedFolder.localPath,
             lastModificationTime,
-            useSubfolders
+            useSubfolders,
+            subFolderRule
         )
     }
 

+ 7 - 0
app/src/main/java/com/nextcloud/client/preferences/SubFolderRule.java

@@ -0,0 +1,7 @@
+package com.nextcloud.client.preferences;
+
+public enum SubFolderRule {
+    YEAR_MONTH,
+    YEAR,
+    YEAR_MONTH_DAY,
+}

+ 15 - 3
app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java

@@ -21,6 +21,7 @@
 
 package com.owncloud.android.datamodel;
 
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.files.services.NameCollisionPolicy;
 
 import java.io.Serializable;
@@ -33,6 +34,8 @@ public class SyncedFolder implements Serializable, Cloneable {
     public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1;
     private static final long serialVersionUID = -793476118299906429L;
 
+
+
     private long id;
     private String localPath;
     private String remotePath;
@@ -47,6 +50,7 @@ public class SyncedFolder implements Serializable, Cloneable {
     private long enabledTimestampMs;
     private MediaFolderType type;
     private boolean hidden;
+    private SubFolderRule subfolderRule;
 
     /**
      * constructor for new, to be persisted entity.
@@ -77,7 +81,8 @@ public class SyncedFolder implements Serializable, Cloneable {
                         boolean enabled,
                         long timestampMs,
                         MediaFolderType type,
-                        boolean hidden) {
+                        boolean hidden,
+                        SubFolderRule subFolderRule) {
         this(UNPERSISTED_ID,
              localPath,
              remotePath,
@@ -91,7 +96,8 @@ public class SyncedFolder implements Serializable, Cloneable {
              enabled,
              timestampMs,
              type,
-             hidden);
+             hidden,
+             subFolderRule);
     }
 
     /**
@@ -112,7 +118,8 @@ public class SyncedFolder implements Serializable, Cloneable {
                            boolean enabled,
                            long timestampMs,
                            MediaFolderType type,
-                           boolean hidden) {
+                           boolean hidden,
+                           SubFolderRule subFolderRule) {
         this.id = id;
         this.localPath = localPath;
         this.remotePath = remotePath;
@@ -126,6 +133,7 @@ public class SyncedFolder implements Serializable, Cloneable {
         this.setEnabled(enabled, timestampMs);
         this.type = type;
         this.hidden = hidden;
+        this.subfolderRule = subFolderRule;
     }
 
     /**
@@ -204,6 +212,8 @@ public class SyncedFolder implements Serializable, Cloneable {
         return this.hidden;
     }
 
+    public SubFolderRule getSubfolderRule() { return this.subfolderRule; }
+
     public void setId(long id) {
         this.id = id;
     }
@@ -251,4 +261,6 @@ public class SyncedFolder implements Serializable, Cloneable {
     public void setHidden(boolean hidden) {
         this.hidden = hidden;
     }
+
+    public void setSubFolderRule(SubFolderRule subFolderRule) { this.subfolderRule = subFolderRule; }
 }

+ 12 - 4
app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java

@@ -21,6 +21,8 @@
 
 package com.owncloud.android.datamodel;
 
+import com.nextcloud.client.preferences.SubFolderRule;
+
 import java.util.List;
 
 /**
@@ -32,6 +34,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
     private String folderName;
     private long numberOfFiles;
 
+
     /**
      * constructor for the display item specialization for a synced folder object.
      *
@@ -50,6 +53,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
      * @param numberOfFiles   the UI info for number of files within the folder
      * @param type            the type of the folder
      * @param hidden          hide item flag
+     * @param subFolderRule   whether to filter subFolder by year/month/day
      */
     public SyncedFolderDisplayItem(long id,
                                    String localPath,
@@ -67,7 +71,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
                                    String folderName,
                                    long numberOfFiles,
                                    MediaFolderType type,
-                                   boolean hidden) {
+                                   boolean hidden,
+                                   SubFolderRule subFolderRule) {
         super(id,
               localPath,
               remotePath,
@@ -81,7 +86,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
               enabled,
               timestampMs,
               type,
-              hidden);
+              hidden,
+              subFolderRule);
         this.filePaths = filePaths;
         this.folderName = folderName;
         this.numberOfFiles = numberOfFiles;
@@ -101,7 +107,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
                                    long timestampMs,
                                    String folderName,
                                    MediaFolderType type,
-                                   boolean hidden) {
+                                   boolean hidden,
+                                   SubFolderRule subFolderRule) {
         super(id,
               localPath,
               remotePath,
@@ -115,7 +122,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
               enabled,
               timestampMs,
               type,
-              hidden);
+              hidden,
+              subFolderRule);
         this.folderName = folderName;
     }
 

+ 7 - 1
app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java

@@ -31,6 +31,7 @@ import com.nextcloud.client.account.User;
 import com.nextcloud.client.core.Clock;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.nextcloud.client.preferences.AppPreferencesImpl;
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
@@ -361,6 +362,9 @@ public class SyncedFolderProvider extends Observable {
                     ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)));
             boolean hidden = cursor.getInt(cursor.getColumnIndexOrThrow(
                 ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1;
+            SubFolderRule subFolderRule = SubFolderRule.values()[cursor.getInt(
+                    cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE))];
+
 
             syncedFolder = new SyncedFolder(id,
                                             localPath,
@@ -375,7 +379,8 @@ public class SyncedFolderProvider extends Observable {
                                             enabled,
                                             enabledTimestampMs,
                                             type,
-                                            hidden);
+                                            hidden,
+                                            subFolderRule);
         }
         return syncedFolder;
     }
@@ -403,6 +408,7 @@ public class SyncedFolderProvider extends Observable {
                syncedFolder.getNameCollisionPolicyInt());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId());
         cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden());
+        cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE, syncedFolder.getSubfolderRule().ordinal());
 
         return cv;
     }

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

@@ -292,6 +292,7 @@ public class ProviderMeta {
         public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option";
         public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy";
         public static final String SYNCED_FOLDER_HIDDEN = "hidden";
+        public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule";
 
         // Columns of external links table
         public static final String EXTERNAL_LINKS_ICON_URL = "icon_url";

+ 16 - 7
app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt

@@ -44,6 +44,7 @@ import com.nextcloud.client.jobs.BackgroundJobManager
 import com.nextcloud.client.jobs.MediaFoldersDetectionWork
 import com.nextcloud.client.jobs.NotificationWork
 import com.nextcloud.client.preferences.AppPreferences
+import com.nextcloud.client.preferences.SubFolderRule
 import com.owncloud.android.BuildConfig
 import com.owncloud.android.MainApp
 import com.owncloud.android.R
@@ -400,7 +401,8 @@ class SyncedFoldersActivity :
             localFolder.name,
             files.size.toLong(),
             syncedFolder.type,
-            syncedFolder.isHidden
+            syncedFolder.isHidden,
+            syncedFolder.subfolderRule,
         )
     }
 
@@ -429,7 +431,8 @@ class SyncedFoldersActivity :
             mediaFolder.folderName,
             mediaFolder.numberOfFiles,
             mediaFolder.type,
-            syncedFolder.isHidden
+            syncedFolder.isHidden,
+            syncedFolder.subfolderRule,
         )
     }
 
@@ -457,7 +460,8 @@ class SyncedFoldersActivity :
             mediaFolder.folderName,
             mediaFolder.numberOfFiles,
             mediaFolder.type,
-            false
+            false,
+            SubFolderRule.YEAR_MONTH,
         )
     }
 
@@ -548,7 +552,8 @@ class SyncedFoldersActivity :
                         clock.currentTime,
                         null,
                         MediaFolderType.CUSTOM,
-                        false
+                        false,
+                        SubFolderRule.YEAR_MONTH,
                     )
                     onSyncFolderSettingsClick(0, emptyCustomFolder)
                 } else {
@@ -657,7 +662,8 @@ class SyncedFoldersActivity :
                 clock.currentTime,
                 File(syncedFolder.localPath).name,
                 syncedFolder.type,
-                syncedFolder.isHidden
+                syncedFolder.isHidden,
+                syncedFolder.subFolderRule,
             )
             saveOrUpdateSyncedFolder(newCustomFolder)
             adapter.addSyncFolderItem(newCustomFolder)
@@ -674,7 +680,8 @@ class SyncedFoldersActivity :
                 syncedFolder.isSubfolderByDate,
                 syncedFolder.uploadAction,
                 syncedFolder.nameCollisionPolicy.serialize(),
-                syncedFolder.isEnabled
+                syncedFolder.isEnabled,
+                syncedFolder.subFolderRule,
             )
             saveOrUpdateSyncedFolder(item)
 
@@ -754,7 +761,8 @@ class SyncedFoldersActivity :
         subfolderByDate: Boolean,
         uploadAction: Int,
         nameCollisionPolicy: Int,
-        enabled: Boolean
+        enabled: Boolean,
+        subFolderRule: SubFolderRule,
     ) {
         item.id = id
         item.localPath = localPath
@@ -766,6 +774,7 @@ class SyncedFoldersActivity :
         item.uploadAction = uploadAction
         item.setNameCollisionPolicy(nameCollisionPolicy)
         item.setEnabled(enabled, clock.currentTime)
+        item.setSubFolderRule(subFolderRule)
     }
 
     override fun onRequestPermissionsResult(

+ 45 - 6
app/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java

@@ -30,14 +30,18 @@ import android.text.TextUtils;
 import android.text.style.StyleSpan;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.Spinner;
 import android.widget.TextView;
 
 import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.nextcloud.client.di.Injectable;
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.R;
 import com.owncloud.android.databinding.SyncedFoldersSettingsLayoutBinding;
 import com.owncloud.android.datamodel.MediaFolderType;
+import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
 import com.owncloud.android.files.services.NameCollisionPolicy;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -87,11 +91,13 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
     private AppCompatCheckBox mUploadOnChargingCheckbox;
     private AppCompatCheckBox mUploadExistingCheckbox;
     private AppCompatCheckBox mUploadUseSubfoldersCheckbox;
+    private Spinner mUploadSubfolderRuleSpinner;
     private TextView mUploadBehaviorSummary;
     private TextView mNameCollisionPolicySummary;
     private TextView mLocalFolderPath;
     private TextView mLocalFolderSummary;
     private TextView mRemoteFolderSummary;
+    private TextView mUploadSubfolderRulesLabel;
 
     private SyncedFolderParcelable mSyncedFolder;
     private MaterialButton mCancel;
@@ -182,6 +188,11 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
 
         mUploadUseSubfoldersCheckbox = binding.settingInstantUploadPathUseSubfoldersCheckbox;
 
+        mUploadSubfolderRuleSpinner = binding.settingInstantUploadSubfolderRuleSpinner;
+        mUploadSubfolderRulesLabel = binding.settingInstantUploadSubfolderRulesLabel;
+
+
+
         viewThemeUtils.platform.themeCheckbox(mUploadOnWifiCheckbox,
                                               mUploadOnChargingCheckbox,
                                               mUploadExistingCheckbox,
@@ -227,6 +238,11 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
         mUploadExistingCheckbox.setChecked(mSyncedFolder.isExisting());
         mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate());
 
+        mUploadSubfolderRuleSpinner.setSelection(mSyncedFolder.getSubFolderRule().ordinal());
+        mUploadSubfolderRuleSpinner.setEnabled(mUploadUseSubfoldersCheckbox.isChecked());
+        mUploadSubfolderRuleSpinner.setAlpha(getAlpha(mUploadUseSubfoldersCheckbox.isChecked()));
+        mUploadSubfolderRulesLabel.setAlpha(getAlpha(mUploadUseSubfoldersCheckbox.isChecked()));
+
         mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]);
 
         final int nameCollisionPolicyIndex =
@@ -312,12 +328,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
     }
 
     private void setupViews(SyncedFoldersSettingsLayoutBinding binding, boolean enable) {
-        float alpha;
-        if (enable) {
-            alpha = alphaEnabled;
-        } else {
-            alpha = alphaDisabled;
-        }
+        float alpha = getAlpha(enable);
         binding.settingInstantUploadOnWifiContainer.setEnabled(enable);
         binding.settingInstantUploadOnWifiContainer.setAlpha(alpha);
 
@@ -347,6 +358,16 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
         checkWritableFolder();
     }
 
+    private static float getAlpha(boolean enable) {
+        float alpha;
+        if (enable) {
+            alpha = alphaEnabled;
+        } else {
+            alpha = alphaDisabled;
+        }
+        return alpha;
+    }
+
     /**
      * setup all listeners.
      *
@@ -390,9 +411,27 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment implem
                 public void onClick(View v) {
                     mSyncedFolder.setSubfolderByDate(!mSyncedFolder.isSubfolderByDate());
                     mUploadUseSubfoldersCheckbox.toggle();
+                    // Only allow setting subfolder rule if subfolder is allowed
+                    mUploadSubfolderRuleSpinner.setEnabled(mUploadUseSubfoldersCheckbox.isChecked());
+                    mUploadSubfolderRuleSpinner.setAlpha(getAlpha(mUploadSubfolderRuleSpinner.isEnabled()));
+                    mUploadSubfolderRulesLabel.setAlpha(getAlpha(mUploadSubfolderRuleSpinner.isEnabled()));
                 }
             });
 
+        binding.settingInstantUploadSubfolderRuleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
+                mSyncedFolder.setSubFolderRule(SubFolderRule.values()[i]);
+                var x = mSyncedFolder.getSubFolderRule();
+                var y = mSyncedFolder.getSubFolderRule();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> adapterView) {
+                mSyncedFolder.setSubFolderRule(SubFolderRule.YEAR_MONTH);
+            }
+        });
+
         binding.remoteFolderContainer.setOnClickListener(v -> {
             Intent action = new Intent(getActivity(), FolderPickerActivity.class);
             getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_REMOTE_FOLDER);

+ 9 - 0
app/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java

@@ -23,7 +23,9 @@ package com.owncloud.android.ui.dialog.parcel;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.datamodel.MediaFolderType;
+import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.NameCollisionPolicy;
@@ -47,6 +49,7 @@ public class SyncedFolderParcelable implements Parcelable {
     private long id;
     private String account;
     private int section;
+    private SubFolderRule subFolderRule;
 
     public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) {
         id = syncedFolderDisplayItem.getId();
@@ -65,6 +68,7 @@ public class SyncedFolderParcelable implements Parcelable {
             syncedFolderDisplayItem.getNameCollisionPolicyInt());
         this.section = section;
         hidden = syncedFolderDisplayItem.isHidden();
+        subFolderRule = syncedFolderDisplayItem.getSubfolderRule();
     }
 
     private SyncedFolderParcelable(Parcel read) {
@@ -83,6 +87,7 @@ public class SyncedFolderParcelable implements Parcelable {
         nameCollisionPolicy = NameCollisionPolicy.deserialize(read.readInt());
         section = read.readInt();
         hidden = read.readInt() != 0;
+        subFolderRule = SubFolderRule.values()[read.readInt()];
     }
 
     public SyncedFolderParcelable() {
@@ -106,6 +111,7 @@ public class SyncedFolderParcelable implements Parcelable {
         dest.writeInt(nameCollisionPolicy.serialize());
         dest.writeInt(section);
         dest.writeInt(hidden ? 1 : 0);
+        dest.writeInt(subFolderRule.ordinal());
     }
 
     public static final Creator<SyncedFolderParcelable> CREATOR =
@@ -216,6 +222,8 @@ public class SyncedFolderParcelable implements Parcelable {
         return this.section;
     }
 
+    public SubFolderRule getSubFolderRule() { return this.subFolderRule; }
+
     public void setFolderName(String folderName) {
         this.folderName = folderName;
     }
@@ -271,4 +279,5 @@ public class SyncedFolderParcelable implements Parcelable {
     public void setSection(int section) {
         this.section = section;
     }
+    public void setSubFolderRule(SubFolderRule subFolderRule) { this.subFolderRule = subFolderRule; }
 }

+ 17 - 4
app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java

@@ -28,10 +28,12 @@ import android.os.Environment;
 import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.model.RemoteFile;
 import com.owncloud.android.lib.resources.shares.ShareeUser;
@@ -68,6 +70,8 @@ public final class FileStorageUtils {
     private static final String TAG = FileStorageUtils.class.getSimpleName();
 
     private static final String PATTERN_YYYY_MM = "yyyy/MM/";
+    private static final String PATTERN_YYYY = "yyyy/";
+    private static final String PATTERN_YYYY_MM_DD = "yyyy/MM/dd/";
     private static final String DEFAULT_FALLBACK_STORAGE_PATH = "/storage/sdcard0";
 
     private FileStorageUtils() {
@@ -144,14 +148,22 @@ public final class FileStorageUtils {
      * @param date: date in microseconds since 1st January 1970
      * @return string: yyyy/mm/
      */
-    private static String getSubPathFromDate(long date, Locale currentLocale) {
+    private static String getSubPathFromDate(long date, Locale currentLocale, SubFolderRule subFolderRule) {
         if (date == 0) {
             return "";
         }
+        String datePattern = "";
+        if (subFolderRule == SubFolderRule.YEAR) {
+            datePattern = PATTERN_YYYY;
+        } else if (subFolderRule == SubFolderRule.YEAR_MONTH) {
+            datePattern = PATTERN_YYYY_MM;
+        } else if (subFolderRule == SubFolderRule.YEAR_MONTH_DAY) {
+            datePattern = PATTERN_YYYY_MM_DD;
+        }
 
         Date d = new Date(date);
 
-        DateFormat df = new SimpleDateFormat(PATTERN_YYYY_MM, currentLocale);
+        DateFormat df = new SimpleDateFormat(datePattern, currentLocale);
         df.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
 
         return df.format(d);
@@ -168,10 +180,11 @@ public final class FileStorageUtils {
                                                   String remotePath,
                                                   String syncedFolderLocalPath,
                                                   long dateTaken,
-                                                  Boolean subfolderByDate) {
+                                                  Boolean subfolderByDate,
+                                                  SubFolderRule subFolderRule) {
         String subfolderByDatePath = "";
         if (subfolderByDate) {
-            subfolderByDatePath = getSubPathFromDate(dateTaken, current);
+            subfolderByDatePath = getSubPathFromDate(dateTaken, current, subFolderRule);
         }
 
         File parentFile = new File(file.getAbsolutePath().replace(syncedFolderLocalPath, "")).getParentFile();

+ 28 - 0
app/src/main/res/layout/synced_folders_settings_layout.xml

@@ -303,6 +303,34 @@
                 </LinearLayout>
             </LinearLayout>
 
+            <LinearLayout
+                android:id="@+id/setting_instant_upload_subfolder_rule_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:baselineAligned="false"
+                android:clipToPadding="false"
+                android:gravity="center_vertical"
+                android:minHeight="?attr/listPreferredItemHeightSmall"
+                android:orientation="vertical"
+                android:padding="@dimen/standard_padding">
+
+                <TextView
+                    android:id="@+id/setting_instant_upload_subfolder_rules_label"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="marquee"
+                    android:maxLines="2"
+                    android:text="@string/prefs_instant_upload_subfolder_rule_title"
+                    android:textAppearance="?attr/textAppearanceListItem" />
+
+                <Spinner
+                    android:id="@+id/setting_instant_upload_subfolder_rule_spinner"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:entries="@array/sub_folder_rule_values" />
+            </LinearLayout>
+
             <LinearLayout
                 android:id="@+id/setting_instant_behaviour_container"
                 android:layout_width="match_parent"

+ 5 - 0
app/src/main/res/values/attrs.xml

@@ -29,6 +29,11 @@
         <item>@string/link_share_view_only</item>
         <item>@string/link_share_editing</item>
     </string-array>
+    <string-array name="sub_folder_rule_values" translatable="false">
+        <item>@string/sub_folder_rule_month</item>
+        <item>@string/sub_folder_rule_year</item>
+        <item>@string/sub_folder_rule_day</item>
+    </string-array>
     <declare-styleable name="AppWidgetAttrs">
         <attr name="appWidgetPadding" format="dimension" />
         <attr name="appWidgetInnerRadius" format="dimension" />

+ 6 - 1
app/src/main/res/values/strings.xml

@@ -355,7 +355,9 @@
     <string name="prefs_synced_folders_local_path_title">Local folder</string>
     <string name="prefs_synced_folders_remote_path_title">Remote folder</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Use subfolders</string>
-    <string name="prefs_instant_upload_path_use_subfolders_summary">Store in subfolders based on year and month</string>
+    <string name="prefs_instant_upload_path_use_subfolders_summary">Store in subfolders based on date</string>
+
+    <string name="prefs_instant_upload_subfolder_rule_title">Subfolder options</string>
 
     <string name="share_link_file_no_exist">Unable to share. Please check whether the file exists.</string>
     <string name="share_link_file_error">An error occurred while trying to share this file or folder.</string>
@@ -1108,4 +1110,7 @@
     <string name="image_preview_filedetails">File details</string>
     <string name="image_preview_image_taking_conditions">Image taking conditions</string>
     <string translatable="false" name="make_model">%1$s %2$s</string>
+    <string name="sub_folder_rule_year">Year</string>
+    <string name="sub_folder_rule_month">Year/Month</string>
+    <string name="sub_folder_rule_day">Year/Month/Day</string>
 </resources>

+ 4 - 1
app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java

@@ -22,7 +22,9 @@
 
 package com.owncloud.android.ui.activity;
 
+import com.nextcloud.client.preferences.SubFolderRule;
 import com.owncloud.android.datamodel.MediaFolderType;
+import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.NameCollisionPolicy;
@@ -176,6 +178,7 @@ public class SyncedFoldersActivityTest {
                                            folderName,
                                            2,
                                            MediaFolderType.IMAGE,
-                                           false);
+                                           false,
+                                           SubFolderRule.YEAR_MONTH);
     }
 }