Skip to content

Commit c4ef40c

Browse files
fix(android): share state logs as file attachment instead of path string (#33281)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> On Android, **Download State Logs** was sharing only a file path string instead of a downloadable `.json` file. Support reproduced this on 8.2.0 (build 6019, Pixel 9a, Android 16) — tracked in [MCWP-718](https://consensyssoftware.atlassian.net/browse/MCWP-718). **Root cause:** `downloadStateLogs` passed a plain filesystem path to `react-native-share`. On Android, the library only treats `file://` or `content://` URLs as file attachments; plain paths are shared via `Intent.EXTRA_TEXT` as text. This matches the "path string only" symptom reported in support. **Fix:** 1. Share state logs with a `file://` URI (same pattern already used in Experimental Settings). 2. Add `<files-path>` to `filepaths.xml` so FileProvider can expose files written to `RNFS.DocumentDirectoryPath`. Unit tests updated to assert the new share URL format. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Fixed an issue where Download State Logs on Android shared only a file path string instead of a downloadable JSON file. ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: [MCWP-718](https://consensyssoftware.atlassian.net/browse/MCWP-718) Refs: [#24359](#24359), [MCWP-553](https://consensyssoftware.atlassian.net/browse/MCWP-553) ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Download State Logs on Android Scenario: user downloads state logs and can save the JSON file Given I am on an Android device with MetaMask installed and unlocked And I navigate to Settings → Advanced When I tap "Download State Logs" And I choose a save target (e.g. Save to Files, Google Drive, or Gmail attachment) Then the share sheet offers file-save targets (not just messaging apps) And the shared content is a valid .json file attachment And the file is not a plain filesystem path string ``` **Tested on:** Physical Android device — verified on a dev build; share sheet offers save targets and produces a valid `.json` file attachment (no path string) ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — fix verified manually on a physical Android device. Before screenshot available in the support Slack thread / [MCWP-718](https://consensyssoftware.atlassian.net/browse/MCWP-718). ### **Before** Support screenshot (path string only): Slack thread in [#metamask-support](https://consensys.slack.com/archives/C8RSKCNCD/p1783643040.893449) https://github.com/user-attachments/assets/0f48ac6b-b103-40ae-a3e3-8c66484f7b8e ### **After** https://github.com/user-attachments/assets/4b2f27d0-4c13-4954-805e-5715a84ffc55 ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [MCWP-718]: https://consensyssoftware.atlassian.net/browse/MCWP-718?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 6411d21 commit c4ef40c

3 files changed

Lines changed: 7 additions & 6 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<paths xmlns:android="http://schemas.android.com/apk/res/android">
33
<external-path name="data" path="Download/" />
4+
<files-path name="internal_files" path="." />
45
</paths>

app/util/logs/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ describe('logs :: downloadStateLogs', () => {
785785
expect(Share.open).toHaveBeenCalledWith({
786786
subject: 'TestApp State logs - v1.0.0 (100)',
787787
title: 'TestApp State logs - v1.0.0 (100)',
788-
url: '/mock/path/state-logs-v1.0.0-(100).json',
788+
url: 'file:///mock/path/state-logs-v1.0.0-(100).json',
789789
filename: 'state-logs-v1.0.0-(100).json',
790790
type: 'application/json',
791791
failOnCancel: false,
@@ -822,7 +822,7 @@ describe('logs :: downloadStateLogs', () => {
822822
expect(Share.open).toHaveBeenCalledWith({
823823
subject: 'TestApp State logs - v1.0.0 (100)',
824824
title: 'TestApp State logs - v1.0.0 (100)',
825-
url: '/mock/path/state-logs-v1.0.0-(100).json',
825+
url: 'file:///mock/path/state-logs-v1.0.0-(100).json',
826826
filename: 'state-logs-v1.0.0-(100).json',
827827
type: 'application/json',
828828
failOnCancel: false,

app/util/logs/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,13 @@ export const downloadStateLogs = async (
164164
// a filename and JSON mime type lets Android offer "Save to Files"/Drive.
165165
await RNFS.writeFile(path, stateLogsWithReleaseDetails, 'utf8');
166166

167-
// Pass the plain file path (no `file://` scheme). react-native-share
168-
// copies it into its own FileProvider and shares a `content://` URI;
169-
// passing a raw `file://` URI crashes Android with FileUriExposedException.
167+
// Pass a `file://` URI so react-native-share treats this as a file
168+
// attachment on Android (plain paths are shared as EXTRA_TEXT). The
169+
// library converts the path to a `content://` URI via FileProvider.
170170
await Share.open({
171171
subject: `${appName} State logs - v${appVersion} (${buildNumber})`,
172172
title: `${appName} State logs - v${appVersion} (${buildNumber})`,
173-
url: path,
173+
url: `file://${path}`,
174174
filename,
175175
type: 'application/json',
176176
failOnCancel: false,

0 commit comments

Comments
 (0)