Ver código fonte

wip

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 2 anos atrás
pai
commit
aeac90881d

+ 0 - 2
.idea/codeStyles/Project.xml

@@ -58,8 +58,6 @@
           <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
         </value>
       </option>
-      <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
-      <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </JetCodeStyleSettings>
     <MarkdownNavigatorCodeStyleSettings>

+ 4 - 0
app/src/main/java/com/nextcloud/client/di/ComponentsModule.java

@@ -109,6 +109,7 @@ import com.owncloud.android.ui.fragment.FileDetailSharingFragment;
 import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
 import com.owncloud.android.ui.fragment.GalleryFragmentBottomSheetDialog;
+import com.owncloud.android.ui.fragment.GroupfolderListFragment;
 import com.owncloud.android.ui.fragment.LocalFileListFragment;
 import com.owncloud.android.ui.fragment.OCFileListBottomSheetDialog;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
@@ -462,4 +463,7 @@ abstract class ComponentsModule {
 
     @ContributesAndroidInjector
     abstract DocumentScanActivity documentScanActivity();
+
+    @ContributesAndroidInjector
+    abstract GroupfolderListFragment groupfolderListFragment();
 }

+ 9 - 0
app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -100,6 +100,7 @@ import com.owncloud.android.ui.events.DummyDrawerEvent;
 import com.owncloud.android.ui.events.SearchEvent;
 import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
+import com.owncloud.android.ui.fragment.GroupfolderListFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.fragment.SharedListFragment;
 import com.owncloud.android.ui.preview.PreviewTextStringFragment;
@@ -422,6 +423,7 @@ public abstract class DrawerActivity extends ToolbarActivity
             if (this instanceof FileDisplayActivity &&
                 !(((FileDisplayActivity) this).getLeftFragment() instanceof GalleryFragment) &&
                 !(((FileDisplayActivity) this).getLeftFragment() instanceof SharedListFragment) &&
+                !(((FileDisplayActivity) this).getLeftFragment() instanceof GroupfolderListFragment) &&
                 !(((FileDisplayActivity) this).getLeftFragment() instanceof PreviewTextStringFragment)) {
                 showFiles(false);
                 ((FileDisplayActivity) this).browseToRoot();
@@ -465,6 +467,13 @@ public abstract class DrawerActivity extends ToolbarActivity
             startSharedSearch(menuItem);
         } else if (itemId == R.id.nav_recently_modified) {
             startRecentlyModifiedSearch(menuItem);
+        } else if (itemId == R.id.nav_groupfolders) {
+            MainApp.showOnlyFilesOnDevice(false);
+            Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.setAction(FileDisplayActivity.LIST_GROUPFOLDERS);
+            intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
+            startActivity(intent);
         } else {
             if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK &&
                 menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) {

+ 7 - 1
app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -99,13 +99,13 @@ import com.owncloud.android.ui.asynctasks.FetchRemoteFileTask;
 import com.owncloud.android.ui.dialog.SendShareDialog;
 import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
 import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment;
-import com.owncloud.android.ui.events.ChangeMenuEvent;
 import com.owncloud.android.ui.events.SearchEvent;
 import com.owncloud.android.ui.events.SyncEventFinished;
 import com.owncloud.android.ui.events.TokenPushEvent;
 import com.owncloud.android.ui.fragment.FileDetailFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.GalleryFragment;
+import com.owncloud.android.ui.fragment.GroupfolderListFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.fragment.SearchType;
 import com.owncloud.android.ui.fragment.SharedListFragment;
@@ -164,6 +164,7 @@ public class FileDisplayActivity extends FileActivity
 
     public static final String RESTART = "RESTART";
     public static final String ALL_FILES = "ALL_FILES";
+    public static final String LIST_GROUPFOLDERS = "LIST_GROUPFOLDERS";
     public static final String PHOTO_SEARCH = "PHOTO_SEARCH";
     public static final int SINGLE_USER_SIZE = 1;
     public static final String OPEN_FILE = "NC_OPEN_FILE";
@@ -551,6 +552,11 @@ public class FileDisplayActivity extends FileActivity
                 setLeftFragment(new OCFileListFragment());
                 getSupportFragmentManager().executePendingTransactions();
                 browseToRoot();
+            } else if (LIST_GROUPFOLDERS.equals(intent.getAction())) {
+                Log_OC.d(this, "Switch to list groupfolders fragment");
+
+                setLeftFragment(new GroupfolderListFragment());
+                getSupportFragmentManager().executePendingTransactions();
             }
     }
 

+ 90 - 0
app/src/main/java/com/owncloud/android/ui/adapter/GroupfolderListAdapter.kt

@@ -0,0 +1,90 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2023 Tobias Kaminsky
+ * Copyright (C) 2023 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.recyclerview.widget.RecyclerView
+import com.nextcloud.android.lib.resources.groupfolders.Groupfolder
+import com.owncloud.android.R
+import com.owncloud.android.databinding.ListItemBinding
+import com.owncloud.android.ui.interfaces.GroupfolderListInterface
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import java.io.File
+
+class GroupfolderListAdapter(
+    val context: Context,
+    val viewThemeUtils: ViewThemeUtils,
+    val groupfolderListInterface: GroupfolderListInterface
+) :
+    RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+    lateinit var list: List<Groupfolder>
+
+    private val folderIcon = viewThemeUtils.platform.tintPrimaryDrawable(
+        context,
+        AppCompatResources.getDrawable(
+            context,
+            R.drawable.folder_shared_users
+        )
+    )
+
+    fun setData(result: Map<String, Groupfolder>) {
+        list = result.values.sortedBy { it.mountPoint }
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        return OCFileListItemViewHolder(
+            ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        )
+    }
+
+    override fun getItemCount(): Int {
+        return list.size
+    }
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        val groupfolder = list[position]
+        val listHolder = holder as OCFileListItemViewHolder
+
+        val file = File("/" + groupfolder.mountPoint)
+
+        listHolder.apply {
+            fileName.text = file.name
+            fileSize.text = file.parentFile?.path ?: "/"
+            fileSizeSeparator.visibility = View.GONE
+            lastModification.visibility = View.GONE
+            checkbox.visibility = View.GONE
+            overflowMenu.visibility = View.GONE
+            shared.visibility = View.GONE
+            localFileIndicator.visibility = View.GONE
+            favorite.visibility = View.GONE
+
+            thumbnail.setImageDrawable(folderIcon)
+
+            itemLayout.setOnClickListener { groupfolderListInterface.onFolderClick(groupfolder.mountPoint) }
+        }
+    }
+}

+ 72 - 0
app/src/main/java/com/owncloud/android/ui/asynctasks/GroupfoldersSearchTask.kt

@@ -0,0 +1,72 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2023 Tobias Kaminsky
+ * Copyright (C) 2023 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.asynctasks
+
+import android.os.AsyncTask
+import com.nextcloud.android.lib.resources.groupfolders.GetGroupfoldersRemoteOperation
+import com.nextcloud.android.lib.resources.groupfolders.Groupfolder
+import com.nextcloud.client.account.User
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.ui.fragment.GroupfolderListFragment
+import java.lang.ref.WeakReference
+
+class GroupfoldersSearchTask(
+    fragment: GroupfolderListFragment,
+    private val user: User,
+    storageManager: FileDataStorageManager
+) : AsyncTask<Void, Void, Map<String, Groupfolder>>() {
+    private val fragmentWeakReference: WeakReference<GroupfolderListFragment?>
+    private val storageManager: FileDataStorageManager
+
+    init {
+        fragmentWeakReference = WeakReference(fragment)
+        this.storageManager = storageManager
+    }
+
+    override fun doInBackground(vararg voids: Void): Map<String, Groupfolder> {
+        if (fragmentWeakReference.get() == null) {
+            return HashMap()
+        }
+        val fragment = fragmentWeakReference.get()
+        return if (isCancelled) {
+            HashMap()
+        } else {
+            val searchRemoteOperation = GetGroupfoldersRemoteOperation()
+            if (fragment?.context != null) {
+                val result = searchRemoteOperation.executeNextcloudClient(
+                    user,
+                    fragment.requireContext()
+                )
+                if (result.isSuccess) {
+                    result.resultData
+                } else {
+                    HashMap()
+                }
+            } else {
+                HashMap()
+            }
+        }
+    }
+
+    override fun onPostExecute(result: Map<String, Groupfolder>) {
+        fragmentWeakReference.get()?.setData(result)
+    }
+}

+ 193 - 0
app/src/main/java/com/owncloud/android/ui/fragment/GroupfolderListFragment.kt

@@ -0,0 +1,193 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2023 Tobias Kaminsky
+ * Copyright (C) 2023 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.fragment
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.os.Handler
+import android.view.View
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import com.nextcloud.android.lib.resources.groupfolders.Groupfolder
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.logger.Logger
+import com.owncloud.android.R
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
+import com.owncloud.android.lib.resources.files.model.RemoteFile
+import com.owncloud.android.ui.activity.FileDisplayActivity
+import com.owncloud.android.ui.adapter.GroupfolderListAdapter
+import com.owncloud.android.ui.asynctasks.GroupfoldersSearchTask
+import com.owncloud.android.ui.interfaces.GroupfolderListInterface
+import com.owncloud.android.utils.DisplayUtils
+import com.owncloud.android.utils.FileStorageUtils
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+/**
+ * A Fragment that lists groupfolders
+ */
+class GroupfolderListFragment : OCFileListFragment(), Injectable, GroupfolderListInterface {
+
+    lateinit var adapter: GroupfolderListAdapter
+
+    @Inject
+    lateinit var logger: Logger
+
+    @Inject
+    lateinit var viewThemeUtils: ViewThemeUtils
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        searchFragment = true
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+
+        currentSearchType = SearchType.GROUPFOLDER
+        menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT
+        requireActivity().invalidateOptionsMenu()
+
+        search()
+    }
+
+    override fun setAdapter(args: Bundle?) {
+        adapter = GroupfolderListAdapter(requireContext(), viewThemeUtils, this)
+        setRecyclerViewAdapter(adapter)
+
+        val layoutManager = GridLayoutManager(context, 1)
+        recyclerView.layoutManager = layoutManager
+    }
+
+    private fun search() {
+        GroupfoldersSearchTask(
+            this,
+            accountManager.user,
+            mContainerActivity.storageManager
+        ).execute()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        Handler().post {
+            if (activity is FileDisplayActivity) {
+                val fileDisplayActivity = activity as FileDisplayActivity
+                fileDisplayActivity.updateActionBarTitleAndHomeButtonByString(
+                    getString(R.string.drawer_item_groupfolders)
+                )
+                fileDisplayActivity.setMainFabVisible(false)
+            }
+        }
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    fun setData(result: Map<String, Groupfolder>) {
+        adapter.setData(result)
+        adapter.notifyDataSetChanged()
+    }
+
+    private suspend fun fetchFileData(partialFile: OCFile): OCFile? {
+        return withContext(Dispatchers.IO) {
+            val user = accountManager.user
+            val fetchResult = ReadFileRemoteOperation(partialFile.remotePath).execute(user, context)
+            if (!fetchResult.isSuccess) {
+                logger.e(SHARED_TAG, "Error fetching file")
+                if (fetchResult.isException) {
+                    logger.e(SHARED_TAG, "exception: ", fetchResult.exception)
+                }
+                null
+            } else {
+                val remoteFile = fetchResult.data[0] as RemoteFile
+                val file = FileStorageUtils.fillOCFile(remoteFile)
+                FileStorageUtils.searchForLocalFileInDefaultPath(file, user.accountName)
+                val savedFile = mContainerActivity.storageManager.saveFileWithParent(file, context)
+                savedFile.apply {
+                    isSharedViaLink = partialFile.isSharedViaLink
+                    isSharedWithSharee = partialFile.isSharedWithSharee
+                    sharees = partialFile.sharees
+                }
+            }
+        }
+    }
+
+    private fun fetchFileAndRun(partialFile: OCFile, block: (file: OCFile) -> Unit) {
+        lifecycleScope.launch {
+            isLoading = true
+            val file = fetchFileData(partialFile)
+            isLoading = false
+            if (file != null) {
+                block(file)
+            } else {
+                DisplayUtils.showSnackMessage(requireActivity(), R.string.error_retrieving_file)
+            }
+        }
+    }
+
+    override fun onShareIconClick(file: OCFile) {
+        fetchFileAndRun(file) { fetched ->
+            super.onShareIconClick(fetched)
+        }
+    }
+
+    override fun showShareDetailView(file: OCFile) {
+        fetchFileAndRun(file) { fetched ->
+            super.showShareDetailView(fetched)
+        }
+    }
+
+    override fun showActivityDetailView(file: OCFile) {
+        fetchFileAndRun(file) { fetched ->
+            super.showActivityDetailView(fetched)
+        }
+    }
+
+    override fun onOverflowIconClicked(file: OCFile, view: View?) {
+        fetchFileAndRun(file) { fetched ->
+            super.onOverflowIconClicked(fetched, view)
+        }
+    }
+
+    override fun onItemClicked(file: OCFile) {
+        fetchFileAndRun(file) { fetched ->
+            super.onItemClicked(fetched)
+        }
+    }
+
+    override fun onLongItemClicked(file: OCFile): Boolean {
+        fetchFileAndRun(file) { fetched ->
+            super.onLongItemClicked(fetched)
+        }
+        return true
+    }
+
+    companion object {
+        private val SHARED_TAG = GroupfolderListFragment::class.java.simpleName
+    }
+
+    override fun onFolderClick(path: String) {
+        Log_OC.d("groupfolder", path)
+    }
+}

+ 7 - 1
app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt

@@ -61,7 +61,13 @@ class OCFileListSearchAsyncTask(
         }
 
         fragment.setTitle()
-        val remoteOperationResult = remoteOperation.execute(currentUser, fragment.context)
+        lateinit var remoteOperationResult: RemoteOperationResult<List<Any>>
+        try {
+            remoteOperationResult = remoteOperation.execute(currentUser, fragment.context)
+        } catch (e: UnsupportedOperationException) {
+            remoteOperationResult = remoteOperation.executeNextcloudClient(currentUser, fragment.requireContext())
+        }
+
         if (remoteOperationResult.hasSuccessfulResult() && !isCancelled && fragment.searchFragment) {
             fragment.searchEvent = event
             if (remoteOperationResult.resultData.isNullOrEmpty()) {

+ 2 - 1
app/src/main/java/com/owncloud/android/ui/fragment/SearchType.kt

@@ -13,5 +13,6 @@ enum class SearchType : Parcelable {
     RECENTLY_MODIFIED_SEARCH,
 
     // not a real filter, but nevertheless
-    SHARED_FILTER
+    SHARED_FILTER,
+    GROUPFOLDER
 }

+ 27 - 0
app/src/main/java/com/owncloud/android/ui/interfaces/GroupfolderListInterface.kt

@@ -0,0 +1,27 @@
+/*
+ *
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2023 Tobias Kaminsky
+ * Copyright (C) 2023 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.interfaces
+
+interface GroupfolderListInterface {
+    fun onFolderClick(path: String)
+}

+ 1 - 1
build.gradle

@@ -8,7 +8,7 @@ buildscript {
         daggerVersion = "2.45"
         markwonVersion = "4.6.2"
         prismVersion = "2.0.0"
-        androidLibraryVersion = "master-SNAPSHOT"
+        androidLibraryVersion = "groupfolders-SNAPSHOT"
         mockitoVersion = "4.11.0"
         mockitoKotlinVersion = "4.1.0"
         mockkVersion = "1.13.3"