Merge pull request #7051 from BlueWallet/saveandroid

FIX: FIle save on Android 13+
This commit is contained in:
GLaDOS 2024-09-15 17:24:09 +00:00 committed by GitHub
commit 6dd7cad5ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 66 additions and 82 deletions

View file

@ -1,10 +1,10 @@
import LocalQRCode from '@remobile/react-native-qrcode-local-image'; import LocalQRCode from '@remobile/react-native-qrcode-local-image';
import { Alert, Linking, PermissionsAndroid, Platform } from 'react-native'; import { Alert, Linking, Platform } from 'react-native';
import DocumentPicker from 'react-native-document-picker'; import DocumentPicker from 'react-native-document-picker';
import RNFS from 'react-native-fs'; import RNFS from 'react-native-fs';
import { launchImageLibrary } from 'react-native-image-picker'; import { launchImageLibrary } from 'react-native-image-picker';
import Share from 'react-native-share'; import Share from 'react-native-share';
import { request, PERMISSIONS } from 'react-native-permissions';
import presentAlert from '../components/Alert'; import presentAlert from '../components/Alert';
import loc from '../loc'; import loc from '../loc';
import { isDesktop } from './environment'; import { isDesktop } from './environment';
@ -39,6 +39,7 @@ const _shareOpen = async (filePath: string, showShareDialog: boolean = false) =>
* Writes a file to fs, and triggers an OS sharing dialog, so user can decide where to put this file (share to cloud * Writes a file to fs, and triggers an OS sharing dialog, so user can decide where to put this file (share to cloud
* or perhabs messaging app). Provided filename should be just a file name, NOT a path * or perhabs messaging app). Provided filename should be just a file name, NOT a path
*/ */
export const writeFileAndExport = async function (fileName: string, contents: string, showShareDialog: boolean = true) { export const writeFileAndExport = async function (fileName: string, contents: string, showShareDialog: boolean = true) {
const sanitizedFileName = _sanitizeFileName(fileName); const sanitizedFileName = _sanitizeFileName(fileName);
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
@ -46,31 +47,22 @@ export const writeFileAndExport = async function (fileName: string, contents: st
await RNFS.writeFile(filePath, contents); await RNFS.writeFile(filePath, contents);
await _shareOpen(filePath, showShareDialog); await _shareOpen(filePath, showShareDialog);
} else if (Platform.OS === 'android') { } else if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, { const isAndroidVersion33OrAbove = Platform.Version >= 33;
title: loc.send.permission_storage_title, const permissionType = isAndroidVersion33OrAbove ? PERMISSIONS.ANDROID.READ_MEDIA_IMAGES : PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE;
message: loc.send.permission_storage_message, request(permissionType).then(async result => {
buttonNeutral: loc.send.permission_storage_later, if (result === 'granted') {
buttonNegative: loc._.cancel, const filePath = RNFS.ExternalDirectoryPath + `/${sanitizedFileName}`;
buttonPositive: loc._.ok,
});
// In Android 13 no WRITE_EXTERNAL_STORAGE permission is needed
// @see https://stackoverflow.com/questions/76311685/permissionandroid-request-always-returns-never-ask-again-without-any-prompt-r
if (granted === PermissionsAndroid.RESULTS.GRANTED || Platform.Version >= 30) {
const filePath = RNFS.DownloadDirectoryPath + `/${sanitizedFileName}`;
try { try {
await RNFS.writeFile(filePath, contents); await RNFS.writeFile(filePath, contents);
console.log(`file saved to ${filePath}`);
if (showShareDialog) { if (showShareDialog) {
await _shareOpen(filePath); await _shareOpen(filePath);
} else { } else {
presentAlert({ message: loc.formatString(loc.send.file_saved_at_path, { fileName: sanitizedFileName }) }); presentAlert({ message: loc.formatString(loc.send.file_saved_at_path, { filePath }) });
} }
} catch (e: any) { } catch (e: any) {
console.log(e); presentAlert({ message: e.message });
} }
} else { } else {
console.log('Storage Permission: Denied');
Alert.alert(loc.send.permission_storage_title, loc.send.permission_storage_denied_message, [ Alert.alert(loc.send.permission_storage_title, loc.send.permission_storage_denied_message, [
{ {
text: loc.send.open_settings, text: loc.send.open_settings,
@ -82,8 +74,7 @@ export const writeFileAndExport = async function (fileName: string, contents: st
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' }, { text: loc._.cancel, onPress: () => {}, style: 'cancel' },
]); ]);
} }
} else { });
presentAlert({ message: 'Not implemented for this platform' });
} }
}; };

View file

@ -178,8 +178,6 @@
"permission_camera_message": "We need your permission to use your camera.", "permission_camera_message": "We need your permission to use your camera.",
"psbt_sign": "Sign a transaction", "psbt_sign": "Sign a transaction",
"open_settings": "Open Settings", "open_settings": "Open Settings",
"permission_storage_later": "Ask me later.",
"permission_storage_message": "BlueWallet needs your permission to access your storage to save this file.",
"permission_storage_denied_message": "BlueWallet is unable to save this file. Please open your device settings and enable Storage Permission.", "permission_storage_denied_message": "BlueWallet is unable to save this file. Please open your device settings and enable Storage Permission.",
"permission_storage_title": "Storage Access Permission", "permission_storage_title": "Storage Access Permission",
"psbt_clipboard": "Copy to Clipboard", "psbt_clipboard": "Copy to Clipboard",
@ -193,8 +191,8 @@
"reset_amount": "Reset Amount", "reset_amount": "Reset Amount",
"reset_amount_confirm": "Would you like to reset the amount?", "reset_amount_confirm": "Would you like to reset the amount?",
"success_done": "Done", "success_done": "Done",
"txSaved": "The transaction file ({filePath}) has been saved in your Downloads folder.", "txSaved": "The transaction file ({filePath}) has been saved.",
"file_saved_at_path": "The file ({fileName}) has been saved in your Downloads folder.", "file_saved_at_path": "The file ({filePath}) has been saved.",
"cant_send_to_silentpayment_adress": "This wallet cannot send to SilentPayment addresses", "cant_send_to_silentpayment_adress": "This wallet cannot send to SilentPayment addresses",
"cant_send_to_bip47": "This wallet cannot send to BIP47 payment codes", "cant_send_to_bip47": "This wallet cannot send to BIP47 payment codes",
"cant_find_bip47_notification": "Add this Payment Code to contacts first", "cant_find_bip47_notification": "Add this Payment Code to contacts first",

View file

@ -11,7 +11,7 @@ import * as BlueElectrum from '../../blue_modules/BlueElectrum';
import * as encryption from '../../blue_modules/encryption'; import * as encryption from '../../blue_modules/encryption';
import * as fs from '../../blue_modules/fs'; import * as fs from '../../blue_modules/fs';
import ecc from '../../blue_modules/noble_ecc'; import ecc from '../../blue_modules/noble_ecc';
import { BlueCard, BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents'; import { BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
import { import {
HDAezeedWallet, HDAezeedWallet,
HDSegwitBech32Wallet, HDSegwitBech32Wallet,
@ -22,7 +22,6 @@ import {
} from '../../class'; } from '../../class';
import presentAlert from '../../components/Alert'; import presentAlert from '../../components/Alert';
import Button from '../../components/Button'; import Button from '../../components/Button';
import SafeArea from '../../components/SafeArea';
import SaveFileButton from '../../components/SaveFileButton'; import SaveFileButton from '../../components/SaveFileButton';
import loc from '../../loc'; import loc from '../../loc';
@ -285,17 +284,14 @@ export default class SelfTest extends Component {
} }
render() { render() {
if (this.state.isLoading) {
return <BlueLoading />;
}
return ( return (
<SafeArea> <ScrollView automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
<BlueCard>
<ScrollView>
<BlueSpacing20 /> <BlueSpacing20 />
{(() => { {this.state.isLoading ? (
<BlueLoading />
) : (
(() => {
if (this.state.isOk) { if (this.state.isOk) {
return ( return (
<View style={styles.center}> <View style={styles.center}>
@ -315,7 +311,8 @@ export default class SelfTest extends Component {
</View> </View>
); );
} }
})()} })()
)}
<BlueSpacing20 /> <BlueSpacing20 />
<SaveFileButton fileName="bluewallet-selftest.txt" fileContent={'Success on ' + new Date().toUTCString()}> <SaveFileButton fileName="bluewallet-selftest.txt" fileContent={'Success on ' + new Date().toUTCString()}>
<Button title="Test Save to Storage" /> <Button title="Test Save to Storage" />
@ -323,8 +320,6 @@ export default class SelfTest extends Component {
<BlueSpacing20 /> <BlueSpacing20 />
<Button title="Test File Import" onPress={this.onPressImportDocument} /> <Button title="Test File Import" onPress={this.onPressImportDocument} />
</ScrollView> </ScrollView>
</BlueCard>
</SafeArea>
); );
} }
} }