Skip to content

feat(scan): New Document Scanning, powered by FairScan - #17710

Draft
PhilLab wants to merge 10 commits into
masterfrom
ph/fairscan_integration
Draft

PhilLab wants to merge 10 commits into
masterfrom
ph/fairscan_integration

Conversation

@PhilLab

@PhilLab PhilLab commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Important note: this is not yet working out-of-the-box, because FairScan isn't yet available as library.
Eventually, it will be available upstream, but for testing, you'll need to:

  1. Checkout the feature branch on the fork git clone https://github.com/phillab/fairscan -b ph/offer_as_library
  2. Build and publish it to local: cd fairscan && ./gradlew publishToMavenLocal

Or you just download the QA apk from the comment further below.

TODO:

  • Add related tickets and screenshots
  • Explain Architecture and intended behavior (with and without FairScan installed) and impact on apk size
  • Small UI cleanup ("about" screen: wrong title. Remove email option)
  • Merge FairScan upstream, proper publish of dependency
  • Dependency cleanup possible? (Probably hard)
  • Further code/commit cleanup and review
  • (Potential follow-up): OpenCV minification
  • (Potential follow-up): Rename file after capturing
  • (Potential follow-up): OCR?

🖼️ Screenshots

🏚️ Before 🏡 After
B A

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI (N/A)

@PhilLab
PhilLab requested a balanced review from Copilot September 24, 2026 22:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

This PR replaces the previous in-app document scanning implementation (DocumentScan/AppScan) with the FairScan library, removing the legacy scanning UI/worker pipeline and wiring scanning via FairScan intents. It also adds CI plumbing to build FairScan from source and publish it to mavenLocal() until an upstream artifact is available.

Changes:

  • Swap :appscan / DocumentScan integration for FairScan dependency resolution (currently via mavenLocal() + composite action).
  • Remove legacy DocumentScan activities, ViewModel, workers, DI wiring, and related resources/strings.
  • Harden URI “sensitive source” detection and add unit tests to cover FairScan-embedded authorities.
File Description
settings.gradle.kts Adds mavenLocal() (group-filtered) for FairScan and removes :appscan from the build.
gradle/​libs.versions.toml Drops DocumentScanner coordinates; introduces FairScan version/catalog entry.
gradle.properties Disables AGP unique manifest package enforcement to work around LiteRT packaging conflict.
appscan/​src/​main/​java/​com/​nextcloud/​appscan/​ScanPageContract.kt Removes AppScan contract (module elimination).
appscan/​src/​main/​java/​com/​nextcloud/​appscan/​AppScanActivity.kt Removes AppScan activity (module elimination).
appscan/​src/​main/​AndroidManifest.xml Removes appscan manifest (module elimination).
appscan/​build.gradle.kts Removes appscan module Gradle build file.
app/​src/​test/​java/​com/​owncloud/​android/​ui/​helpers/​UriUploaderTest.kt Adds unit tests for updated sensitive-URI logic (including FairScan embedded authority).
app/​src/​test/​java/​com/​nextcloud/​client/​jobs/​BackgroundJobFactoryTest.kt Updates factory test after removing PDF generation use case.
app/​src/​main/​res/​values/​strings.xml Removes strings only used by the legacy document scan UI/export flow.
app/​src/​main/​res/​menu/​activity_document_scan.xml Removes legacy document scan menu.
app/​src/​main/​res/​layout/​document_page_item.xml Removes legacy scan page item layout.
app/​src/​main/​res/​layout/​dialog_scan_export_type.xml Removes legacy export type dialog layout.
app/​src/​main/​res/​layout/​activity_document_scan.xml Removes legacy document scan activity layout.
app/​src/​main/​java/​com/​owncloud/​android/​ui/​helpers/​UriUploader.kt Replaces substring check with authority-based checks for content:// URIs; exposes for testing.
app/​src/​main/​java/​com/​owncloud/​android/​ui/​fragment/​OCFileListFragment.java Replaces internal DocumentScan launch with FairScan intent fallback logic.
app/​src/​main/​java/​com/​owncloud/​android/​ui/​fragment/​OCFileListBottomSheetDialog.kt Simplifies scan action wiring to always use “scan from app” path.
app/​src/​main/​java/​com/​owncloud/​android/​ui/​fragment/​OCFileListBottomSheetActions.kt Removes legacy scan methods and availability flag; updates docstring.
app/​src/​main/​java/​com/​nextcloud/​client/​jobs/​BackgroundJobManagerImpl.kt Removes PDF-generation background work API/implementation.
app/​src/​main/​java/​com/​nextcloud/​client/​jobs/​BackgroundJobManager.kt Removes startPdfGenerateAndUploadWork from the interface.
app/​src/​main/​java/​com/​nextcloud/​client/​jobs/​BackgroundJobFactory.kt Removes PDF generation worker creation path and dependency.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​GeneratePdfFromImagesWork.kt Removes legacy WorkManager PDF generation worker.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​GeneratePDFUseCase.kt Removes legacy “images to PDF” use case.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​DocumentScanViewModel.kt Removes legacy scan/export UI state machine.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​DocumentScanActivity.kt Removes legacy document scan activity.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​DocumentPageListAdapter.kt Removes legacy scan page list adapter.
app/​src/​main/​java/​com/​nextcloud/​client/​documentscan/​AppScanOptionalFeature.kt Removes optional-feature wrapper (no longer needed).
app/​src/​main/​java/​com/​nextcloud/​client/​di/​ViewModelModule.kt Removes DocumentScanViewModel binding.
app/​src/​main/​java/​com/​nextcloud/​client/​di/​VariantModule.kt Removes reflection-based variant module for :appscan.
app/​src/​main/​java/​com/​nextcloud/​client/​di/​ComponentsModule.java Removes Dagger injector for DocumentScanActivity.
app/​src/​main/​java/​com/​nextcloud/​client/​di/​AppComponent.kt Removes VariantModule from component modules list.
app/​src/​main/​AndroidManifest.xml Removes DocumentScanActivity declaration; adjusts permission merge behavior.
app/​src/​androidTestGplay/​java/​com/​nextcloud/​client/​di/​VariantModuleTest.kt Removes tests covering now-deleted VariantModule/AppScanOptionalFeature behavior.
app/​src/​androidTestGeneric/​java/​com/​nextcloud/​client/​di/​VariantModuleTest.kt Removes tests covering now-deleted VariantModule/AppScanOptionalFeature behavior.
app/​src/​androidTest/​java/​com/​owncloud/​android/​ui/​dialog/​DialogFragmentIT.kt Updates test wiring after removing scan actions/optional feature injection.
app/​src/​androidTest/​java/​com/​nextcloud/​client/​documentscan/​GeneratePDFUseCaseTest.kt Removes tests for deleted PDF generation use case.
app/​build.gradle.kts Replaces variant-scoped :appscan dependency with FairScan library dependency.
.github/​workflows/​unit-tests.yml Publishes FairScan to mavenLocal() before running unit tests.
.github/​workflows/​qa.yml Publishes FairScan to mavenLocal() before QA build.
.github/​workflows/​garm.yml Publishes FairScan to mavenLocal() before emulator/KVM steps.
.github/​workflows/​detectWrongSettings.yml Publishes FairScan to mavenLocal() before running “Detect SNAPSHOT”.
.github/​workflows/​check.yml Publishes FairScan to mavenLocal() before running matrix checks.
.github/​workflows/​assembleFlavors.yml Publishes FairScan to mavenLocal() before assembling flavors.
.github/​workflows/​analysis.yml Publishes FairScan to mavenLocal() before analysis job steps.
.github/​actions/​publish-fairscan-to-maven-local/​action.yml Adds composite action to checkout FairScan and publishToMavenLocal for CI consumption.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +20 to +24
default: "phillab/FairScan"
ref:
description: "FairScan ref to build. See the 'repository' input comment: temporary."
required: false
default: "ph/offer_as_library"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks. As it is a draft, this is deliberate for now. Will be adjusted before merging.

Comment thread app/build.gradle.kts
Dead code once nothing calls startPdfGenerateAndUploadWork(): this job
only existed to combine DocumentScanActivity's scanned pages into one
PDF for upload, and that flow is being replaced by an embedded
FairScan, which produces the finished PDF itself.

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
DocumentScanActivity's own page-collection UI (backed by the Hazzatur
Document-Scanning-Android-SDK via AppScanOptionalFeature) is being
replaced by an embedded FairScan, which handles its own multi-page
capture and PDF export. Removing it here together with its only
caller keeps the tree buildable; the cross-app "launch FairScan if
installed separately" fallback in OCFileListBottomSheetDialog is now
the sole scan path until the embedded replacement lands.

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
Nothing references AppScanOptionalFeature/VariantModule's reflection
lookup of com.nextcloud.appscan.ScanPageContract anymore, so the
Hazzatur Document-Scanning-Android-SDK dependency and the :appscan
module it lived in go too. The JitPack repo in settings.gradle.kts
stays - other dependencies still resolve through it.

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
FairScan will replace the old scanner component with
state-of-the-art CV and UI.

This is WIP, because FairScan is (not yet) available
on a remote registry. So it is only included via local
maven, but nothing actually ensures that the build
machine actually has FairScan build locally, via
  ./gradlew publishToMavenLocal

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
…talled

A separately installed FairScan wins over the built-in scanner, so users
can choose to install (a newer version of) FairScan themselves.

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
UriUploader.isSensitiveUri() rejected any URI containing this app's own
package name, to prevent leaking private app data through the
upload-from-app flow. Once FairScan's FileProvider runs embedded, its
authority is built from this app's own applicationId, so the scanned
PDF's URI matched that check and was silently dropped.

Check content:// URIs against this app's own known provider authorities
instead of a package-name substring, so an embedded library's provider
isn't mistaken for one of this app's own. file:// URIs keep the
substring check, since they have no authority to compare.

Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: Philipp Hasper <vcs@hasper.info>
Ran `./gradlew --write-verification-metadata sha256 -Dorg.gradle.dependency.verification=lenient help && ./gradlew --export-keys`
@github-actions

Copy link
Copy Markdown

📱 QA build

Download app-qa-debug.apk
QR code Open the QR code for this download
Commit 79e48fa
Version 17710
Available until 7 days after this build

The QA build installs alongside a released Nextcloud app, so you can keep
using your existing install while testing.

Downloading the file requires a GitHub account, so open this link on the
device you want to test on, or transfer the APK to it.

@github-actions

Copy link
Copy Markdown

Codacy

SpotBugs

CategoryBaseNew
Bad practice3232
Correctness5858
Dodgy code185185
Experimental11
Malicious code vulnerability22
Multithreaded correctness2626
Performance3737
Security1717
Total358358

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

return uploadPath + displayName
}

private fun isSensitiveUri(uri: Uri): Boolean = uri.toString().contains(activity.packageName)

@PhilLab PhilLab Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TODO: This change actually points towards a bad data flow. I must rather revert it and solve the file handover differently

@PhilLab PhilLab added enhancement feature: document scanning Capturing documents via camera and processing, including page detection, multi-page PDFs, etc. labels Sep 25, 2026
@PhilLab PhilLab changed the title feat(scan): Replace Documentscan with FairScan feat(scan): New Document Scanning, powered by FairScan Sep 25, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted enhancement feature: document scanning Capturing documents via camera and processing, including page detection, multi-page PDFs, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants