Browse Source

Merge pull request #11588 from nextcloud/1905-autoUploadFolderIcon

Different folder icon for AutoUpload
Andy Scherzinger 2 years ago
parent
commit
206db7c64a
24 changed files with 177 additions and 47 deletions
  1. 1 0
      app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt
  2. 8 0
      app/src/main/java/com/nextcloud/client/di/AppModule.java
  3. 2 7
      app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt
  4. 10 11
      app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt
  5. 2 7
      app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt
  6. 2 2
      app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt
  7. 6 1
      app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt
  8. 9 1
      app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt
  9. 44 0
      app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java
  10. 6 0
      app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java
  11. 3 0
      app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java
  12. 7 0
      app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java
  13. 3 2
      app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt
  14. 4 1
      app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
  15. 5 2
      app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt
  16. 5 0
      app/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java
  17. 4 1
      app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java
  18. 6 1
      app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  19. 4 1
      app/src/main/java/com/owncloud/android/utils/DisplayUtils.java
  20. 2 9
      app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java
  21. 4 0
      app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
  22. 29 0
      app/src/main/res/drawable/folder_auto_upload.xml
  23. 6 1
      app/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt
  24. 5 0
      drawable_resources/folder_auto_upload.svg

+ 1 - 0
app/src/androidTest/java/com/owncloud/android/ui/fragment/GalleryFragmentIT.kt

@@ -87,6 +87,7 @@ class GalleryFragmentIT : AbstractIT() {
         activity.addFragment(sut)
 
         waitForIdleSync()
+        shortSleep()
         screenshot(activity)
     }
 

+ 8 - 0
app/src/main/java/com/nextcloud/client/di/AppModule.java

@@ -58,6 +58,7 @@ import com.owncloud.android.authentication.PassCodeManager;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
 import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.UploadsStorageManager;
 import com.owncloud.android.ui.activities.data.activities.ActivitiesRepository;
 import com.owncloud.android.ui.activities.data.activities.ActivitiesServiceApi;
@@ -121,6 +122,13 @@ class AppModule {
         return new ArbitraryDataProviderImpl(dao);
     }
 
+    @Provides
+    SyncedFolderProvider syncedFolderProvider(ContentResolver contentResolver,
+                                              AppPreferences appPreferences,
+                                              Clock clock) {
+        return new SyncedFolderProvider(contentResolver, appPreferences, clock);
+    }
+
     @Provides
     ActivitiesServiceApi activitiesServiceApi(UserAccountManager accountManager) {
         return new ActivitiesServiceApiImpl(accountManager);

+ 2 - 7
app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt

@@ -32,7 +32,6 @@ import com.nextcloud.client.account.User
 import com.nextcloud.client.account.UserAccountManager
 import com.nextcloud.client.core.Clock
 import com.nextcloud.client.preferences.AppPreferences
-import com.nextcloud.client.preferences.AppPreferencesImpl
 import com.nextcloud.common.NextcloudClient
 import com.nextcloud.java.util.Optional
 import com.owncloud.android.MainApp
@@ -69,7 +68,8 @@ class AccountRemovalWork(
     private val backgroundJobManager: BackgroundJobManager,
     private val clock: Clock,
     private val eventBus: EventBus,
-    private val preferences: AppPreferences
+    private val preferences: AppPreferences,
+    private val syncedFolderProvider: SyncedFolderProvider
 ) : Worker(context, params) {
 
     companion object {
@@ -180,11 +180,6 @@ class AccountRemovalWork(
     }
 
     private fun removeSyncedFolders(context: Context, user: User, clock: Clock) {
-        val syncedFolderProvider = SyncedFolderProvider(
-            context.contentResolver,
-            AppPreferencesImpl.fromContext(context),
-            clock
-        )
         val syncedFolders = syncedFolderProvider.syncedFolders
         val syncedFolderIds: MutableList<Long> = ArrayList()
         for (syncedFolder in syncedFolders) {

+ 10 - 11
app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt

@@ -72,7 +72,8 @@ class BackgroundJobFactory @Inject constructor(
     private val deckApi: DeckApi,
     private val viewThemeUtils: Provider<ViewThemeUtils>,
     private val localBroadcastManager: Provider<LocalBroadcastManager>,
-    private val generatePdfUseCase: GeneratePDFUseCase
+    private val generatePdfUseCase: GeneratePDFUseCase,
+    private val syncedFolderProvider: SyncedFolderProvider
 ) : WorkerFactory() {
 
     @SuppressLint("NewApi")
@@ -90,7 +91,7 @@ class BackgroundJobFactory @Inject constructor(
 
         // ContentObserverWork requires N
         return if (deviceInfo.apiLevel >= Build.VERSION_CODES.N && workerClass == ContentObserverWork::class) {
-            createContentObserverJob(context, workerParameters, clock)
+            createContentObserverJob(context, workerParameters)
         } else {
             when (workerClass) {
                 ContactsBackupWork::class -> createContactsBackupWork(context, workerParameters)
@@ -125,16 +126,14 @@ class BackgroundJobFactory @Inject constructor(
 
     private fun createContentObserverJob(
         context: Context,
-        workerParameters: WorkerParameters,
-        clock: Clock
+        workerParameters: WorkerParameters
     ): ListenableWorker? {
-        val folderResolver = SyncedFolderProvider(contentResolver, preferences, clock)
         @RequiresApi(Build.VERSION_CODES.N)
         if (deviceInfo.apiLevel >= Build.VERSION_CODES.N) {
             return ContentObserverWork(
                 context,
                 workerParameters,
-                folderResolver,
+                syncedFolderProvider,
                 powerManagementService,
                 backgroundJobManager.get()
             )
@@ -186,14 +185,12 @@ class BackgroundJobFactory @Inject constructor(
         return FilesSyncWork(
             context = context,
             params = params,
-            resources = resources,
             contentResolver = contentResolver,
             userAccountManager = accountManager,
-            preferences = preferences,
             uploadsStorageManager = uploadsStorageManager,
             connectivityService = connectivityService,
             powerManagementService = powerManagementService,
-            clock = clock
+            syncedFolderProvider = syncedFolderProvider
         )
     }
 
@@ -217,7 +214,8 @@ class BackgroundJobFactory @Inject constructor(
             accountManager,
             preferences,
             clock,
-            viewThemeUtils.get()
+            viewThemeUtils.get(),
+            syncedFolderProvider
         )
     }
 
@@ -241,7 +239,8 @@ class BackgroundJobFactory @Inject constructor(
             backgroundJobManager.get(),
             clock,
             eventBus,
-            preferences
+            preferences,
+            syncedFolderProvider
         )
     }
 

+ 2 - 7
app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt

@@ -30,10 +30,8 @@ import androidx.exifinterface.media.ExifInterface
 import androidx.work.Worker
 import androidx.work.WorkerParameters
 import com.nextcloud.client.account.UserAccountManager
-import com.nextcloud.client.core.Clock
 import com.nextcloud.client.device.PowerManagementService
 import com.nextcloud.client.network.ConnectivityService
-import com.nextcloud.client.preferences.AppPreferences
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.ArbitraryDataProvider
 import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
@@ -60,14 +58,12 @@ import java.util.TimeZone
 class FilesSyncWork(
     private val context: Context,
     params: WorkerParameters,
-    private val resources: Resources,
     private val contentResolver: ContentResolver,
     private val userAccountManager: UserAccountManager,
-    private val preferences: AppPreferences,
     private val uploadsStorageManager: UploadsStorageManager,
     private val connectivityService: ConnectivityService,
     private val powerManagementService: PowerManagementService,
-    private val clock: Clock
+    private val syncedFolderProvider: SyncedFolderProvider
 ) : Worker(context, params) {
 
     companion object {
@@ -91,10 +87,9 @@ class FilesSyncWork(
             connectivityService,
             powerManagementService
         )
-        FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom)
+        FilesSyncHelper.insertAllDBEntries(skipCustom, syncedFolderProvider)
         // Create all the providers we'll need
         val filesystemDataProvider = FilesystemDataProvider(contentResolver)
-        val syncedFolderProvider = SyncedFolderProvider(contentResolver, preferences, clock)
         val currentLocale = resources.configuration.locale
         val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale)
         dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id)

+ 2 - 2
app/src/main/java/com/nextcloud/client/jobs/MediaFoldersDetectionWork.kt

@@ -68,7 +68,8 @@ class MediaFoldersDetectionWork constructor(
     private val userAccountManager: UserAccountManager,
     private val preferences: AppPreferences,
     private val clock: Clock,
-    private val viewThemeUtils: ViewThemeUtils
+    private val viewThemeUtils: ViewThemeUtils,
+    private val syncedFolderProvider: SyncedFolderProvider
 ) : Worker(context, params) {
 
     companion object {
@@ -86,7 +87,6 @@ class MediaFoldersDetectionWork constructor(
     @Suppress("LongMethod", "ComplexMethod", "NestedBlockDepth") // legacy code
     override fun doWork(): Result {
         val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(context)
-        val syncedFolderProvider = SyncedFolderProvider(contentResolver, preferences, clock)
         val gson = Gson()
         val mediaFoldersModel: MediaFoldersModel
         val imageMediaFolders = MediaProvider.getImageFolders(

+ 6 - 1
app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt

@@ -50,6 +50,7 @@ import com.owncloud.android.databinding.FileActionsBottomSheetBinding
 import com.owncloud.android.databinding.FileActionsBottomSheetItemBinding
 import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.datamodel.SyncedFolderProvider
 import com.owncloud.android.datamodel.ThumbnailsCacheManager
 import com.owncloud.android.lib.resources.files.model.FileLockType
 import com.owncloud.android.ui.activity.ComponentsGetter
@@ -72,6 +73,9 @@ class FileActionsBottomSheet private constructor() : BottomSheetDialogFragment()
     @Inject
     lateinit var storageManager: FileDataStorageManager
 
+    @Inject
+    lateinit var syncedFolderProvider: SyncedFolderProvider
+
     lateinit var viewModel: FileActionsViewModel
 
     private var _binding: FileActionsBottomSheetBinding? = null
@@ -141,7 +145,8 @@ class FileActionsBottomSheet private constructor() : BottomSheetDialogFragment()
                 context,
                 binding.thumbnailLayout.thumbnailShimmer,
                 null,
-                viewThemeUtils
+                viewThemeUtils,
+                syncedFolderProvider
             )
         }
     }

+ 9 - 1
app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt

@@ -33,8 +33,10 @@ import androidx.core.content.pm.ShortcutInfoCompat
 import androidx.core.content.pm.ShortcutManagerCompat
 import androidx.core.graphics.drawable.IconCompat
 import androidx.core.graphics.drawable.toBitmap
+import com.nextcloud.client.account.User
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.datamodel.SyncedFolderProvider
 import com.owncloud.android.datamodel.ThumbnailsCacheManager
 import com.owncloud.android.ui.activity.FileActivity
 import com.owncloud.android.ui.activity.FileDisplayActivity
@@ -49,7 +51,12 @@ class ShortcutUtil @Inject constructor(private val mContext: Context) {
      *
      * @param file The file/folder to which a pinned shortcut should be added to the home screen.
      */
-    fun addShortcutToHomescreen(file: OCFile, viewThemeUtils: ViewThemeUtils) {
+    fun addShortcutToHomescreen(
+        file: OCFile,
+        viewThemeUtils: ViewThemeUtils,
+        user: User,
+        syncedFolderProvider: SyncedFolderProvider
+    ) {
         if (ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)) {
             val intent = Intent(mContext, FileDisplayActivity::class.java)
             intent.action = FileDisplayActivity.OPEN_FILE
@@ -68,6 +75,7 @@ class ShortcutUtil @Inject constructor(private val mContext: Context) {
                     file.isSharedWithMe || file.isSharedWithSharee,
                     file.isSharedViaLink,
                     file.isEncrypted,
+                    syncedFolderProvider.findByRemotePathAndAccount(file.remotePath, user),
                     file.isGroupFolder,
                     file.mountType,
                     mContext,

+ 44 - 0
app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java

@@ -20,6 +20,7 @@
  */
 package com.owncloud.android.datamodel;
 
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -405,4 +406,47 @@ public class SyncedFolderProvider extends Observable {
 
         return cv;
     }
+
+    /**
+     * method to check if sync folder for the remote path exist in table or not
+     *
+     * @param remotePath to be check
+     * @param user       for which we are looking
+     * @return <code>true</code> if exist, <code>false</code> otherwise
+     */
+    public boolean findByRemotePathAndAccount(String remotePath, User user) {
+        boolean result = false;
+
+        //if path ends with / then remove the last / to work the query right way
+        //because the sub folders of synced folders will not have the slash at the end
+        if (remotePath.endsWith("/")) {
+            remotePath = remotePath.substring(0, remotePath.length() - 1);
+        }
+
+        Cursor cursor = mContentResolver.query(
+            ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
+            null,
+            ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH + " LIKE ? AND " +
+                ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " =? ",
+            new String[]{"%" + remotePath + "%", user.getAccountName()},
+            null);
+
+        if (cursor != null && cursor.getCount() >= 1) {
+            result = true;
+        } else {
+            if (cursor == null) {
+                Log_OC.e(TAG, "Sync folder db cursor for remote path = " + remotePath + " in NULL.");
+            } else {
+                Log_OC.e(TAG, cursor.getCount() + " items for remote path = " + remotePath
+                    + " available in sync folder db. Expected 1 or greater than 1. Failed to update sync folder db.");
+            }
+        }
+
+        if (cursor != null) {
+            cursor.close();
+        }
+
+        return result;
+
+    }
 }

+ 6 - 0
app/src/main/java/com/owncloud/android/ui/activity/EditorWebView.java

@@ -43,10 +43,13 @@ import com.nextcloud.java.util.Optional;
 import com.owncloud.android.R;
 import com.owncloud.android.databinding.RichdocumentsWebviewBinding;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 
+import javax.inject.Inject;
+
 public abstract class EditorWebView extends ExternalSiteWebView {
     public static final int REQUEST_LOCAL_FILE = 101;
     public ValueCallback<Uri[]> uploadMessage;
@@ -56,6 +59,8 @@ public abstract class EditorWebView extends ExternalSiteWebView {
 
     RichdocumentsWebviewBinding binding;
 
+    @Inject SyncedFolderProvider syncedFolderProvider;
+
     protected void loadUrl(String url) {
         onUrlLoaded(url);
     }
@@ -225,6 +230,7 @@ public abstract class EditorWebView extends ExternalSiteWebView {
                                                                                   file.isSharedWithSharee(),
                                                                               file.isSharedViaLink(),
                                                                               file.isEncrypted(),
+                                                                              syncedFolderProvider.findByRemotePathAndAccount(file.getRemotePath(), user),
                                                                               file.isGroupFolder(),
                                                                               file.getMountType(),
                                                                               this,

+ 3 - 0
app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -66,6 +66,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.databinding.ReceiveExternalFilesBinding;
 import com.owncloud.android.databinding.UploadFileDialogBinding;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.NameCollisionPolicy;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
@@ -143,6 +144,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
 
     @Inject AppPreferences preferences;
     @Inject LocalBroadcastManager localBroadcastManager;
+    @Inject SyncedFolderProvider syncedFolderProvider;
     private AccountManager mAccountManager;
     private Stack<String> mParents = new Stack<>();
     private List<Parcelable> mStreamsToUpload;
@@ -772,6 +774,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
                                                          new int[]{R.id.filename},
                                                          getStorageManager(),
                                                          getUser().get(),
+                                                         syncedFolderProvider,
                                                          viewThemeUtils);
 
                 binding.list.setAdapter(sa);

+ 7 - 0
app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.java

@@ -30,6 +30,7 @@ import com.nextcloud.java.util.Optional;
 import com.owncloud.android.R;
 import com.owncloud.android.databinding.ShareActivityBinding;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -43,6 +44,8 @@ import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 
+import javax.inject.Inject;
+
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentTransaction;
 
@@ -55,6 +58,9 @@ public class ShareActivity extends FileActivity {
 
     static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT";
 
+    @Inject
+    SyncedFolderProvider syncedFolderProvider;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -75,6 +81,7 @@ public class ShareActivity extends FileActivity {
                                                                                       file.isSharedWithSharee(),
                                                                                   file.isSharedViaLink(),
                                                                                   file.isEncrypted(),
+                                                                                  syncedFolderProvider.findByRemotePathAndAccount(file.getRemotePath(), optionalUser.get()),
                                                                                   file.isGroupFolder(),
                                                                                   file.getMountType(),
                                                                                   this,

+ 3 - 2
app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt

@@ -160,9 +160,11 @@ class SyncedFoldersActivity :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var syncedFolderProvider: SyncedFolderProvider
+
     private lateinit var binding: SyncedFoldersLayoutBinding
     private lateinit var adapter: SyncedFolderAdapter
-    private lateinit var syncedFolderProvider: SyncedFolderProvider
 
     private var syncedFolderPreferencesDialogFragment: SyncedFolderPreferencesDialogFragment? = null
     private var path: String? = null
@@ -256,7 +258,6 @@ class SyncedFoldersActivity :
             lightVersion,
             viewThemeUtils
         )
-        syncedFolderProvider = SyncedFolderProvider(contentResolver, preferences, clock)
         binding.emptyList.emptyListIcon.setImageResource(R.drawable.nav_synced_folders)
         viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.emptyList.emptyListViewAction)
         val lm = GridLayoutManager(this, gridWidth)

+ 4 - 1
app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

@@ -54,6 +54,7 @@ import com.owncloud.android.databinding.ListItemBinding;
 import com.owncloud.android.datamodel.DecryptedFolderMetadata;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.datamodel.VirtualFolderType;
 import com.owncloud.android.db.ProviderMeta;
@@ -136,6 +137,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         Activity activity,
         @NonNull User user,
         AppPreferences preferences,
+        SyncedFolderProvider syncedFolderProvider,
         ComponentsGetter transferServiceGetter,
         OCFileListFragmentInterface ocFileListFragmentInterface,
         boolean argHideItemOptions,
@@ -173,7 +175,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
                                                         .getCapability(activity)
                                                         .getVersion()
                                                         .isShareesOnDavSupported(),
-                                                    viewThemeUtils);
+                                                    viewThemeUtils,
+                                                    syncedFolderProvider);
 
         // initialise thumbnails cache on background thread
         new ThumbnailsCacheManager.InitDiskCacheTask().execute();

+ 5 - 2
app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt

@@ -34,6 +34,7 @@ import com.nextcloud.client.preferences.AppPreferences
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.datamodel.SyncedFolderProvider
 import com.owncloud.android.datamodel.ThumbnailsCacheManager
 import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncGalleryImageDrawable
 import com.owncloud.android.datamodel.ThumbnailsCacheManager.GalleryImageGenerationTask
@@ -61,7 +62,8 @@ class OCFileListDelegate(
     private val transferServiceGetter: ComponentsGetter,
     private val showMetadata: Boolean,
     private var showShareAvatar: Boolean,
-    private var viewThemeUtils: ViewThemeUtils
+    private var viewThemeUtils: ViewThemeUtils,
+    private val syncFolderProvider: SyncedFolderProvider? = null
 ) {
     private val checkedFiles: MutableSet<OCFile> = HashSet()
     private var highlightedItem: OCFile? = null
@@ -216,7 +218,8 @@ class OCFileListDelegate(
             context,
             gridViewHolder.shimmerThumbnail,
             preferences,
-            viewThemeUtils
+            viewThemeUtils,
+            syncFolderProvider
         )
         // item layout + click listeners
         bindGridItemLayout(file, gridViewHolder)

+ 5 - 0
app/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java

@@ -34,6 +34,7 @@ import com.nextcloud.client.account.User;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable;
 import com.owncloud.android.utils.DisplayUtils;
@@ -51,6 +52,7 @@ public class UploaderAdapter extends SimpleAdapter {
     private final FileDataStorageManager mStorageManager;
     private final LayoutInflater inflater;
     private final ViewThemeUtils viewThemeUtils;
+    private SyncedFolderProvider syncedFolderProvider;
 
     public UploaderAdapter(Context context,
                            List<? extends Map<String, ?>> data,
@@ -59,11 +61,13 @@ public class UploaderAdapter extends SimpleAdapter {
                            int[] to,
                            FileDataStorageManager storageManager,
                            User user,
+                           SyncedFolderProvider syncedFolderProvider,
                            ViewThemeUtils viewThemeUtils) {
         super(context, data, resource, from, to);
         this.user = user;
         mStorageManager = storageManager;
         mContext = context;
+        this.syncedFolderProvider = syncedFolderProvider;
         inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         this.viewThemeUtils = viewThemeUtils;
     }
@@ -104,6 +108,7 @@ public class UploaderAdapter extends SimpleAdapter {
             final Drawable icon = MimeTypeUtil.getFolderTypeIcon(isShared,
                                                                  file.isSharedViaLink(),
                                                                  file.isEncrypted(),
+                                                                 syncedFolderProvider.findByRemotePathAndAccount(file.getRemotePath(), user),
                                                                  file.isGroupFolder(),
                                                                  file.getMountType(),
                                                                  mContext,

+ 4 - 1
app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java

@@ -36,6 +36,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.databinding.ConflictResolveDialogBinding;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.adapter.LocalFileListAdapter;
@@ -71,6 +72,7 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
     private final List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
     private Button positiveButton;
     @Inject ViewThemeUtils viewThemeUtils;
+    @Inject SyncedFolderProvider syncedFolderProvider;
 
     private static final String KEY_NEW_FILE = "file";
     private static final String KEY_EXISTING_FILE = "ocfile";
@@ -212,7 +214,8 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
                                   getContext(),
                                   null,
                                   null,
-                                  viewThemeUtils);
+                                  viewThemeUtils,
+                                  syncedFolderProvider);
 
         View.OnClickListener checkBoxClickListener = v ->
             positiveButton.setEnabled(binding.newCheckbox.isChecked() || binding.existingCheckbox.isChecked());

+ 6 - 1
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -50,6 +50,7 @@ import com.nextcloud.android.lib.resources.files.ToggleFileLockRemoteOperation;
 import com.nextcloud.android.lib.richWorkspace.RichWorkspaceDirectEditingRemoteOperation;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
+import com.nextcloud.client.core.Clock;
 import com.nextcloud.client.device.DeviceInfo;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.documentscan.DocumentScanActivity;
@@ -67,6 +68,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.VirtualFolderType;
 import com.owncloud.android.lib.common.Creator;
 import com.owncloud.android.lib.common.OwnCloudClient;
@@ -202,6 +204,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
     @Inject FastScrollUtils fastScrollUtils;
     @Inject EditorUtils editorUtils;
     @Inject ShortcutUtil shortcutUtil;
+    @Inject Clock clock;
+    @Inject SyncedFolderProvider syncedFolderProvider;
 
     protected FileFragment.ContainerActivity mContainerActivity;
 
@@ -422,6 +426,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
             getActivity(),
             accountManager.getUser(),
             preferences,
+            syncedFolderProvider,
             mContainerActivity,
             this,
             hideItemOptions,
@@ -1178,7 +1183,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
             } else if (itemId == R.id.action_unlock_file) {
                 mContainerActivity.getFileOperationsHelper().toggleFileLock(singleFile, false);
             } else if (itemId == R.id.action_pin_to_homescreen) {
-                shortcutUtil.addShortcutToHomescreen(singleFile, viewThemeUtils);
+                shortcutUtil.addShortcutToHomescreen(singleFile, viewThemeUtils, accountManager.getUser(), syncedFolderProvider);
                 return true;
             }
         }

+ 4 - 1
app/src/main/java/com/owncloud/android/utils/DisplayUtils.java

@@ -72,6 +72,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -845,13 +846,15 @@ public final class DisplayUtils {
                                     Context context,
                                     LoaderImageView shimmerThumbnail,
                                     AppPreferences preferences,
-                                    ViewThemeUtils viewThemeUtils) {
+                                    ViewThemeUtils viewThemeUtils,
+                                    SyncedFolderProvider syncedFolderProvider) {
         if (file.isFolder()) {
             stopShimmer(shimmerThumbnail, thumbnailView);
             thumbnailView.setImageDrawable(MimeTypeUtil
                                                .getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(),
                                                                   file.isSharedViaLink(),
                                                                   file.isEncrypted(),
+                                                                  syncedFolderProvider != null && syncedFolderProvider.findByRemotePathAndAccount(file.getRemotePath(), user),
                                                                   file.isGroupFolder(),
                                                                   file.getMountType(),
                                                                   context,

+ 2 - 9
app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java

@@ -32,12 +32,10 @@ import android.os.Build;
 import android.provider.MediaStore;
 
 import com.nextcloud.client.account.UserAccountManager;
-import com.nextcloud.client.core.Clock;
 import com.nextcloud.client.device.BatteryStatus;
 import com.nextcloud.client.device.PowerManagementService;
 import com.nextcloud.client.jobs.BackgroundJobManager;
 import com.nextcloud.client.network.ConnectivityService;
-import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.datamodel.FilesystemDataProvider;
 import com.owncloud.android.datamodel.MediaFolderType;
@@ -122,13 +120,8 @@ public final class FilesSyncHelper {
         }
     }
 
-    public static void insertAllDBEntries(AppPreferences preferences,
-                                          Clock clock,
-                                          boolean skipCustom) {
-        final Context context = MainApp.getAppContext();
-        final ContentResolver contentResolver = context.getContentResolver();
-        SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock);
-
+    public static void insertAllDBEntries(boolean skipCustom,
+                                          SyncedFolderProvider syncedFolderProvider) {
         for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
             if (syncedFolder.isEnabled() && (!skipCustom || syncedFolder.getType() != MediaFolderType.CUSTOM)) {
                 insertAllDBEntriesForSyncedFolder(syncedFolder);

+ 4 - 0
app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java

@@ -144,6 +144,7 @@ public final class MimeTypeUtil {
     public static Drawable getFolderTypeIcon(boolean isSharedViaUsers,
                                              boolean isSharedViaLink,
                                              boolean isEncrypted,
+                                             boolean isAutoUploadFolder,
                                              boolean isGroupFolder,
                                              WebdavEntry.MountType mountType,
                                              Context context,
@@ -158,6 +159,8 @@ public final class MimeTypeUtil {
             drawableId = R.drawable.folder_shared_users;
         } else if (isEncrypted) {
             drawableId = R.drawable.folder_encrypted;
+        } else if (isAutoUploadFolder) {
+            drawableId = R.drawable.folder_auto_upload;
         } else if (WebdavEntry.MountType.EXTERNAL == mountType) {
             drawableId = R.drawable.folder_external;
         } else {
@@ -175,6 +178,7 @@ public final class MimeTypeUtil {
                                  false,
                                  false,
                                  false,
+                                 false,
                                  WebdavEntry.MountType.INTERNAL,
                                  context,
                                  viewThemeUtils);

+ 29 - 0
app/src/main/res/drawable/folder_auto_upload.xml

@@ -0,0 +1,29 @@
+<!--
+    @author Google LLC
+    @author Andy Scherzinger
+    Copyright (C) 2018 Google LLC
+    Copyright (C) 2023 Andy Scherzinger
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="#0082c9"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="nonZero"
+        android:pathData="M10,4L12,6L20,6C21.1,6 22,6.89 22,8L22,18C22,19.097 21.097,20 20,20L4,20C2.903,20 2,19.097 2,18L2,6C2,4.89 2.89,4 4,4L10,4ZM8.412,17.286L15.576,17.286L15.576,16.262L8.412,16.262L8.412,17.286ZM10.459,15.239L13.529,15.239L13.529,12.168L15.576,12.168L11.994,8.586L8.412,12.168L10.459,12.168L10.459,15.239Z" />
+</vector>

+ 6 - 1
app/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt

@@ -36,6 +36,7 @@ import com.nextcloud.client.logger.Logger
 import com.nextcloud.client.network.ConnectivityService
 import com.nextcloud.client.preferences.AppPreferences
 import com.owncloud.android.datamodel.ArbitraryDataProvider
+import com.owncloud.android.datamodel.SyncedFolderProvider
 import com.owncloud.android.datamodel.UploadsStorageManager
 import com.owncloud.android.utils.theme.ViewThemeUtils
 import org.greenrobot.eventbus.EventBus
@@ -109,6 +110,9 @@ class BackgroundJobFactoryTest {
     @Mock
     private lateinit var generatePDFUseCase: GeneratePDFUseCase
 
+    @Mock
+    private lateinit var syncedFolderProvider: SyncedFolderProvider
+
     private lateinit var factory: BackgroundJobFactory
 
     @Before
@@ -132,7 +136,8 @@ class BackgroundJobFactoryTest {
             deckApi,
             { viewThemeUtils },
             { localBroadcastManager },
-            generatePDFUseCase
+            generatePDFUseCase,
+            syncedFolderProvider
         )
     }
 

+ 5 - 0
drawable_resources/folder_auto_upload.svg

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
+    <path d="M10,4L12,6L20,6C21.1,6 22,6.89 22,8L22,18C22,19.097 21.097,20 20,20L4,20C2.903,20 2,19.097 2,18L2,6C2,4.89 2.89,4 4,4L10,4ZM8.412,17.286L15.576,17.286L15.576,16.262L8.412,16.262L8.412,17.286ZM10.459,15.239L13.529,15.239L13.529,12.168L15.576,12.168L11.994,8.586L8.412,12.168L10.459,12.168L10.459,15.239Z" style="fill-rule:nonzero;"/>
+</svg>