Skip to content

Make FTP server a separate module#4602

Draft
TranceLove wants to merge 11 commits intoTeamAmaze:release/4.0from
TranceLove:feature/ftpserver-module
Draft

Make FTP server a separate module#4602
TranceLove wants to merge 11 commits intoTeamAmaze:release/4.0from
TranceLove:feature/ftpserver-module

Conversation

@TranceLove
Copy link
Copy Markdown
Collaborator

@TranceLove TranceLove commented Apr 11, 2026

Description

Make FTP server a separate module, coupled via another server-core module, for facilitating integrations in the future.

Issue tracker

Automatic tests

  • Added test cases

Manual tests

  • Done

Build tasks success

Successfully running following tasks on local:

  • ./gradlew assembledebug
  • ./gradlew spotlessCheck

- Replace EventBus with kotlinx.coroutines
- FtpServerFragment update code to recommended
This enables future integration of other file services more easier.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR modularizes the FTP server feature by extracting it from the app module into a dedicated ftpserver library, coupled through a new server-core abstraction module to support future pluggable server implementations.

Changes:

  • Added new Gradle modules: server-core (server abstractions/registry) and ftpserver (FTP implementation: engine, service/receiver base classes, filesystem adapters, UI, resources).
  • Updated app integration to use the new FTP module (new AppFtpService/AppFtpReceiver, updated fragment/tile/notification to reference FtpPreferences/FtpServerEngine).
  • Added unit tests for FTP custom commands (AVBL/FEAT/PWD) and updated Kotlin stdlib version.

Reviewed changes

Copilot reviewed 62 out of 63 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
settings.gradle Includes the new server-core and ftpserver modules.
server-core/** Introduces server abstractions (types, preferences/notification interfaces, registry, events) and module Gradle config.
ftpserver/** New FTP module: engine/service base, filesystem implementations, UI/resources, and unit tests.
app/** Wires the app to the new FTP module (service/receiver, fragment, tile, notification) and adds a WebDAV certificate callable/test.
gradle/libs.versions.toml Updates Kotlin stdlib version used by the project.
file_operations/src/test/** Adds/updates a (currently empty/commented) test helper class.
app/play/release/output-metadata.json Adds a generated build artifact (should likely not be committed).
plan-modularizeFtpServer.prompt.md Planning documentation for the modularization work.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ftpserver/src/main/AndroidManifest.xml Outdated
Comment thread ftpserver/src/main/res/layout/fragment_ftp.xml
Comment thread ftpserver/build.gradle Outdated
Comment thread app/src/main/java/com/amaze/filemanager/ui/fragments/FtpServerFragment.kt Outdated
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch 3 times, most recently from cbc759a to ed9a33d Compare April 11, 2026 16:18
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch from ed9a33d to 9f9a3f0 Compare April 11, 2026 16:27
SshAuthenticationTaskTest.prepareSshConnectionPool() was storing a raw
SSHClient Mockito mock directly into NetCopyClientConnectionPool.connections
without wrapping it in SSHClientImpl. This caused a ClassCastException when
shutdown() was called in AbstractSftpServerTest.tearDown(), since the lambda
tried to cast the raw SSHClient (which is not a NetCopyClient) to NetCopyClient.
The unhandled exception from the fire-and-forget runInBackground call was then
rethrown as OnErrorNotImplementedException, leaving connections un-cleared.
On the next test's setUp() call, the stale connection from SshAuthenticationTaskTest
was still present in the pool. When validate() found it invalid and tried to
reconnect via createSshClient(url), it required a DB lookup that found no matching
entry (because the SshAuthenticationTaskTest connection used a different URL), causing
FilesOnSshdTest.testListFilesWithSpecialChars to time out waiting for SFTP results.

Fixes:
- Wrap mock SSHClient in SSHClientImpl in prepareSshConnectionPool() so the
  connections map always holds proper NetCopyClient instances
- Add tearDown() to SshAuthenticationTaskTest to reset sshClientFactory and
  clear the connections map after each test, preventing state leakage
- Add sshClientFactory reset in AbstractSftpServerTest.setUp() as an additional
  safeguard against contamination from any preceding test class
- Minor: refactor FilesOnSshdTest to use SAM lambda syntax for OnFileFound
  callbacks; extend atMost timeout to 120 seconds for testListFilesWithSpecialChars
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch from 9d063ea to f49db29 Compare April 12, 2026 03:36
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch 3 times, most recently from 1abd5de to 640126d Compare April 14, 2026 23:19
To complete the registry base approach
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch from 640126d to 502d11a Compare April 14, 2026 23:21
@TranceLove TranceLove marked this pull request as ready for review April 14, 2026 23:23
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch from a60a1de to 94a6a98 Compare April 16, 2026 14:55
@Bambooin
Copy link
Copy Markdown
Contributor

Verified in Android 16 with daily FTP usage.

@EmmanuelMess EmmanuelMess self-requested a review April 18, 2026 16:04
Copy link
Copy Markdown
Member

@EmmanuelMess EmmanuelMess left a comment

Choose a reason for hiding this comment

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

I still need to check some files and all the tests.
Also, I am not quite understanding the separation of concerns between app, ftpserver and server-core or if the server-core functionality is actually completely already in use.

/**
* [TypeConverter] between [JSONObject] as object and String representation.
*/
object JsonTypeConverter {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems reduntant?

/**
* Get the FTP service class to start/stop
*/
abstract fun getFtpServiceClass(): Class<*>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be out FtpServerService.

context: Context,
intent: Intent,
) {
logger.debug("Received: ${intent.action}")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove all debug logs

*/
class AppFtpService : FtpServerService() {
private val serverNotification
get() = ServerRegistry.getProvider(ServerType.FTP)!!.getNotification()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please don't use !! in new code.

return getString(R.string.ftp_command_FEAT)
}

companion object {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Companion object should be on top.

*
* Each server module should implement this to provide its components.
*/
interface ServerProvider {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should probably be in another file.

*
* Each server module should implement this to provide its components.
*/
interface ServerProvider {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very similar to FileServer (see comment there).

* Each server implementation should provide its own implementation of this interface
* to handle server lifecycle and configuration.
*/
interface FileServer {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very similar to ServerProvider, not sure what "server lifecycle and configuration." means in this case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be in some llm specific folder somewhere. See https://github.com/EmmanuelMess/Multistep-DEVS-Simulator .claude and spec files folders for ideas.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also its current structure might not be useful, useful information would be:

  • restrictions applied to the llm in code creation
  • specfic set of specifications passed, initially and during refinement. Including UI/UX design files and diagrams
  • a review of code of llm, and changed that were manually applied later

│ ├── ServerPreferences.kt
│ └── ServerRegistry.kt

ftpserver/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If possible this is better as a UML diagram of classes and such, not a semi formatted list. See https://github.com/EmmanuelMess/Multistep-DEVS-Simulator/blob/master/design/class%20diagrams/class%20diagram.svg for an example.

@EmmanuelMess EmmanuelMess added the PR-Requested-Changes this PR is awaiting an update from the author label Apr 18, 2026
@EmmanuelMess EmmanuelMess marked this pull request as draft April 18, 2026 18:05
To prompt users to disable battery optimization for better network I/O performance when running FTP server.

Fixes TeamAmaze#4603
@TranceLove TranceLove force-pushed the feature/ftpserver-module branch from 94a6a98 to b22a5e2 Compare May 1, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR-Requested-Changes this PR is awaiting an update from the author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants