Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gallery/Grid view #534

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from

Conversation

JustFanta01
Copy link
Contributor

Hello guys 👋
This is our proposal for implementing a gallery view [/issues/372]
Me, @WheelyMcBones and @taglioIsCoding have made the following changes.

Referring to our previous PR [/pull/533], in order to create a new Fragment dedicated to the grid visualization, we added the FilesFragmentsInterface, which both the already existing BrowseFilesFragment and the new GalleryFragment now implements. For the visualization we used a GridLayout with custom Item and Adapter. The two Fragments should be interchangeable, but the GalleryFragment is automatically being chosen only for the vault and folder marked as AutoUpload in the settings. Since the automatic upload service is selecting only media files, we can do the assumption that won't be populated with other kind of files so we are not displaying any other information like filename, modification date, size, etc.

JustFanta01 and others added 30 commits April 11, 2024 14:45
Also using cloud.id to be able to distinguish between two files with same path but on different instances of the same cloud
When onBackPressed(), or similar, were triggered the recycle/bind reused a ViewHolder with the selection mode already set
Copy link

coderabbitai bot commented May 8, 2024

Walkthrough

The recent updates focus on enhancing functionality related to image thumbnails within the Cryptomator application. Key changes include the addition of thumbnail properties to files, improved handling of image files across different vault formats, and UI updates in various fragments to support these changes. Thumbnail management options have also been integrated into user settings, allowing for customizable thumbnail generation based on user preferences.

Changes

File Path Change Summary
.../crypto/CryptoFile.kt
.../crypto/CryptoImplVaultFormatPre7.kt
Added thumbnail property and handling logic for image thumbnails in crypto files.
.../crypto/CryptoImplVaultFormat7.kt
.../crypto/CryptoImplDecorator.kt
Enhanced thumbnail caching and added numerous imports for handling files and thumbnails.
.../presentation/.../ActivityComponent.java
.../presentation/.../GalleryFragment.kt
Updated dependency injection and added GalleryFragment for improved UI interactions with thumbnails.
.../presentation/.../BrowseFilesIntent.java
.../presentation/.../VaultListActivity.kt
Enhanced file browsing intents to include vault IDs, improving navigation and file management.
.../presentation/ui/.../BrowseFilesAdapter.kt
.../presentation/ui/.../GalleryFilesAdapter.kt
Updated adapters for file browsing and gallery views to support thumbnail display and interaction.
.../util/SharedPreferencesHandler.kt
.../util/ThumbnailsOption.kt
Added settings and options for managing thumbnail generation preferences.

🐇✨
In the vaults of code, where secrets hide,
New thumbnails spring to life, side by side.
A rabbit hops through digital frames,
Enhancing views, giving files new names.
Celebrate the pixels, small and bright,
For every image now catches the light! 🌟📸
🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Out of diff range and nitpick comments (1)
presentation/src/main/java/org/cryptomator/presentation/ui/fragment/GalleryFragment.kt (1)

54-54: The constant COLUMNS is hard-coded. Consider making this configurable through resources or settings to enhance flexibility.

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 45f7d0e and dbd526b.
Files ignored due to path filters (7)
  • presentation/src/main/res/drawable/rectangle_selection_mode.xml is excluded by !**/*.xml
  • presentation/src/main/res/layout/fragment_gallery_view.xml is excluded by !**/*.xml
  • presentation/src/main/res/layout/item_gallery_files_node.xml is excluded by !**/*.xml
  • presentation/src/main/res/values/arrays.xml is excluded by !**/*.xml
  • presentation/src/main/res/values/colors.xml is excluded by !**/*.xml
  • presentation/src/main/res/values/strings.xml is excluded by !**/*.xml
  • presentation/src/main/res/xml/preferences.xml is excluded by !**/*.xml
Files selected for processing (24)
  • data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoFile.kt (2 hunks)
  • data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplDecorator.kt (7 hunks)
  • data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplVaultFormat7.kt (4 hunks)
  • data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplVaultFormatPre7.kt (2 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java (2 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/intent/BrowseFilesIntent.java (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/model/CloudFileModel.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/model/CloudNodeModel.kt (2 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/presenter/AutoUploadChooseVaultPresenter.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt (4 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/presenter/SharedFilesPresenter.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/activity/BrowseFilesActivity.kt (5 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/activity/VaultListActivity.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt (4 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/adapter/GalleryFilesAdapter.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FileSettingsBottomSheet.kt (2 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/BrowseFilesFragment.kt (11 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/FilesFragmentInterface.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/GalleryFragment.kt (1 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt (6 hunks)
  • presentation/src/main/java/org/cryptomator/presentation/util/DateHelper.kt (1 hunks)
  • util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt (2 hunks)
  • util/src/main/java/org/cryptomator/util/ThumbnailsOption.kt (1 hunks)
  • util/src/main/java/org/cryptomator/util/file/LruFileCacheUtil.kt (2 hunks)
Files not reviewed due to errors (1)
  • data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplVaultFormatPre7.kt (no review received)
Files skipped from review due to trivial changes (1)
  • util/src/main/java/org/cryptomator/util/ThumbnailsOption.kt
Additional comments not posted (47)
presentation/src/main/java/org/cryptomator/presentation/intent/BrowseFilesIntent.java (1)

15-16: Addition of vaultId as an optional parameter is correctly implemented.

presentation/src/main/java/org/cryptomator/presentation/model/CloudFileModel.kt (1)

14-14: Addition of thumbnail property to handle thumbnail images for files is correctly implemented.

data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoFile.kt (1)

16-16: Addition of thumbnail property to store thumbnail files is correctly implemented.

presentation/src/main/java/org/cryptomator/presentation/model/CloudNodeModel.kt (1)

3-3: Verify the necessity of the Bitmap import as there are no uses of it in the provided code changes.

presentation/src/main/java/org/cryptomator/presentation/ui/fragment/FilesFragmentInterface.kt (1)

11-29: Addition of multiple abstract methods to enhance UI interactions and state management is correctly implemented.

presentation/src/main/java/org/cryptomator/presentation/util/DateHelper.kt (1)

12-14: Addition of a lazy-initialized dateFormatter for efficient date formatting is correctly implemented.

presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FileSettingsBottomSheet.kt (1)

37-43: Addition of thumbnail handling in the UI is correctly implemented, enhancing the user experience by displaying visual content.

util/src/main/java/org/cryptomator/util/file/LruFileCacheUtil.kt (1)

34-34: Addition of LOCAL cache option to efficiently manage local resources is correctly implemented.

presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java (1)

79-79: Ensure GalleryFragment is properly set up for dependency injection.

This addition aligns with the existing architecture where fragments are injected to facilitate dependency management. Ensure that all necessary dependencies for GalleryFragment are provided in the corresponding module.

presentation/src/main/java/org/cryptomator/presentation/presenter/AutoUploadChooseVaultPresenter.kt (1)

144-144: Ensure consistent handling of vaultId in intents.

The addition of vaultId to the intent ensures that the correct vault is targeted for operations. This is crucial for the correct functioning of features that operate on specific vaults, such as auto-uploads.

presentation/src/main/java/org/cryptomator/presentation/ui/activity/VaultListActivity.kt (1)

156-156: Ensure correct navigation to vault content.

The method correctly constructs the intent with necessary parameters such as vaultId and decryptedRoot, ensuring that the user is navigated to the appropriate content.

presentation/src/main/java/org/cryptomator/presentation/ui/fragment/BrowseFilesFragment.kt (1)

41-41: Ensure BrowseFilesFragment correctly implements FilesFragmentInterface.

The change to make BrowseFilesFragment open for extension and correctly implement FilesFragmentInterface is appropriate for allowing further customization and functionality extension.

util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt (1)

164-171: Ensure correct handling of thumbnail generation settings.

The addition of handling for thumbnail generation settings in shared preferences is crucial for allowing users to control this feature based on their preferences. The implementation provides multiple options, enhancing user control.

presentation/src/main/java/org/cryptomator/presentation/ui/fragment/GalleryFragment.kt (5)

41-41: Ensure the layout resource R.layout.fragment_gallery_view exists.


44-48: Dependency injection is correctly set up for GalleryFilesAdapter and BrowseFilesPresenter.


56-60: Proper use of Kotlin property syntax for folder with custom getter and setter.


183-191: Usage of @SuppressLint("RestrictedApi") is justified due to the known Android bug. Ensure that this workaround is still necessary with the latest Android SDK updates.


284-287: The method fileCanBeChosen uses a regex pattern to match file names. Ensure that the regex pattern is correctly defined and optimized for performance.

presentation/src/main/java/org/cryptomator/presentation/presenter/SharedFilesPresenter.kt (1)

346-346: Ensure that the vaultId is always correctly passed and handled in intents. This is crucial for maintaining the correct context when navigating between activities.

presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt (4)

31-31: Add import for THUMBNAIL_GENERATION constant.


51-51: Addition of setupThumbnailGeneration method call in onCreatePreferences.


259-259: Set thumbnailGenerationChangeListener in onResume.


342-342: Addition of THUMBNAIL_GENERATION constant.

presentation/src/main/java/org/cryptomator/presentation/ui/adapter/BrowseFilesAdapter.kt (5)

3-3: Import BitmapFactory for handling bitmap operations.


31-32: Import MimeType and MimeTypes for handling MIME type operations.


52-53: Inject SharedPreferencesHandler and MimeTypes into BrowseFilesAdapter.


151-157: Implement thumbnail display for image files in bindNodeImage.

This change allows thumbnails to be displayed for image files, enhancing the user interface by providing visual previews of images.


159-160: Check if a file is of image media type using MIME types.

presentation/src/main/java/org/cryptomator/presentation/ui/adapter/GalleryFilesAdapter.kt (5)

3-8: Import necessary classes for handling views and bitmaps.


30-31: Import MimeType and MimeTypes for handling MIME type operations in the gallery view.


39-46: Constructor setup for GalleryFilesAdapter with necessary dependencies.


157-164: Implement thumbnail display for image files in gallery view.

This change enhances the gallery view by displaying thumbnails for image files, providing a more visually appealing user interface.


166-167: Check if a file is of image media type using MIME types in the gallery view.

presentation/src/main/java/org/cryptomator/presentation/ui/activity/BrowseFilesActivity.kt (5)

52-53: Imports for FilesFragmentInterface and GalleryFragment have been added.

This aligns with the PR's objective to integrate the new GalleryFragment into the existing activity structure.


108-108: The method createFragment has been modified to support dynamic fragment creation based on the folder and settings.

This change is crucial for supporting the dynamic loading of either BrowseFilesFragment or GalleryFragment based on the context, which is a key feature of the new gallery view.


429-439: The method createFragmentFor has been significantly refactored to decide between loading a BrowseFilesFragment or a GalleryFragment based on whether the folder is marked for auto-upload.

This is a smart use of polymorphism to handle different types of fragments dynamically. It directly supports the PR's goal of using GalleryFragment for specific folders.


441-443: The method isAutoUploadFolder checks if the current folder is set for auto-uploads by comparing it against user settings.

This method is essential for determining the context in which the GalleryFragment should be used, ensuring that it is only used for folders intended for media file uploads.


565-565: The method browseFilesFragment has been updated to return a FilesFragmentInterface.

This update is necessary for the abstraction used in this activity, allowing it to interact with both BrowseFilesFragment and GalleryFragment through a common interface.

data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplDecorator.kt (5)

4-8: Imports for handling bitmaps and thumbnails have been added.

These imports are necessary for the new functionality related to thumbnail generation and caching, aligning with the backend changes described in the PR summary.


72-82: Initialization of sharedPreferencesHandler, diskLruCache, and thumbnailExecutorService has been added.

These initializations are crucial for managing preferences and caching mechanisms for thumbnails, which is a significant part of the new functionality introduced in this PR.


Line range hint 372-399: Implementation of thumbnail generation and caching logic within the read method.

This implementation is a key part of the new functionality, ensuring that thumbnails are generated and cached efficiently. It uses a separate thread for thumbnail generation, which is good practice for performance.


434-455: The method startThumbnailGeneratorThread handles the generation of thumbnails in a separate thread.

This method is well-implemented to handle potentially expensive image processing operations in a background thread, thus not blocking the main thread, which is crucial for maintaining application responsiveness.


468-492: The method isGenerateThumbnailsEnabled checks if thumbnail generation is enabled based on user preferences and file type.

This method is essential for ensuring that thumbnails are only generated when appropriate, based on user settings and the media type of the files, which aligns with the user-configurable nature of the new feature.

data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoImplVaultFormat7.kt (2)

168-179: Thumbnail handling logic appears correct, but verify supporting methods.

Verification successful

The methods isImageMediaType and generateCacheKey are correctly defined in CryptoImplDecorator.kt and are inherited by CryptoImplVaultFormat7.kt, which extends this class. The usage of these methods in the thumbnail handling logic of CryptoImplVaultFormat7.kt is therefore appropriate and correct.

  • isImageMediaType is defined in multiple locations, but the relevant definition for this context is in CryptoImplDecorator.kt.
  • generateCacheKey is also defined in CryptoImplDecorator.kt.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the existence and correctness of `isImageMediaType` and `generateCacheKey` methods.

# Test: Search for the methods in the codebase. Expect: Definitions or imports of these methods.
rg --type kotlin $'fun isImageMediaType\\('
rg --type kotlin $'fun generateCacheKey\\('

Length of output: 812


463-471: Cache deletion logic appears correct, but verify supporting methods.

Verification successful

The verification process has confirmed the existence of the methods generateCacheKey and getLruCacheFor within the CryptoImplDecorator.kt file. Both methods are defined as protected, which implies they are accessible within the same module where they are declared, or from subclasses of the class in which they are declared.

Given this information, the original review comment's concern about verifying the existence and correctness of these methods is addressed. The methods exist and are likely correctly implemented, as they are part of the same class or a closely related class (CryptoImplDecorator.kt) that is used in the CryptoImplVaultFormat7.kt file.

Since the methods are found and there are no indications of incorrect implementation from the provided outputs, we can conclude that the cache deletion logic in the delete method, which relies on these methods, is correctly supported.

Final Response:

The methods generateCacheKey and getLruCacheFor have been successfully verified as existing and correctly implemented in the CryptoImplDecorator.kt file. The cache deletion logic in the delete method of CryptoImplVaultFormat7.kt is properly supported by these methods.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the existence and correctness of `generateCacheKey` and `getLruCacheFor` methods.

# Test: Search for the methods in the codebase. Expect: Definitions or imports of these methods.
rg --type kotlin $'fun generateCacheKey\\('
rg --type kotlin $'fun getLruCacheFor\\('

Length of output: 365

presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt (2)

517-517: Ensure the ImagePreviewFilesStore is properly initialized with valid parameters.

Verification successful

The initialization of ImagePreviewFilesStore in BrowseFilesPresenter.kt uses cloudFileNodes as the first parameter and the index of a specific cloudFile within cloudFileNodes as the second parameter. This setup appears to be logically sound as it initializes the store with a list of nodes and a position index, which are typical and valid parameters for such a use case.

Given this information, it can be concluded that ImagePreviewFilesStore is properly initialized with valid parameters in the context of the BrowseFilesPresenter.

  • The cloudFileNodes parameter provides the data context for the image previews.
  • The index parameter specifies the initial position in the list, which is essential for operations that might need to know the current focus within the list.

This setup aligns with common practices in handling collections and indices in programming, especially in a feature that deals with previews where both the collection of items and the current item's index are crucial.

Conclusion: The initialization of ImagePreviewFilesStore in BrowseFilesPresenter.kt is verified to be correct and uses appropriate parameters.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `ImagePreviewFilesStore` is initialized with valid parameters.

# Test: Search for the initialization of `ImagePreviewFilesStore`. Expect: Valid parameters.
rg --type kotlin $'ImagePreviewFilesStore'

Length of output: 1566



Script:

#!/bin/bash
# Description: Extract the specific line from BrowseFilesPresenter.kt where ImagePreviewFilesStore is initialized.

# Extract the line and a few lines around it for context.
rg --type kotlin --context 2 'ImagePreviewFilesStore' presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt

Length of output: 601


1116-1117: Ensure the vaultId is correctly used in the intent builder.

Comment on lines +114 to +117
private val thumbnailGenerationChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
// TODO ...
true
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete implementation of thumbnailGenerationChangeListener.

Please complete the implementation of the thumbnailGenerationChangeListener. This is crucial for handling changes in user preferences regarding thumbnail generation.

Comment on lines +150 to +153
private fun setupThumbnailGeneration() {
val preference = findPreference(THUMBNAIL_GENERATION) as Preference?
// TODO ...
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete setup for thumbnail generation preference.

Please complete the implementation of the setupThumbnailGeneration method. This method should properly configure the preference related to thumbnail generation.

Comment on lines +1102 to +1103
val vauldId = view?.folder?.vault()?.vaultId
val browseFilesIntentBuilder = Intents.browseFilesIntent() //
Copy link

@coderabbitai coderabbitai bot May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the variable name typo: vauldId should be vaultId.

- val vauldId = view?.folder?.vault()?.vaultId
+ val vaultId = view?.folder?.vault()?.vaultId

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
val vauldId = view?.folder?.vault()?.vaultId
val browseFilesIntentBuilder = Intents.browseFilesIntent() //
val vaultId = view?.folder?.vault()?.vaultId
val browseFilesIntentBuilder = Intents.browseFilesIntent() //

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, please change

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SailReal, I've made the necessary change to correct the typo from vauldId to vaultId.

val vaultId = view?.folder?.vault()?.vaultId
val browseFilesIntentBuilder = Intents.browseFilesIntent() //

This should resolve the issue. Let me know if there's anything else you need.


New Learnings Added
User: SailReal
PR: cryptomator/android#534
File: presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt:1102-1103
Timestamp: 2024-05-14T11:00:59.728Z
Learning: In the `BrowseFilesPresenter` class, ensure the variable name `vaultId` is correctly spelled.

Note: Learnings are effective only in the context of specific code segments. To apply general coding guidelines, please configure review instructions.

@Inject
lateinit var browseFilesPresenter: BrowseFilesPresenter

private var navigationMode: ChooseCloudNodeSettings.NavigationMode? = null
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider initializing navigationMode to a default value to ensure it's never null when accessed before being set.

- private var navigationMode: ChooseCloudNodeSettings.NavigationMode? = null
+ private var navigationMode: ChooseCloudNodeSettings.NavigationMode = BROWSE_FILES

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
private var navigationMode: ChooseCloudNodeSettings.NavigationMode? = null
private var navigationMode: ChooseCloudNodeSettings.NavigationMode = BROWSE_FILES

Comment on lines +65 to +93
private val refreshListener = SwipeRefreshLayout.OnRefreshListener { browseFilesPresenter.onRefreshTriggered(folder) }

private val nodeClickListener = object : GalleryFilesAdapter.ItemClickListener {
override fun onFolderClicked(cloudFolderModel: CloudFolderModel) {
browseFilesPresenter.onFolderClicked(cloudFolderModel)
filterText = ""
browseFilesPresenter.invalidateOptionsMenu()
}

override fun onFileClicked(cloudNodeModel: CloudFileModel) {
if (fileCanBeChosen(cloudNodeModel)) {
browseFilesPresenter.onFileChosen(cloudNodeModel)
} else {
browseFilesPresenter.onFileClicked(cloudNodeModel)
}
}

override fun onNodeSettingsClicked(cloudNodeModel: CloudNodeModel<*>) {
browseFilesPresenter.onNodeSettingsClicked(cloudNodeModel)
}

override fun onNodeLongClicked() {
browseFilesPresenter.onSelectionModeActivated()
}

override fun onSelectedNodesChanged(selectedNodes: Int) {
browseFilesPresenter.onSelectedNodesChanged(selectedNodes)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Listeners for UI events in GalleryFragment.kt do not appear to use runOnUiThread for executing UI updates on the main thread. This could potentially lead to thread safety issues when updating the UI from background threads.

  • GalleryFragment.kt: Ensure that all UI updates in listeners are wrapped with runOnUiThread to maintain thread safety.
Analysis chain

Listeners for UI events are well implemented. Ensure that all UI updates are performed on the main thread to avoid potential issues with UI thread safety.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that UI updates in listeners are performed on the main thread.

# Test: Search for UI updates in listeners. Expect: UI updates wrapped in main thread calls.
rg --type kotlin 'runOnUiThread'

Length of output: 157

Comment on lines +98 to +137
override fun setupView() {
setupNavigationMode()

floatingActionButton.setOnClickListener { browseFilesPresenter.onAddContentClicked() }
chooseLocationButton.setOnClickListener { browseFilesPresenter.onFolderChosen(folder) }

swipeRefreshLayout.setColorSchemeColors(ContextCompat.getColor(context(), R.color.colorPrimary))
swipeRefreshLayout.setOnRefreshListener(refreshListener)

cloudNodesAdapter.setCallback(nodeClickListener)
cloudNodesAdapter.setChooseCloudNodeSettings(chooseCloudNodeSettings)
navigationMode?.let { cloudNodesAdapter.updateNavigationMode(it) }


recyclerView.layoutManager = GridLayoutManager(context(), COLUMNS)
recyclerView.adapter = cloudNodesAdapter
recyclerView.setHasFixedSize(true)

val spacing = resources.getDimensionPixelSize(global_padding) / 4

// bottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 88f, resources.displayMetrics).toInt()
recyclerView.setPadding(spacing, spacing, spacing, spacing)
recyclerView.clipToPadding = false
recyclerView.clipChildren = false

recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) {
outRect.set(spacing, spacing, spacing, spacing)
}
})

browseFilesPresenter.onFolderRedisplayed(folder)

when {
!hasCloudNodeSettings() -> setupViewForBrowseFilesMode()
isSelectionMode(FOLDERS_ONLY) -> setupViewForFolderSelection()
isSelectionMode(FILES_ONLY) -> setupViewForFilesSelection()
isNavigationMode(SELECT_ITEMS) -> setupViewForNodeSelectionMode()
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method setupView correctly configures UI components and sets up listeners. However, consider extracting complex conditional logic into separate methods for better readability and maintainability.

- when {
-   !hasCloudNodeSettings() -> setupViewForBrowseFilesMode()
-   isSelectionMode(FOLDERS_ONLY) -> setupViewForFolderSelection()
-   isSelectionMode(FILES_ONLY) -> setupViewForFilesSelection()
-   isNavigationMode(SELECT_ITEMS) -> setupViewForNodeSelectionMode()
- }
+ setupViewBasedOnSettings()

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
override fun setupView() {
setupNavigationMode()
floatingActionButton.setOnClickListener { browseFilesPresenter.onAddContentClicked() }
chooseLocationButton.setOnClickListener { browseFilesPresenter.onFolderChosen(folder) }
swipeRefreshLayout.setColorSchemeColors(ContextCompat.getColor(context(), R.color.colorPrimary))
swipeRefreshLayout.setOnRefreshListener(refreshListener)
cloudNodesAdapter.setCallback(nodeClickListener)
cloudNodesAdapter.setChooseCloudNodeSettings(chooseCloudNodeSettings)
navigationMode?.let { cloudNodesAdapter.updateNavigationMode(it) }
recyclerView.layoutManager = GridLayoutManager(context(), COLUMNS)
recyclerView.adapter = cloudNodesAdapter
recyclerView.setHasFixedSize(true)
val spacing = resources.getDimensionPixelSize(global_padding) / 4
// bottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 88f, resources.displayMetrics).toInt()
recyclerView.setPadding(spacing, spacing, spacing, spacing)
recyclerView.clipToPadding = false
recyclerView.clipChildren = false
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) {
outRect.set(spacing, spacing, spacing, spacing)
}
})
browseFilesPresenter.onFolderRedisplayed(folder)
when {
!hasCloudNodeSettings() -> setupViewForBrowseFilesMode()
isSelectionMode(FOLDERS_ONLY) -> setupViewForFolderSelection()
isSelectionMode(FILES_ONLY) -> setupViewForFilesSelection()
isNavigationMode(SELECT_ITEMS) -> setupViewForNodeSelectionMode()
}
}
override fun setupView() {
setupNavigationMode()
floatingActionButton.setOnClickListener { browseFilesPresenter.onAddContentClicked() }
chooseLocationButton.setOnClickListener { browseFilesPresenter.onFolderChosen(folder) }
swipeRefreshLayout.setColorSchemeColors(ContextCompat.getColor(context(), R.color.colorPrimary))
swipeRefreshLayout.setOnRefreshListener(refreshListener)
cloudNodesAdapter.setCallback(nodeClickListener)
cloudNodesAdapter.setChooseCloudNodeSettings(chooseCloudNodeSettings)
navigationMode?.let { cloudNodesAdapter.updateNavigationMode(it) }
recyclerView.layoutManager = GridLayoutManager(context(), COLUMNS)
recyclerView.adapter = cloudNodesAdapter
recyclerView.setHasFixedSize(true)
val spacing = resources.getDimensionPixelSize(global_padding) / 4
// bottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 88f, resources.displayMetrics).toInt()
recyclerView.setPadding(spacing, spacing, spacing, spacing)
recyclerView.clipToPadding = false
recyclerView.clipChildren = false
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) {
outRect.set(spacing, spacing, spacing, spacing)
}
})
browseFilesPresenter.onFolderRedisplayed(folder)
setupViewBasedOnSettings()
}

Comment on lines +201 to +237
override fun show(nodes: List<CloudNodeModel<*>>?) {
cloudNodesAdapter.clear()
cloudNodesAdapter.addAll(cloudNodesAdapter.filterNodes(nodes, filterText))
updateEmptyFolderHint()
}

override fun showProgress(nodes: List<CloudNodeModel<*>>?, progress: ProgressModel?) {
nodes?.forEach { node ->
showProgress(node, progress)
}
}

override fun showProgress(node: CloudNodeModel<*>?, progress: ProgressModel?) {
val viewHolder = viewHolderFor(node)
if (viewHolder.isPresent) {
viewHolder.get().showProgress(progress)
} else {
node?.progress = progress
node?.let { addOrUpdate(it) }
}
}

override fun hideProgress(nodes: List<CloudNodeModel<*>>?) {
nodes?.forEach { node ->
hideProgress(node)
}
}

override fun hideProgress(cloudNode: CloudNodeModel<*>?) {
val viewHolder = viewHolderFor(cloudNode)
if (viewHolder.isPresent) {
viewHolder.get().hideProgress()
} else {
cloudNode?.progress = ProgressModel.COMPLETED
cloudNode?.let { addOrUpdate(it) }
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The search for synchronization mechanisms around ProgressModel updates in the Kotlin files of the project revealed that the term "synchronized" is used in two different files (Presenter.kt and LogRotator.kt), but neither of these usages are related to the ProgressModel or its updates. This indicates that there are no explicit synchronization mechanisms in place for the updates to ProgressModel as used in the GalleryFragment.kt.

Given this information, it appears that the updates to ProgressModel are not explicitly thread-safe, which could potentially lead to issues if these objects are accessed from multiple threads concurrently.

Final Response:

The verification process did not find any synchronization mechanisms around updates to ProgressModel in the provided context. If ProgressModel is accessed from multiple threads, this might lead to thread safety issues. It is recommended to implement appropriate synchronization if concurrent access is expected.

  • File: presentation/src/main/java/org/cryptomator/presentation/ui/fragment/GalleryFragment.kt
  • Concern: Updates to ProgressModel are not thread-safe.
Analysis chain

The methods for showing and hiding progress are implemented correctly. Ensure that the ProgressModel updates are thread-safe if accessed from multiple threads.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify thread safety of `ProgressModel` updates.

# Test: Search for synchronization mechanisms around `ProgressModel` updates. Expect: Use of locks or synchronized blocks.
rg --type kotlin 'synchronized'

Length of output: 295

Copy link
Member

@SailReal SailReal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for this contribution ❤️!

Besides the comments in the code, here are some general thoughts:

  • We should remove the automatic detection of where to show the GridView and where not, and instead add a menu item to toggle the layout. Then pass this value to the next Fragment so that we only switch back when the user
    • toggles the menu
    • destroys the BrowseFilesActivity in which this mode is stored.
  • To avoid the thumbnail "jumping around" to show progress when clicked, we can show the progress section at the bottom, but in the thumbnail.
  • We should always show the name of the file/folder below it. We shrink it to the width of the icon, but we need it. Otherwise we can't know the difference between two folders or files that aren't images.
  • We should also decrease the size of the thumbnail and add a next to the node to interact with it (the > from the normal view)
  • I'm still thinking whether we should add some overhead and reuse more code or accept that BrowseFilesAdapter/GalleryFilesAdapter, BrowseFilesFragment/GalleryFragment and BrowseFilesAdapter/GalleryFilesAdapter do more or less the same thing. If we accept it, I think we should move a lot of code into a base class. Once that's decided, I'll have a closer look at those classes
  • We will provide some high resolution placeholders for the file icons.
  • The code is formatted almost everywhere, but some files are not completely 😅
  • Always use brackets in if statements

Comment on lines +1102 to +1103
val vauldId = view?.folder?.vault()?.vaultId
val browseFilesIntentBuilder = Intents.browseFilesIntent() //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, please change

Comment on lines +126 to +128
// descritto da R.layout.item_gallery_files_node
// sono state importate le sue componenti
// kotlinx.android.synthetic.main.item_gallery_files_node.view.galleryCloudNodeImage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translate it if we need this comment, otherwise remove it

Comment on lines +150 to +153
// durante il rebind sta probabilmente riutilizzando lo stesso oggetto grafico (itemView)
// di un precente cloudNode che era stato selezionato
// e.g. se l'item 22 viene selezionato, cambia il foreground e quando viene
// ribindato con l'indice 0 rimane il foregound sbagliato!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translate it if we need this comment, otherwise remove it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants