Commit Graph
138 Commits
Author SHA1 Message Date
fufesou d0a1af8c51 feat(android): transfer files directly through SAF folders (#16644)
* feat(android): transfer files directly through SAF folders

Let Android users choose folders in the existing file-transfer browser
and stream documents through provider descriptors without workspace copies.
Keep local browsing separate from host sharing, and restrict new peer
document requests to the current shared folder.

Preserve traversal checks, overwrite protection, cancellation, empty files,
and provider errors through completion. Run Android provider IO on blocking
workers and reuse the native directory-listing bridge. Retain the existing
workspace import/export flow and non-Android file-transfer behavior.

Validation: all 11 base tests pass, including shared-root confinement,
document IO, cancellation, provider failures, and resume cleanup. macOS Rust
checking and isolated Kotlin compilation pass. Flutter analysis reports no
errors, with existing warnings. Full Android builds and device tests remain
unperformed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): group storage locations in the file menu

Replace the standalone folder icon and ambiguous RustDesk entry with adjacent RustDesk storage and Other folders actions in the existing menu. Reuse the current storage handlers and register the two localized labels.

Validation: targeted Flutter analysis reports no errors and six existing diagnostics. Translation consistency and git diff checks pass. Android device interaction was not tested.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): keep batch deletion running across refreshes

Snapshot selected files before awaiting deletion confirmations so directory refreshes cannot invalidate the batch iterator. Defer refreshes for awaited file deletions until the batch finishes, while retaining refreshes for late completions.

Retain one regression test for completing all three confirmations after selection reconciliation. All nine file-model tests pass; focused analysis reports no errors and one existing informational diagnostic. Android visual behavior was not tested on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(android): narrow SAF scope and move folder selection to settings

Move shared-folder selection from Share screen permissions to Settings > Transfer file, preserving live connection locking and managed settings visibility.

Trim optional SAF resume and thread-identity assertions while retaining transfer and authorization regressions. Split the independent batch-deletion fix into commit 06313a9aa on fix/file-transfer-batch-deletion.

Validation: 19 base unit tests and 11 Flutter file-model/download-path tests passed. Focused Flutter analysis reports only existing informational diagnostics.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up failed SAF transfers and reset navigation

Cancel pending SAF writes when the client receives a transfer error or the connection manager handles WriteError. Log cleanup failures without replacing the original transfer error or discarding data retained after publication fails.

Clear local navigation history after successfully selecting a storage root, so Back does not attempt to return to the previous workspace.

Validation: 19 base unit tests and 11 Flutter file-model/download-path tests passed. macOS cargo check passed; focused Flutter analysis reports only existing diagnostics. Android build and device validation were not performed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): guard SAF overwrites and clean up interrupted receives

Require matching per-file confirmation before SAF downloads replace an
existing destination, including transfers from peers without overwrite
detection. Enforce the policy before creating a temporary document.

Cancel unfinished SAF receives when the Android client session exits.
Delete newly created temporary documents if opening them fails, preserving
the original error and reporting cleanup failures. Keep publication-failure
recovery and non-Android transfer paths unchanged.

Validation: 19 base unit tests and macOS cargo check --lib --features flutter
passed. The document storage Kotlin modules compiled against Android SDK 35.
Full Android builds and device validation were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up SAF uploads on host disconnect

Cancel pending SAF receives after the Android connection manager removes
a closed connection, including receiver EOF. Log cleanup failures without
preventing cleanup of other jobs. Preserve publication-failure recovery.

Validation: 19 base unit tests and macOS cargo check --lib --features flutter
passed. Android host disconnect behavior still requires device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore storage root after failed folder selection

Restore the previous local home when opening a selected SAF folder or the
RustDesk workspace fails or throws. Preserve navigation history on failure
and retain existing error reporting.

Keep the home assignment before the asynchronous read so superseded read
completions cannot overwrite a newer storage selection.

Validation: 11 existing file-model/download-path Flutter tests passed.
Focused analysis reports no errors and six pre-existing diagnostics.
Changed-method formatting and whitespace checks passed. Android device
validation of overlapping storage switches remains pending.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): enforce shared storage and protect SAF overwrites

Authorize peer filesystem requests against the currently shared root so
remembered workspace paths cannot bypass a selected SAF folder. Offer
text-only RustDesk storage and Other folders choices under Change path,
including an explicit switch back to the application workspace.

Reject SAF host overwrites from peers without overwrite-detection support,
while preserving modern senders' existing confirmation flow. Cover legacy
creation, rejected replacement, and modern replacement in the Rust fixture.

Validation: 19 base tests, 11 Flutter tests, focused Dart analysis, Android
Rust/APK builds, and whitespace checks passed. Device checks verified
storage switching, cancellation, old-path rejection, and Android-initiated
three-file overwrite/Skip batches against macOS. The separate legacy-host
overwrite guard was verified by Rust tests and was not in the device APK.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject partial SAF listings and restore folder roots

Reject provider cursors reporting loading or errors before using their
cached rows as a complete directory listing. Propagate the error through
the existing folder-transfer initialization and browsing paths.

After restoring a local SAF directory, derive its navigation root from
the displayed document path so Home and breadcrumbs use the selected
tree independently of the host's shared-folder setting.

Validation: Kotlin storage modules compile; an Android runtime cursor
check covers complete, loading, and error results. Eleven existing
Flutter tests and three temporary reconnect checks pass. Focused Dart
analysis reports only the existing setter lint; diff checks pass.
Cloud-provider transfers and navigation in a rebuilt APK were not tested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): discard pending SAF writes after block errors

Cancel pending document receives when a data-block write fails, removing
temporary data before cancellation or disconnect. Preserve the original
write error and throttle cleanup-error logging. Keep publication-failure
retention unchanged.

Extend the existing provider tests with a real descriptor I/O failure
and a cleanup-failure case.

Validation:
- Regression failed before the fix.
- cargo test --offline -p base --lib: 19 passed within 60 seconds.
- Rustfmt and git diff --check passed.
- No Android build or on-device storage-exhaustion test was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): guide first-time file transfer folder setup

Prompt users to choose the folder remote devices may access when first
opening File transfer, manually starting the service, or enabling file
transfer. Offer RustDesk storage and the existing SAF picker, and explain
where to change the shared folder later in Settings.

Remember successful setup or explicit Skip, while allowing canceled setup
to be retried. Honor existing settings and permission restrictions. Use
the native translation placeholder convention for the settings path.

Validation:
- Two onboarding widget tests pass, including the breadcrumb regression.
- Flutter analysis and Dart formatting checks pass for the new files.
- Staged diff whitespace checks pass.

Android system-picker and service integration were not tested on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): group file transfer folder choices

Replace plain list rows with an indented group of full-width outlined
buttons. Tighten spacing while preserving the standard Android touch
target and allowing labels to wrap.

Validation: both existing onboarding widget tests, scoped Flutter
analysis, Dart formatting, and diff whitespace checks pass.
Android visual validation was not performed on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up SAF finalization and guard source resume

Discard temporary documents when flushing, syncing, or closing a receive
fails before publication. Attempt discard even when cancellation cannot
close the descriptor, and retain both errors when cleanup also fails.
Keep recovery documents after publication failures.

Reject nonzero source offsets when the SAF modification time is unknown,
so same-size changes cannot combine with a previous partial download.
Report that the transfer must restart; fresh sends remain supported.

Validation: all 19 base library tests pass, including finalization and
cancellation cleanup, combined provider errors, unknown-timestamp resume,
fresh sends, and failed-publication retention. Tests used a 60-second
limit. Rustfmt and diff whitespace checks pass. Android device/provider
failure testing and a rebuilt APK were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): restore reliable batch deletion

Snapshot selected entries before awaiting deletion confirmations so
refreshes cannot invalidate the batch iterator. Defer refreshes for
awaited file deletions until the batch finishes, while preserving
refreshes for late completions.

Completing the batch lets the existing mobile delete handler clear the
selection and exit selection mode. Restore the previously split fix on
the SAF branch with one five-file regression test.

Validation: all nine file-model tests pass. The regression reproduced
Concurrent modification during iteration before the fix. Focused Flutter
analysis has no errors or warnings, with one existing setter-style
informational diagnostic. Formatting and diff checks pass. Android device
deletion and selection-mode rendering were not tested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): store logs in app media storage

Write release logs to Android/media/com.carriez.flutter_hbb/RustDesk,
using Android's package-specific media directory and the existing logger.
Include this directory in Export Logs alongside legacy logs and recordings,
and restrict the added export access to the canonical log directory.

Suggest Android/media for the first SAF folder selection on API 26+.

Validation: Android ARM64 release cargo check and Kotlin compilation passed.
Focused Flutter analysis found no new diagnostics; git diff --check passed.
Full APK linking is blocked by missing libvpx.a in the build environment.
On-device log writing, export, and initial picker location remain unverified.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): keep logs in a user-authorized shared folder

Write release logs through SAF in Android/media/RustDesk instead of the
package-owned media directory. Require the independent log-folder grant
before starting the service, and include these logs in Export Logs while
retaining export of existing logs and recordings.

Keep daily/size rotation and bounded retention within the log backend.
Report provider failures explicitly and route JNI lifetime diagnostics
directly to logcat to avoid re-entering JNI during native thread teardown.
Allow a deleted log folder to be selected and created again.

Validation: Android ARM64 release cargo check, Kotlin compilation, and two
existing Flutter setup tests passed. A temporary real-JVM reproduction
using the production writer passed native thread exit at debug and trace
levels after reproducing the earlier crash. Full Rust/APK builds passed
before the final JNI and missing-folder fixes; those fixes passed Rust and
Kotlin checks. The final APK and folder authorization, rotation, export,
and uninstall retention have not been tested on an Android device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): validate SAF parent names and restore workspace navigation

Reject a newly created SAF parent when the provider changes its display
name, before receiving data into that directory. This prevents sanitized
names from splitting one source folder into multiple destination folders
while reporting success. An adjusted empty directory can remain.

Restore the local navigation root for saved paths inside the actual app
workspace before checking the saved directory. Keep that choice separate
from the host's shared SAF root and leave peer access checks unchanged.

Validation: 12 tracked Flutter file-model/download-path tests passed.
Three temporary controller tests passed for workspace root/subfolder
restoration, local SAF restoration, and workspace-prefix rejection; the
workspace case failed before the fix. Kotlin compilation, focused Dart
analysis, and diff checks passed, with one existing analysis info.
No rebuilt APK or device provider-name/navigation test was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): authorize log storage before outgoing connections

Require the existing log-folder authorization before opening an Android remote-control, file-transfer, camera, or terminal session. Canceling or failing authorization leaves the connection unstarted; check that the page is still mounted before navigation.

Validation: two temporary authorization regression tests and two existing setup widget tests passed. Focused analysis reported only unchanged diagnostics, and git diff --check passed. No rebuilt APK or device validation.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clear terminal admin intent on canceled log setup

Clear IS_TERMINAL_ADMIN when the Android log-folder authorization guard aborts a terminal launch. A canceled administrator-terminal attempt can no longer pass its one-shot intent to a later ordinary terminal connection. Successful authorization keeps the existing session initialization behavior.

Validation: the temporary regression test reproduced the stale flag before the fix; all three temporary widget tests pass after it, including successful administrator intent preservation. Focused Dart analysis reports only unchanged diagnostics, and git diff --check passes. Android authentication dialogs were not tested on a device.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clarify log setup and default folder selection

Show a concise log-directory prompt before opening the picker, and skip
the prompt when existing log access is ready. Start first file-transfer
folder selection in Documents, excluding the separate log grant from
the check for previous transfer-folder selections.

Validation:
- Focused Flutter analysis, locale consistency, and diff checks passed.
- Six temporary authorization/dialog tests passed during implementation.
- The review rerun of existing setup tests timed out while loading (60s).
- Kotlin/APK builds and Android device validation were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): check log readiness before boot hosting

Run the log-directory preflight off the receiver thread before creating
the hosting service. Abort startup with an explicit error when logging
is unavailable, and finish the asynchronous broadcast on every path.

Validation: four temporary JVM startup tests, Android Kotlin compilation,
and git diff --check passed. Release APK reboot and provider validation
were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify log setup and preserve reconnect paths

Use a single Choose folder action with clearer log-storage guidance and
request Android/media/RustDesk as the initial picker location. Reuse the
existing directory ID and add the button label to the locale maps.

Restore the workspace root before opening each Android local reconnect
candidate, preserving the currently browsed directory ahead of saved
locations while retaining the existing fallback order.

Validation: 14 existing Flutter tests and two temporary Android-controller
regression tests passed. Scoped Dart analysis and git diff --check passed,
with only the existing setter-style informational diagnostic.

Android compilation and device validation were not performed for these
changes. The initial picker location remains a system hint and can be
ignored if the directory does not exist.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): prioritize log setup and separate picker defaults

Authorize log storage before showing first-use file-transfer setup.
Always request Documents for transfer folder selection, independently
of previously granted folders.

Request Android/media/RustDesk for logs when accessible, otherwise use
Android/media so the existing authorization flow can create the child.
Remove the obsolete log-grant classifier.

Validation: two temporary setup-order tests, three temporary JVM picker
tests, and two existing Flutter setup tests passed. Scoped Dart analysis,
Android Kotlin compilation, and git diff --check passed.

Actual picker behavior was not tested on a device. Initial locations
remain system hints supported from API 26.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: translation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): preserve default logs and simplify folder setup

Limit folder setup prompts to hosting startup and remember successful
manual shared-folder selections. Outgoing sessions and file-transfer
permission toggles no longer request folder configuration.

Keep release logs in Config::log_path() until public storage is authorized,
and use that original directory when public writes fail. A failed default
writer must not prevent logging to a working public folder. Report the
fallback explicitly and retain both errors when neither destination works.

Use Android/media/<installed AppName>/Logs for public logs. Resolve and
create only authorized descendants, verify provider-created directory
names, and update picker and startup messages. Keep hosting authorization
and the Documents transfer-picker default unchanged.

Validation:
- Two focused Rust regressions using the real writer and flexi_logger with
  a supplied Android bridge: default/public routing, fallback and failures.
- Five temporary Flutter checks and two existing folder-setup widget tests.
- Two temporary Kotlin checks for standard/custom app names, authorized
  directory creation and independent picker defaults.
- Android :app:compileDebugKotlin -Ptarget-platform=android-arm64 passed.
- Scoped Dart analysis matches HEAD's existing diagnostics; rustfmt and
  git diff --check passed.

The temporary checks supply Android APIs; release APK logging, permission
revocation, fallback and reboot behavior have not been verified on device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify log folder selection errors

Show "Please select Android/media." when a log-folder selection is invalid.
Display the platform error message instead of the full PlatformException,
while keeping the complete error in debug output. Directory validation and
permission handling are unchanged.

Validation: scoped Dart analysis, both existing folder-setup widget tests,
and git diff --check passed. No APK rebuild or device verification.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify the log folder picker location

Always request Android/media as the initial log authorization location
and show the same short path in the picker title. Keep creating and
using <AppName>/Logs internally.

Remove the obsolete filesystem probe for an existing Logs directory.
Preserve accepted grants, logging policy and the Documents default for
the separate file-transfer picker.

Validation: two temporary picker checks with supplied Android APIs,
Android :app:compileDebugKotlin -Ptarget-platform=android-arm64, and
git diff --check passed. Actual system-picker positioning has not
been verified on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep hosting independent of log access

Restore boot and native service startup without requiring public log
folder authorization. The existing logger uses the original directory
when public logging is unconfigured or fails. Removing the early service
stops also avoids the foreground-service startup failure path.

Keep log and shared-folder setup on deliberate hosting startup, but allow
hosting confirmation after cancellation or setup failure. Request log
setup once, before shared-folder setup.

Route Android SAF AllFiles requests through the existing CM enumeration
and result channel so the connection loop can continue processing messages.
Preserve path validation, file-count limits and ordinary filesystem paths.

Validation:
- Android Kotlin compilation and ARM64 release cargo check passed.
- Focused Dart analysis and both existing folder-setup tests passed.
- Temporary source-based startup, cancellation, log fallback and SAF
  dispatch checks passed using supplied platform/provider APIs.
- git diff --check passed; no new rustfmt differences from HEAD.

The CM unit test timed out during dependency compilation at the 60-second
limit and did not execute. No APK/device reboot, revocation or slow-provider
validation was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject SAF root deletion before traversal

Reject the selected SAF tree root before recursive enumeration or deletion,
so a failed root-deletion request cannot remove its empty subdirectories.
Keep ordinary subdirectory deletion on the existing path and strengthen
one existing root-rejection assertion.

Validation:
- cargo test -p base --lib fs::android (2 passed)
- Three temporary source-backed filesystem checks passed for root
  preservation, ordinary subtree deletion and nonempty subtree rejection
- rustfmt --check and git diff --check passed
- No Android device validation performed

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject stale SAF source metadata

Check provider size and modification time after opening a SAF source against
its listed metadata. Reject changed sources before advertising a stale digest,
preventing resumed downloads from combining old and new file contents.

Treat negative provider timestamps as unknown instead of rejecting the entire
listing. Offset resume remains disallowed for sources with unknown timestamps.

Validation:
- cargo test -p base --lib: 20 tests passed.
- Standalone Rust transfer checks: reject a changed source, restart with the
  current contents, and preserve resume for an unchanged source.
- rustfmt --check and git diff --check passed.
- No Android APK build or device validation performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): move shared folder selection under permissions

Place Change Path below Transfer file on the Share screen page, retaining
custom-client visibility and active-session permission restrictions.
Remove the Settings entry and shorten the first-use sharing prompt.

Keep the existing translation key and update its English and Chinese
display text. Adjust the existing setup widget test to match.

Validation:
- Existing setup widget tests: 2 passed.
- Focused Dart analysis: no errors or warnings; existing page deprecation
  notices remain.
- Locale consistency, Dart formatting, and git diff --check passed.
- No Android APK build or device layout validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): tidy shared folder option layout

Render Change Path as a compact, full-width sub-option below Transfer
file, with one directional inset and a trailing chevron. This removes
the extra mobile text-button padding while preserving permission locking.

Validation:
- Focused Dart analysis: no errors or warnings; three existing deprecation
  notices remain.
- Dart formatting and git diff --check passed.
- No new tests for this presentation-only change; updated device layout
  has not been validated on Android.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): authorize legacy public logs without a folder picker

On Android versions below 8, request the system storage permission and
write configured public logs to Android/media/<AppName>/Logs. Limit
WRITE_EXTERNAL_STORAGE to API 25; keep the existing SAF flow on newer
Android versions. General storage permission alone does not redirect logs
before explicit setup.

Keep the original log destination before setup and on public-write errors.
Permission denial completes promptly and does not prevent hosting. Retain
log rotation and export, and reopen the writer during readiness checks if
the fixed directory has been moved or removed.

Validation:
- Nox Android 7.1.2: permission grant/denial, original-log fallback, fixed
  public destination, persisted setup, and actual Rust logs after restart.
- Isolated device regression: exported public logs match the source bytes;
  moved-directory readiness fails before the fix and passes afterward.
- Full ARMv7 Flutter debug APK with the release Rust library built and
  installed successfully.
- Two Flutter setup tests, scoped Dart analysis, whitespace checks, and
  a fresh CodeRabbit review passed.

Android 5/6 and Android 8+ device testing was not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore SAF log setup and surface folder errors

Use SAF for public logs on all supported Android versions. Remove the
legacy direct writer and WRITE_EXTERNAL_STORAGE permission, preserving
the original logging fallback and normal picker cancellation.

Keep log-folder errors visible until acknowledged, then exit the current
manual hosting setup instead of advancing to file-transfer setup. Translate
the error text, add Chinese translations, and register the missing key in
the language catalogs. Boot startup and existing services are unchanged.

Validation:
- Two existing Flutter file-transfer setup tests passed.
- Focused Flutter analysis and git diff --check passed.
- Built the release ARMv7 Rust library and Flutter debug APK.
- On Nox Android 7.1.2, invalid-root selection showed the persistent Chinese
  error; acknowledgement exited setup without starting hosting.
- Ordinary picker cancellation continued the existing optional setup flow.
- APK inspection found no READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE,
  or MANAGE_EXTERNAL_STORAGE permissions.

Device validation used the debug APK on Nox; other Android versions and
a full release APK were not device-tested for this change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): bind SAF digests to opened files and simplify setup

Read size and modification time from the opened document descriptor so
replacing a source path cannot pair old data with a newer resume digest.
Refresh stale metadata for regular files and keep streams non-resumable
while preserving fresh sequential sends.

Shorten the shared-folder setup prompt and direct log-folder selection to
Android/media. Remove obsolete app-media log-directory queries and export
allowances; retain SAF logs, original app-data logs, and recordings export.

Validation:
- Focused base Android tests: 3 passed; the replacement regression failed
  before the descriptor fix and passed afterward.
- Existing Flutter setup and file-model tests: 11 passed.
- ARMv7 release Rust library and Flutter debug APK builds passed.
- Scoped CodeRabbit re-review: 0 findings; git diff --check passed.

No Android device validation was performed for this revision.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): avoid blocking log callers during folder setup

Bypass per-record JSON/JNI dispatch after discovering that public logging
is unconfigured. Publish routing changes from the application-owned Kotlin
logger so a late fallback response cannot undo successful folder setup.

Return contended public writes to the existing original-directory writer
instead of waiting for provider operations. Keep the JNI callback available
in debug Android builds without changing their logcat logger, and remove
the unused English tree-picker title.

Validation:
- ARMv7 release Rust build and full Flutter debug APK packaging passed.
- Android 7 provider contention check reproduced the wait before the fix
  and passed with the rebuilt Kotlin code and native library.
- Temporary native routing test passed: one initial backend lookup,
  subsequent bypass, re-enabling, and all records in the fallback file.
- Formatting and diff checks passed; scoped CodeRabbit findings were
  reviewed against the Rust fallback and picker callers.

The full APK was built but not installed for this change. Device checks
used the existing isolated probe; full release-app end-to-end validation
remains pending. No permanent test infrastructure was added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): allow slower SAF empty-directory scans

Use the existing 30-second remote-directory timeout only for SAF scans. Ordinary filesystem scans keep the two-second timeout, and the request lifecycle is unchanged.

Add a regression for a SAF response arriving after three seconds. The file-model suite passes all 10 tests. Targeted analysis reports only the existing setter lint; Android device validation was not performed. Scans exceeding 30 seconds can still time out.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): isolate SAF empty-directory scan lifecycles

Only remove an empty-directory request when its timeout still owns the registered completer. Cancel pending SAF scans when the remote session changes so the same directory can be retried immediately. Ordinary filesystem reconnect behavior and timeout durations remain unchanged.

Share regression coverage for completed and reconnected scans, keeping each retry pending across the previous timer deadline without adding production state.

Validation: all 11 file-model tests pass. Test-file analysis and whitespace checks pass; targeted production analysis reports only the existing setter lint. No Android build or device validation was performed for this change.
Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(android): deduplicate SAF helpers and test setup

Share local storage navigation, tree-picker Intent construction, and
provider listing readiness checks. Preserve independent picker defaults,
error messages, failed-navigation rollback, and authorization lifecycles.

Reuse Rust transfer-test fixtures and remove text-only setup assertions
while retaining behavioral regression coverage. Reduce the branch diff
by 78 lines without changing transfer or logging policy.

Validation:
- Focused Rust SAF tests: 3 passed.
- Four focused Flutter suites: 20 passed; the two setup tests were rerun
  successfully after removing text-only assertions.
- Kotlin 2.1.21 / Android SDK 36 compilation passed for all six document
  integration sources in an isolated Linux directory.
- git diff --check passed. No APK build or device validation this round.

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore(android): organize SAF tests and complete zh-TW label

Move the two SAF test files into libs/base/src/fs/tests and update the
module path, preserving their contents and module hierarchy. Fill the
missing Traditional Chinese translation for Choose folder.

Validation: cargo test -p base --lib fs::android passed (3 tests).
Verified both moved files are byte-identical and all six new SAF/logging
keys have nonempty Traditional Chinese translations. Diff check passed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep hosting available and reject stale SAF scans

Keep the log-folder error dialog, skip remaining folder setup after an
error, and continue to the existing hosting confirmation. Public-log
failures retain the existing default-directory logging fallback.

Reject remote SAF empty-directory requests if the session changes while
awaiting bridge dispatch. Reuse the existing session generation and leave
ordinary filesystem requests unchanged. Extend the existing lifecycle
test to cover reconnecting during dispatch.

Validation: 21 focused Flutter tests passed. A temporary startup-flow
check passed for setup error, success and cancellation. Analysis reports
only the existing setter-return-type info. Diff check passed; CodeRabbit
review completed with no findings. No Android device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): avoid logging raw folder setup errors

Log fixed status messages for Android log-folder setup failures instead
of provider-derived exception text. Preserve detailed errors in the
dialog and keep the existing folder setup and hosting flow unchanged.

Validation: focused Flutter analysis and both existing folder-setup
widget tests passed. Diff check passed. No Android device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): offload access checks and restore identical-file hints

Run Android peer-path authorization on blocking workers for initial
folders, file actions and rename destinations. Preserve fail-closed
access decisions and the existing non-Android path.

Set the SAF identical-file hint only for matching sizes and known,
matching modification times. Keep overwrite confirmation and reject
receive resume. Extend the existing transfer test to cover the hint.

Validation:
- cargo test -p base --lib fs::android: 3 passed.
- Android ARMv7 API 21 release cargo check with flutter,hwcodec passed
  using an isolated copy of HEAD plus these changes, verified by SHA256.
- macOS cargo check --lib --features flutter passed for the access fix.
- Temporary scheduling check failed before the fix and passed afterward.
- Scoped CodeRabbit reviews and diff checks passed.
No APK build or device testing was performed for this follow-up.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): limit SAF dispatch and clarify log exports

Use blocking workers only for document-backed file operations. Keep
ordinary Android filesystem calls on their original execution path and
retain asynchronous peer-path authorization checks.

Report success and failure counts when log export succeeds only partly.
Skip the destination picker when there are no default log sources and no
public-folder permission. Preserve public-log permission errors.

Validation:
- cargo test -p base --lib fs::: 19 passed.
- Flutter file-model and Android setup suites: 14 passed.
- Android ARMv7 API 21 release cargo check with flutter,hwcodec passed
  against an isolated source copy, verified by SHA256.
- Temporary Dart feedback and Kotlin export-handler checks failed before
  the fix and passed afterward.
- Focused Dart analysis found only existing diagnostics; diff checks passed.
No APK build or device testing was performed for this follow-up.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): allow longer SAF deletion scans

Use the existing 30-second deadline when recursively scanning SAF folders
before deletion. Keep the two-second timeout for ordinary filesystem
paths, with request handling and deletion behavior unchanged.

Validation:
- Existing Flutter file-model and Android setup suites: 14 passed.
- Targeted Dart analysis found no new diagnostics.
- git diff --check passed.
No Android APK build or device testing was performed for this fix.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep public logging active after cleanup failures

Log a warning when deleting an expired log fails, and continue writing to
the newly opened public log. Keep creation, open, and write failures on
the existing fallback path. Failed deletions may leave extra old logs.

Validation:
- Four temporary Kotlin checks passed: deletion false/exception, normal
  rotation, and propagation of a genuine log-open failure.
- Actual document-storage Kotlin sources compiled against Android API 36.
- git diff --check passed. No APK build or device test performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep accepted SAF download file lists immutable

Reject replacement of an accepted generic SAF download file list before
any mutation, so per-file overwrite approval cannot be redirected to a
different destination. Preserve the ordinary download and upload paths.

Extend the existing overwrite test and construct each receive fixture
with its initial file list only once.

Validation:
- The added assertion failed before the fix; all 19 filesystem tests
  passed afterward: cargo test --offline -p base --lib fs::.
- Scoped CodeRabbit review completed with no findings.
- No new rustfmt differences; git diff --check passed.
- No Android build or device test performed for this fix.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): preserve secondary document-transfer exceptions

Attach cleanup and backup-restore exceptions to the original error before
wrapping it in IOException. Preserve the existing error messages, primary
cause, and transfer control flow.

Validation:
- Confirmed the diff contains only the two addSuppressed calls.
- git diff --check passed.
- No Android build or device test performed for this diagnostic change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): log authorization failures with throttling

Report document access-check errors at warning level and blocking-task
failures at error level. Throttle each site to one record per minute while
preserving existing deny decisions and failure messages.

Validation:
- Existing hbb_common log_throttle tests: 4 passed.
- Rust formatting and git diff --check passed.
- Android target build and device testing not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-28 10:03:43 +08:00
CatzTimesandCatzTimes f010d33ca6 fix(android): wheel scrolling from the controller does not reach Android peers (#16409)
The controller sends a wheel tick as a MouseEvent whose type bits are
MOUSE_TYPE_WHEEL (src/common.rs) with the scroll delta in y, but
InputService only handles the legacy WHEEL_DOWN/WHEEL_UP masks
(523331/963) that current controllers never send. As a result the
mouse wheel does nothing on every Android controlled device.

Handle MOUSE_TYPE_WHEEL with the same stroke emulation the legacy
masks use, as a purely additive branch: a 90px vertical stroke
(MOUSE_WHEEL_STEP, three quarters of the legacy WHEEL_STEP - the full
120px felt too far per tick to reviewers on real devices) over
WHEEL_DURATION at the current cursor position, with the same top-edge
guard and queue consumption, mapped with the controller's sign
convention (y < 0 = wheel down; y == 0, a horizontal tick, is
ignored). The branch is gated to API 24+ like the rest of the gesture
path - below that, legacy devices take the Shizuku backend or a
no-op, not this class. The legacy WHEEL_DOWN/WHEEL_UP implementations
are untouched.

Community reports matching this gap on modern devices: #9321 (Pixel
8, Android 14) and #1466 (Android tablet).

Signed-off-by: CatzTimes <CatzTimes@users.noreply.github.com>
Co-authored-by: CatzTimes <CatzTimes@users.noreply.github.com>
2026-10-22 11:41:56 +08:00
0740063863 Keyboard shortcuts (#14933)
* fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848)

* feat(shortcuts): user-configurable keyboard shortcuts for session actions

  Adds a keyboard shortcut feature (Rust matcher + Dart UI + cross-language
  parity tests) that lets users bind combinations like Ctrl+Alt+Shift+P to
  session actions. Bindings are stored in LocalConfig under
  `keyboard-shortcuts`; the matcher gates dispatch on `enabled` and
  `pass_through` flags so flipping the master switch off is a hard stop.

  Wire-up summary:
  - src/keyboard/shortcuts.rs: matcher, default bindings, parity test against
    flutter/test/fixtures/default_keyboard_shortcuts.json
  - src/keyboard.rs: shortcut intercept in process_event{,_with_session},
    feature-gated to `flutter`; runs before key swapping so users bind to
    physical keys
  - src/flutter_ffi.rs: main_reload_keyboard_shortcuts +
    main_get_default_keyboard_shortcuts; reload_from_config seeded in main_init
  - flutter/lib/common/widgets/keyboard_shortcuts/: shared config page body,
    recording dialog, shortcut display formatter, action group registry
  - flutter/lib/desktop/pages/desktop_keyboard_shortcuts_page.dart and
    flutter/lib/mobile/pages/mobile_keyboard_shortcuts_page.dart: platform
    shells around the shared body
  - flutter/lib/models/shortcut_model.dart: per-session ShortcutModel +
    registerSessionShortcutActions for actions with no toolbar TToggleMenu /
    TRadioMenu (fullscreen, switch display/tab, close tab, voice call, etc.)
  - flutter/lib/common/widgets/toolbar.dart: optional `actionId` field on
    TToggleMenu / TRadioMenu, plus per-helper auto-register pass that wires
    tagged entries' existing onChanged into the ShortcutModel
  - flutter/test/keyboard_shortcuts_test.dart + fixtures: cross-language
    parity (default bindings, supported key vocabulary)

  Design principles applied during review:

  1. Additions are fine; modifications to original logic must be deliberate.
     Tagging an existing TToggleMenu entry with `actionId:` is an addition.
     Rewriting its onChanged to satisfy a new contract is a modification —
     and was reverted for every case where the original click behavior was
     working. Four closures were touched and then reverted (mobile View
     Mode, Privacy mode multi-impl, Relative mouse mode, Reverse mouse
     wheel); their shortcuts are wired via standalone closures in
     shortcut_model.dart instead.

  2. Toolbar auto-register is reserved for entries whose onChanged is
     inherently self-flipping — typically `sessionToggleOption(name)` where
     the named option is flipped in place and the input bool is unused. The
     register pass passes `!menu.value` from registration time, which is
     harmless under self-flipping but wrong for closures that consume the
     input bool directly. Tagging a non-self-flipping entry forces a closure
     rewrite; choose non-toolbar registration in that case.

  3. When shortcuts are disabled, toolbar behavior must be bit-for-bit
     unchanged. The matcher's `enabled`-gate already guarantees no
     dispatch; the auto-register pass is left unconditional (its only effect
     is HashMap operations on a separate ShortcutModel) so mid-session
     enable works without a reconnect. The trade-off is intentional and
     documented at the top of toolbarControls.

  4. Comments stay terse. Rationale lives in one place — the doc comment of
     the helper or registration site, not duplicated at every call site.

  5. Where an existing helper needs a new optional behavior (e.g.
     `_OptionCheckBox` gaining a tooltip slot), the new branch must reduce
     to byte-identical output for existing callers (`trailing == null`
     case → original `Expanded(Text)` layout). Verified.

  6. Action IDs and labels stay consistent. Renamed `reset_cursor` →
     `reset_canvas` so the action ID matches its user-facing label
     ("Reset canvas") and capability flag.

  Out-of-scope but included:
  - AGENTS.md: documents flutter_rust_bridge no-codegen workflow and the
    Web target's hand-written TS client, since both are load-bearing for
    any new FFI work.
  - remote_toolbar.dart: i18n fix for the per-monitor tooltip ("All
    monitors" / "Monitor #N"), unrelated to shortcuts but kept here.

* langs

* fix(keyboard): shortcuts, harden config and callback lifecycle

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, color of "Reset to defaults"

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, release keys before shortcut callback

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, debug web

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(keyboard): iPad, icon

Signed-off-by: fufesou <linlong1266@gmail.com>

* langs: add shortcut keys to az, gl, ur

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep local modifiers and release keys on the right session

When a chord fired, the shortcut path reused release_remote_keys(), which
replays synthetic releases through process_event(). That cleared the local
MODIFIERS_STATE while the user still held the modifiers, so the next key of
a held chord no longer matched and leaked to the remote, and it sent the
releases to the globally current session instead of the one that typed the
chord.

The shortcut path now releases the remote keys itself: it sends through the
caller's session, and restores the modifiers it released on the remote as
still held locally. The press that fired is remembered until its release,
so its auto repeats and its release are consumed instead of reaching the
remote.

Also state the contract for unavailable actions: a bound chord always
belongs to RustDesk and is a no-op when the session has no handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, record the physical key

The recording dialog and the Web Flutter matcher named keys by
LogicalKeyboardKey, while the native matcher sees the physical key through
its USB HID usage and the Web JS matcher through KeyboardEvent.code. On
AZERTY, QWERTZ, Dvorak and similar layouts a recorded binding then did not
fire, or fired from another key.

Name keys by their physical position everywhere: physicalKeyName maps the
USB HID usage to the stored name. A new fixture pins every name to its USB
HID usage, checked on the Dart side and against rdev::usb_hid_key_from_code
on the Rust side, and a test covers AZERTY and QWERTZ events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep an empty binding list on re-enable

setEnabled seeded the defaults whenever the binding list was empty, so a
user who cleared every binding got all defaults back after disable and
enable. Seed only when the config has never held a bindings list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, track every fired key until its release

FIRED_KEY held a single key, so overlapping shortcut keys broke it: with the
chord held, P then C overwrote P, P's release reached the remote, and a P
repeat could fire the screenshot again. A repeat after the modifiers were
released was also treated as a new press and leaked to the remote.

Keep a set of fired keys instead. Once a press fires, every event of that
key belongs to RustDesk until its release: repeats are consumed without
dispatching, whatever the modifiers do, and the release removes the key.
Entries whose release never arrives are dropped when a session view is
entered or a session is closed, not by guessing from the modifiers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep fired keys per session

FIRED_KEYS was process-global while dispatch is per session. Closing or
entering any session cleared the fired keys of all of them, so a key still
held in another session leaked its release to that remote, and a key fired
in one session made the same key in another session look like a repeat.

Key the fired set by session: try_dispatch resolves the session first and
only looks at that session's keys, and session_close clears only the
closing session. Entering a session view no longer clears anything: it is
a pointer enter, and a fired key may still be physically held across it.
A key whose release is missed otherwise heals itself: its next press is
consumed as a repeat and that release removes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(shortcuts): preserve input state and refresh action availability

* fix(keyboard): shortcuts, keep a fired key owned across session close

Close tab is itself a shortcut, so the session that consumed the key press
is gone before the key is released. Both matchers dropped the key with the
session: the Flutter dispatcher removed its own entries in clear(), and the
Rust set was keyed by session and cleared in session_close. The release,
and the repeats while the key stayed held, then reached the next session's
remote as ordinary input.

Ownership of a fired key is physical, so it is now one set shared by every
session on each side, and closing a session no longer touches it: clear()
only drops the pending action and modifier replay, and the Rust session
state only tracks released modifiers. A key whose release is missed heals
itself on its next press. Entering view-only with the Flutter input source
still clears the Rust set, since that source stops routing key events to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, drop the Toggle Input Source action

The action switches the key capture backend between key down and key up.
Fired-key ownership lives in whichever matcher saw the press, the Flutter
dispatcher or the Rust set, and there is no handoff between them: after the
switch the repeats and the release of the held key reach the other backend,
which forwards them to the remote and, while the chord is held, matches the
action again and toggles the source back. Remove the shortcut instead of
building a handoff for one action. The toolbar radio menu is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* perf(keyboard): shortcuts, parse the config once per stored value

The Flutter matcher read the local config three times and parsed its JSON
three times on every key press in legacy mode, on Web and in view-only.
Keep one parsed snapshot keyed by the raw stored string, so a key press
costs one config read and no parsing while the config is unchanged. The
existing readers are thin views of the same snapshot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, run focus-changing actions on key release

Close tab and tab switching move keyboard focus to another session before
the key that fired them is released. On Linux the Flutter input source
routes map-mode sessions through the Rust matcher and legacy-mode sessions
through the Dart matcher, and neither knows the other's fired keys, so
after such an action the repeats and the release of the held key reached
the next session's remote, and the key stayed recorded as fired in the
matcher that saw the press.

Routing Linux legacy mode through the Rust FFI path instead is not a drop-in
change: the raw key handler builds its event with no unicode, so Rust's
legacy mode would fall back to US key names and lose the layout-aware
characters the Dart path sends today, and it would take modifiers from the
X11 key state instead of Flutter's.

Run these three actions when the fired key is released. The matcher that
consumed the press then also sees the repeats and the release, the remote
modifiers are still released on the press, and a session that closes drops
the action it owed. Both matchers keep the same list of actions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* test(keyboard): shortcuts, pin the key-up action list on both sides

kShortcutActionsRunOnKeyUp and runs_on_release are two hand-kept lists of
the actions that run on key release. Add a shared fixture and check each
side against it, the way the key vocabulary and the default bindings are
already checked, so adding a focus-changing action on one side breaks a
test until the other side follows. The Dart test also checks that every
listed id is a configurable action.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix: prevent lost input after remote session shortcuts

Avoid synthetic Alt presses that activate the Windows system menu
and swallow the next character.

Trigger restart, switch-sides, and chat actions on key release so
focus changes cannot leave subsequent shortcut presses ignored.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(mobile): correct Chat keyboard and input helper behavior

- Keep soft keyboard and helper hidden when Chat opens via shortcut
- Restore soft input on touch and remote focus after Chat closes
- Handle modifier releases while Chat owns focus
- Clear hidden virtual modifiers while preserving held keys and Pin behavior

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): prevent recording non-simultaneous key combinations

Build new captures from currently held keys instead of reusing released
letters. Preserve the last valid chord after release so it can be saved.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): isolate window actions and prevent duplicate Alt presses

- Dispatch shortcut actions only to the originating session window.
- Clear local modifier state on focus loss without resending modifiers
  already released to Windows peers, preventing lost following input.
- Strengthen the regression assertion against extra Alt keydown events

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(shortcuts): empty default shortcuts for Cmd+Option+Shift+Del on macOS and iOS

Because Del may be ambiguous for new macOS users.
Cmd+Option+Shift+Backspace is "Empty Trash without confirmation" shortcut on macOS.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: use session peer platform for Ctrl+Alt+Del events

Fix Android-to-Linux Ctrl+Alt+Del delivery by using the session's peer
platform instead of the desktop global session, which defaults to Windows

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: hide relative mouse mode shortcuts on Wayland

Separate relative mouse mode availability from screenshot support.
Filter unsupported actions and defaults while preserving saved bindings

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: align shortcut availability with platform support

- Hide unsupported mobile and web shortcut actions
- Enable mobile scaling and Android voice call shortcuts
- Use openMonitorInTheSameTab() for display switching
- Preserve cursor position when switching displays on desktop and web

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent held-key shortcut recording bypass

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve keyboard input after shortcut dialogs

Run native clipboard shortcuts on key release and wait for remote modifier releases before dispatching.

Release remote modifiers when mobile local UI takes focus, and document the tested iPad Bluetooth Mute shortcut limitation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve remote key state during local shortcuts

Keep unrelated held keys pressed for session actions that retain input. Preserve key release when focus or input handling changes, and avoid synthetic Alt taps during shortcut dispatch.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: suspend saved relative mouse shortcuts on Wayland

Filter the native active binding cache without changing stored configuration, so the binding becomes available again under X11.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude unsupported Wayland shortcuts from Flutter matching

Keep saved bindings intact while filtering the active config used by the Flutter matcher. Cover stored bindings and enable/pass-through flags in the existing shortcut tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: retain forwarded key ownership across shortcut repeats

Ignore repeat presses for keys already forwarded to the peer, and release ordinary held keys before Show My Cursor enables view-only. Cover both paths with converter-level regressions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: release Flutter-held keys before view-only shortcuts

Track ordinary keys forwarded by the Dart matcher paths and await their releases before View Only or Show My Cursor disables input. Keep Mute key ownership and existing empty-cleanup timing unchanged.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: filter active Flutter shortcuts by platform capabilities

Reuse the Settings action filter for saved bindings while preserving storage, default-seeding behavior and configurable Apple Ctrl+Alt+Del bindings.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: filter native shortcut caches for mobile controllers

Suspend saved desktop-only actions on Android and iOS and hide unavailable iOS actions without changing persisted bindings. Keep native cache filtering aligned with Flutter platform capabilities.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: share filtered shortcut bindings with the Web matcher

Pass the active platform config to JavaScript at startup and after settings changes, preserving unsupported saved bindings in local storage.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: release Flutter-held keys before focus-changing shortcuts

Drain ordinary keys owned by the original session before run-on-key-up
actions and native clipboard callbacks. Preserve Mute holds, pending-action
cancellation and Web clipboard key-down activation.

Document the required Web matcher counterpart in the bridge:
rustdesk/rustdesk-web-js@94abe431d6

Validation: 61 native Flutter tests, 32 Chrome/HTML tests, targeted analysis
and Windows debug build.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: remove the useless comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent Windows menu activation from Alt-only shortcuts

Neutralize the forwarded Alt with a balanced temporary Ctrl press when a consumed local shortcut releases its only modifier in Windows Map mode. Keep physical modifier tracking and normal Alt pass-through unchanged.

Add a regression covering both Alt keys, trigger repeats and the eventual physical release. Verified with 36 native shortcut tests and real Windows menu/input checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent Alt menu activation in all keyboard modes

Extend Windows Alt-only shortcut neutralization to Translate and Legacy. Keep the temporary Ctrl packets in Map mode so Legacy modifier synchronization cannot release Alt before Ctrl.

Extend the existing regression across keyboard modes and decode scan, virtual and control-key events. Verify 36 native tests and 34 Windows menu replay cases.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: initialize outgoing chat before shortcut dispatch

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: guard Windows Alt release in Flutter legacy shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: coordinate Web shortcut dispatch and Alt modifier cleanup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: distinguish AltGraph from ordinary Alt shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: dispatch macOS Web shortcuts without waiting for keyup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: support legacy peers in privacy mode shortcut

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude AltGraph from Flutter shortcut matching

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude GTK AltGr input from raw shortcut matching

Signed-off-by: fufesou <linlong1266@gmail.com>

* test: trim redundant keyboard shortcut coverage

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(keyboard): prevent Cmd shortcuts from opening remote Start

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): guard Windows menu keys after Ctrl/Cmd swapping

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): align recording and display routing

Reject non-Apple AltGraph captures to match runtime filtering. Honor individual-display windows for next, previous, and all-display shortcuts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): reject GTK AltGr chords in recorder

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore keyboard focus for modifier-first input

Focus the existing FlutterView when a modifier is pressed without native view focus. This restores shortcut-first input after returning to RustDesk while preserving focused inputs and normal key dispatch.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): release held keys before keyboard mode changes

Drain Flutter-forwarded keys before keyboard-mode shortcut actions so releases use the original encoding. This prevents held keys from remaining down after a Web Input source 2 mode change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(i18n): use sentence case for shortcut labels

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(mobile): preserve touch modifiers in the input helper

Keep touch-selected modifiers visible after hiding the keyboard. Clear physical modifier releases locally in view-only mode so they do not reopen the helper.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): preserve modifiers across Legacy-to-Map shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): check support before registering all displays view

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:31:44 +08:00
memory_clearandfufesou c9c0b5d0ef Fix translation for voice call audio capture prompt (#16433)
* Fix translation for voice call audio capture prompt

* fix: translation, audio capture prompt, wrong "Share screen"

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-10-05 20:36:22 +08:00
vipe3 8154c026a4 fix(android): default to entire screen for capture (#16163)
Use MediaProjectionConfig on Android 14+ to remove the app-versus-screen choice while retaining the legacy fallback.
2026-09-16 12:01:26 +08:00
fufesouandRustDesk cdcb4d4b4c Fix/android voice call (#16180)
* fix(android): handle missing audio permission in voice calls

- Avoid stop errors when voice capture was never started
- Dispatch voice call error dialogs on the main thread
- Explain how to enable Audio capture on the Screen share page

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore audio after failed voice call switches

* fix: translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: translatin

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(i18n): match UI label capitalization in translated messages

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-09-14 12:24:33 +08:00
RustDeskandClaude Opus 5 b50fde6910 add the base crate and repoint the moved modules at it (#16107)
* add the base crate and repoint the moved modules at it

`libs/base` (crate `base`) takes the parts of hbb_common that only this app
uses: `fs`, `platform`, `keyboard`, `message.proto`, and 145 of the 177
`config::keys` constants. hbb_common keeps what the server names, and the 32
keys it reads itself are re-exported from `base::config::keys` so call sites
still see the full set through one path.

Sources move verbatim. The only edits inside them are `crate::` prefixes that
now have to say `hbb_common::`; `keyboard.rs` and `platform/windows.rs` are
byte-identical. The crate stays on edition 2018, the edition the moved code was
written under. `log`, `lazy_static` and `anyhow` become direct dependencies so
the bare paths in that code resolve exactly as before, and its winapi features
are spelled out rather than left to feature unification.

Two call sites outside Rust and Cargo had to follow the move: the Android
protobuf source dir, which still pointed at hbb_common/protos for message.proto,
and the three AGENTS.md entries that named hbb_common for options, protos and
file transfer.

`scrap`'s `drm` feature now forwards to `base/wayland_probe`. Left pointing at
hbb_common it would still have compiled, silently dropping the Wayland
socket-probe fallback, so that forward is verified by a build with and without
the feature.

`config::keys` carries a test asserting its names stay disjoint from the ones
hbb_common kept: the glob re-export and the local constants share a namespace,
and Rust prefers the local item silently, so a name added to both sides would
otherwise let client and server disagree with no diagnostic.

Verified: macOS and Linux, debug and release, `--all-targets`; the 177 key
constants diffed name-for-name and value-for-value; the generated protobuf types
compared before and after; every `#[cfg]` gate on a moved import checked against
its original; and every file that was `rustfmt`-clean before this change still
is, compared against master file by file. Windows is checked by inspection only
-- it cannot be compiled here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab

* one `use` per crate, and write the rule down

`fs.rs` came out of the move with two ungated `use hbb_common::` statements,
because the original single `use crate::{...}` had to give up `message_proto`
to the new crate and the rest was left in a second block. Fold it back into one.

A scan of the whole tree for the same shape finds nothing else: every other file
with more than one top-level `use base::` or `use hbb_common::` is split by a
`#[cfg]` that does not cover the whole block, or by `pub use` next to `use`.
Those are the cases that cannot merge, so AGENTS.md now states both the rule and
the exemption.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:46:42 +08:00
Michael Clarkandfufesou 1ec1b9e7e3 fix: android: target API 36 (#15603)
* fix: android: target API 35

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: handle API 35 foreground service types

Integrate the foreground-service and MediaProjection lifecycle changes
from fufesou/rustdesk#68 while leaving storage permission handling to
#15602.

Co-authored-by: fufesou <linlong1266@gmail.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: bump required android sdk version to 36, per recent google requirement change.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix(android): clear microphone FGS type when capture stops

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): harden API 36 capture service lifecycle

- isolate MediaProjection callbacks per session
- keep foreground service types in sync with capture state
- handle audio startup failures and shared frame ownership
- upgrade AGP to 8.10.1 for API 36 support

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reset capture state on FGS update failure

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): recover capture after projection failure

Propagate virtual display startup failures, clean up partial video
resources, and resume capture after media projection is reauthorized.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): preserve voice call during projection replacement

Keep the existing capture active until
a new projection is acquired, and restore the
voice-call audio source when capture restarts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): use JDK 17 in playground workflow

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clear pending capture restart on denial

Notify MainService when a recovery projection
request is canceled so a later projection grant
cannot restart stale capture state.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): handle audio and projection recovery failures

Verify AudioRecord startup, propagate voice-call restoration failures,
and clear stale capture recovery state when projection setup fails.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-31 22:29:51 +08:00
Michael Clarkandfufesou d4b06a6c5c fix: android: replace all-files access with scoped storage (#15602)
* fix: android: replace all-files access with scoped storage + system picker

Remove MANAGE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, and
WRITE_EXTERNAL_STORAGE from the Android manifest. Remove
requestLegacyExternalStorage. Replace broad external storage with
app-scoped external storage for the file-transfer workspace.

File import uses the system file_picker. File export uses Android's
SAF ACTION_CREATE_DOCUMENT with path validation that restricts
export sources to app-owned directories.

Remove the external_path dependency.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: refine file import feedback

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: use SAF for file imports

Replace file_picker imports with Android's Storage Access Framework to avoid legacy storage permissions, stale cached files, and duplicate staging of large imports. Stream selected documents into app-scoped storage with failure-safe replacement, keep exports restricted to validated app storage roots, use filesDir for the internal fallback workspace, and remove legacy permissions contributed during manifest merging.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: keep file imports in the selected directory

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: reset projection and constrain file workspace

Release capture resources when media projection is revoked externally. Keep Android local file navigation within the app-scoped workspace.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: handle scoped storage start-up regressions. Allow zero digits in POSIX filenames by rejecting NUL explicitly, and initialise the app-specific home directory before the Android service starts the native server.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: update content resolver mode to use 'wt' instead of 'w' to prevent trailing bytes from old document whilst reporting sucess

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android, enforce file workspace boundary on the server, and unblock the ui thread.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: validate rename destinations against the app workspace bound file-operation paths. report rename failures, general import failures, and unregister / reregister projection when its onStop callback fires.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: reconnect was refreshing the directory with net entry instances, while selected items retained the old instances, it was reporting a selected item, but checkbox statue used object identity, and appeared unchecked. Fixed by reconciling by path and entry type before replacing the directory snapshot, rebinding valid selections, and dropping missing ones.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: (android) add SAF folder import and multi item export - import directories using ACTION_OPEN_DOCUMENT_TREE. Export multiple files, logs, and screen recordings via export buttons, add localisation keys for new actions

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix(android): harden scoped storage file handling

- create new SAF documents instead of overwriting export sources
- reject empty peer paths except for home directory reads
- report directory backup restore and cleanup failures
- resolve log export paths from the configured app name

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): harden scoped-storage file operations

- snapshot directory exports before writing to the destination
- query document provider metadata off the main thread
- reject invalid remote directories without read timeouts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): handle SAF directory name collisions

- reject dot-segment folder names during import
- fail imports with duplicate document display names
- only reuse matching directories during export

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): handle SAF folder import collisions

Reject filesystem-equivalent destination names and
avoid showing a failure when folder overwrite is skipped.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-31 16:29:51 +08:00
RustDeskandClaude Opus 5 e6dd925ab0 fix(android): close outgoing sessions when the task is swiped away (#15753)
* fix(android): close outgoing sessions when the task is swiped away

Swiping RustDesk away from recents destroys the UI but does not
necessarily end the process: when MainService is running (screen share
enabled, or started at boot) the process survives, and with it the
native io_loop of any active outgoing session.

That orphaned io_loop keeps echoing TestDelay (client.rs handle_test_delay
runs entirely on the network thread, no UI involved), which keeps
refreshing last_recv_time on the controlled side. Its 30s inactivity
timeout in server/connection.rs therefore never fires, so the remote
session stays established with no UI left to close it, and the peer
cannot be reconnected to.

Close client sessions from Service.onTaskRemoved, which fires only on
explicit task removal -- not on Home or backgrounding, so ordinary
backgrounding is unaffected. The service itself keeps running, so
incoming connections and the device staying reachable are unchanged.

This complements 152c5c71b, which covered the route-pop path via
dispose(); dispose() does not run when the task is removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(android): also close sessions on activity destroy

Review follow-up. onTaskRemoved only reaches MainService, but the
accessibility InputService keeps the process alive on its own: a user
with input control enabled and screen sharing off has a surviving
process after a swipe while MainService is not running, so the callback
never fires and the session still outlives its UI. onTaskRemoved cannot
cover that -- InputService is bound by the system, not started, so the
callback is not delivered there.

Close from MainActivity.onDestroy() as well, which runs while the
process is still alive regardless of which service keeps it up. Guarded
on isFinishing so a destroy for recreation (configuration change, "don't
keep activities") does not tear down a live session. Both paths are
idempotent.

Also drop the now-wrong "on task removed" wording from the Rust log,
which has two distinct callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(android): release held keys before draining the session map

close_all_sessions drained SESSIONS first, then called
release_remote_keys. The release path sends through get_cur_session(),
which resolves against SESSIONS, so every generated key-up was dropped
after take_remote_keys() had already cleared TO_RELEASE: a key held as
the task is removed stays down on the controlled side until its own
timeout, with the state lost locally.

Release first, while a session is still registered. It is a no-op when
no key is held, so the previous is_empty() guard is not needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:18:14 +08:00
rustdesk 137298e05a revert back 2026-07-10 15:21:48 +08:00
rustdesk 685a89a171 target Android 15 2026-07-10 15:12:06 +08:00
劉清揚 4b1ef9e20d fix(android): sync input service state with Flutter (#15419)
Signed-off-by: liuqiang <2465199797@qq.com>
2026-06-29 15:14:34 +08:00
fufesou 1e6a3dc644 fix(android): waiting for image, one cause (#14919)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-04-27 22:37:22 +08:00
21pages c457b0e7d3 add option to hide stop-service when service is running (#14563)
* add option to hide stop-service when service is running

Signed-off-by: 21pages <sunboeasy@gmail.com>

* update hbb_common to upstream

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-03-19 20:04:10 +08:00
21pages f7a5a506f6 rename RustDeskApplication to MainApplication (#13362)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2025-10-31 11:07:32 +08:00
21pages d106d97b99 mobile verify both webpki and installed CA (#13272)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2025-10-30 13:59:00 +08:00
21pages 965cb704ec add try catch on android setCodecInfo in case of unexpected crash (#13280)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2025-10-24 21:04:18 +08:00
RustDesk 5faf0ad3cf terminal works basically. (#12189)
* terminal works basically.
todo:
- persistent
- sessions restore
- web
- mobile

* missed terminal persistent option change

* android sdk 34 -> 35

* +#![cfg_attr(lt_1_77, feature(c_str_literals))]

* fixing ci

* fix ci

* fix ci for android

* try "Fix Android SDK Platform 35"

* fix android 34

* revert flutter_plugin_android_lifecycle to 2.0.17 which used in rustdesk 1.4.0

* refactor, but break something of desktop terminal (new tab showing loading)

* fix connecting...
2025-07-01 13:12:55 +08:00
fufesou 7330dc70f3 fix: android 7.1, input, crash (#12129)
Signed-off-by: fufesou <linlong1266@gmail.com>
2025-06-20 17:50:28 +08:00
rustdesk 1eee03818d fix https://github.com/rustdesk/rustdesk/discussions/11838 2025-06-19 12:28:38 +09:00
rustdesk d52d9da043 upgrade android plugin to 7.3.1 2025-06-01 20:16:42 +08:00
rustdesk e08cf3c0eb update kotlin 2025-06-01 17:58:03 +08:00
rustdesk 280c12942f improve android build 2025-02-25 00:30:29 +08:00
fufesou 2e89a33210 fix: android, back function (#10843)
Signed-off-by: fufesou <linlong1266@gmail.com>
2025-02-20 01:02:24 +08:00
fufesou 023d46b48c refact: android, handle right click (#10806)
Signed-off-by: fufesou <linlong1266@gmail.com>
2025-02-16 20:45:09 +08:00
fufesou a548e9c94d fix: android, controlled side, gesture (#10792)
Signed-off-by: fufesou <linlong1266@gmail.com>
2025-02-15 18:33:26 +08:00
fufesou 1c17fddf51 fix: android clipboard permission (#10223)
* fix: android clipboard permission

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: Android, clipboard, floating ball

Call rust to check if clipboard is enabled.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-07 22:34:54 +08:00
fufesou 3c838e7a92 fix: Android, try sync clipboard on connecting (#10218)
* fix: Android, try sync clipboard on connecting

Signed-off-by: fufesou <linlong1266@gmail.com>

* Android, clipboard, more clear skip check

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* comment todo: Android clipboard listener, callback twice

Signed-off-by: fufesou <linlong1266@gmail.com>

* Android, clipboard, remove listner

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-07 15:12:15 +08:00
fufesou a23822074e feat: Android, opt, check update on startup (#10165)
* feat: Android, opt, check update on startup

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: check update only on startup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Android, "Download new version"

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-04 17:10:32 +08:00
fufesou d61c99b105 fix: Android 29, crash on restart and reconn (#10054)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-25 21:49:09 +08:00
fufesou f760e21ff8 fix: android w&h, refresh when no connection (#9966)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-19 15:03:00 +08:00
fufesou 8b710f62c8 feat: android clipboard, multi-formats (#9950)
* feat: android clipboard, multi-formats

Signed-off-by: fufesou <linlong1266@gmail.com>

* Chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-18 15:43:41 +08:00
21pages 06c7bc137f linux android use cpal (#9914)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-11-14 21:01:41 +08:00
fufesou 72a1f1161e refact: flutter 3.24.4 (#9874)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-10 11:18:08 +08:00
RustDesk 912f5265f1 Revert "Refact/flutter 3.24.4 (#9870)" (#9871)
This reverts commit 5eb2c31207.
2024-11-09 23:32:18 +08:00
5eb2c31207 Refact/flutter 3.24.4 (#9870)
* Update pubspec.lock

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: flutter 3.24.3

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: workaround Autocomplete options

Signed-off-by: fufesou <linlong1266@gmail.com>

* Replace engine with rustdesk custom flutter engine

* Update flutter-build.yml to use RustDesk flutter engine

* Fix the problem of missing extraction file directory windows-x64-release

* Update pubspec.lock.3.22.3

Signed-off-by: fufesou <linlong1266@gmail.com>

* remove pubspec.lock.3.22.3

Signed-off-by: fufesou <linlong1266@gmail.com>

* upgrade flutter android to 3.24.4

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: vitoway <vitoway@email.com>
Co-authored-by: vitoway <167743630+vitoway@users.noreply.github.com>
2024-11-09 20:51:47 +08:00
fufesou 7978e0301d fix: input mobile -> Android (#9767)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-08 12:11:56 +08:00
fufesou c5d3c7f390 Feat/android more actions (#8496)
* feat: android volume and power actions

Signed-off-by: fufesou <linlong1266@gmail.com>

* Add translations and refact action menus

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove divider

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: recover deleted translations

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-27 13:28:05 +08:00
fufesou bbf7d9e08a fix: android, no voice call under android 11 (#8440)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-22 08:10:54 +08:00
21pages 0f6538c1a7 add enable directx option, android software encoding half resolution option (#8435)
* add option enable directx capture screen, default true

Signed-off-by: 21pages <sunboeasy@gmail.com>

* option android software encoding half scale, check isStart flag

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-21 18:54:32 +08:00
fufesou 74cc5abd09 fix: android prompt "Failed to stop voice call" on conn ended (#8434)
* fix: android prompt "Failed to stop voice call" on conn ended

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove invalid comment

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better control of voice call status

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better voice call status control

Signed-off-by: fufesou <linlong1266@gmail.com>

* better end conn for voice call

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-06-21 16:43:54 +08:00
21pages ab451b9056 android keep screen on option (#8344)
* android keep screen on option

Keep screen on option relays on floating window.

Three options: Never, During controlled(default), During service is on

Signed-off-by: 21pages <sunboeasy@gmail.com>

* When rustdesk is in forground, be consistent with the settings

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-13 18:30:29 +08:00
21pages 64d0fb17f7 add floating window setting (#8279)
* Set `disable-floating-window` in client ui, it shows enabled when
  option is enabled and has floating window permission.
* Remove ignore battery setting because not work on every device.
* When the phone orientation changes, make the Y coordinate change
  proportionally, when changing back, the floating window position is still the original one.
* Add custom client option `floating-window-untouchable` to make the
  click event pass through the floating window automically. Set it untouchable automically when transparency is 0.
* On my phone, floating window size 16 no works and 32 works, so keep
  the size range [32, 320]

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-07 11:04:18 +08:00
21pages 9562768a04 android floating window (#8268)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-05 23:11:44 +08:00
rustdesk ce1dac3b86 attempt to fix local reference table overflow, https://github.com/rustdesk/rustdesk/issues/4118 2024-06-05 00:38:54 +08:00
21pages 036d10cfbe android: for software encoding and screen size > 1200, display size reduced to half (#8167)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-28 12:43:13 +08:00
21pages 49b0630752 fix android mediacodec encoding align (#8121)
* update ffmpeg, mediacodec encode align 64
* more d3d11 decode availablity check
* remove unused mediacodec info

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-05-22 20:18:09 +08:00
fufesou 0500bf070e refact: android audio input, voice call (#8037)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2024-05-14 09:20:27 +08:00
21pages d70b0cdd4f Not require both max-width and max-height of mediacodec larger than (#8036)
screen width and screen height

* Only use hardware codec, when api < 29, judge with codec name prefix.

Signed-off-by: 21pages <pages21@163.com>
2024-05-13 20:24:50 +08:00