mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 15:20:55 +01:00
Merge pull request #5750 from BlueWallet/androidpermissions
FIX: Permission issues on newer Android APIs
This commit is contained in:
commit
c4b337fcf5
8 changed files with 81 additions and 83 deletions
|
@ -8,6 +8,8 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Frisbee from 'frisbee';
|
||||||
import { getApplicationName, getVersion, getSystemName, getSystemVersion, hasGmsSync, hasHmsSync } from 'react-native-device-info';
|
import { getApplicationName, getVersion, getSystemName, getSystemVersion, hasGmsSync, hasHmsSync } from 'react-native-device-info';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import loc from '../loc';
|
import loc from '../loc';
|
||||||
|
import { requestNotifications } from 'react-native-permissions';
|
||||||
|
|
||||||
const PushNotification = require('react-native-push-notification');
|
const PushNotification = require('react-native-push-notification');
|
||||||
const constants = require('./constants');
|
const constants = require('./constants');
|
||||||
|
@ -38,75 +39,80 @@ function Notifications(props) {
|
||||||
*/
|
*/
|
||||||
const configureNotifications = async function () {
|
const configureNotifications = async function () {
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
PushNotification.configure({
|
requestNotifications(['alert', 'sound', 'badge']).then(({ status, _ }) => {
|
||||||
// (optional) Called when Token is generated (iOS and Android)
|
if (status === 'granted') {
|
||||||
onRegister: async function (token) {
|
PushNotification.configure({
|
||||||
console.log('TOKEN:', token);
|
// (optional) Called when Token is generated (iOS and Android)
|
||||||
alreadyConfigured = true;
|
onRegister: async function (token) {
|
||||||
await _setPushToken(token);
|
console.log('TOKEN:', token);
|
||||||
resolve(true);
|
alreadyConfigured = true;
|
||||||
},
|
await _setPushToken(token);
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
|
||||||
// (required) Called when a remote is received or opened, or local notification is opened
|
// (required) Called when a remote is received or opened, or local notification is opened
|
||||||
onNotification: async function (notification) {
|
onNotification: async function (notification) {
|
||||||
// since we do not know whether we:
|
// since we do not know whether we:
|
||||||
// 1) received notification while app is in background (and storage is not decrypted so wallets are not loaded)
|
// 1) received notification while app is in background (and storage is not decrypted so wallets are not loaded)
|
||||||
// 2) opening this notification right now but storage is still unencrypted
|
// 2) opening this notification right now but storage is still unencrypted
|
||||||
// 3) any of the above but the storage is decrypted, and app wallets are loaded
|
// 3) any of the above but the storage is decrypted, and app wallets are loaded
|
||||||
//
|
//
|
||||||
// ...we save notification in internal notifications queue thats gona be processed later (on unsuspend with decrypted storage)
|
// ...we save notification in internal notifications queue thats gona be processed later (on unsuspend with decrypted storage)
|
||||||
|
|
||||||
const payload = Object.assign({}, notification, notification.data);
|
const payload = Object.assign({}, notification, notification.data);
|
||||||
if (notification.data && notification.data.data) Object.assign(payload, notification.data.data);
|
if (notification.data && notification.data.data) Object.assign(payload, notification.data.data);
|
||||||
delete payload.data;
|
delete payload.data;
|
||||||
// ^^^ weird, but sometimes payload data is not in `data` but in root level
|
// ^^^ weird, but sometimes payload data is not in `data` but in root level
|
||||||
console.log('got push notification', payload);
|
console.log('got push notification', payload);
|
||||||
|
|
||||||
await Notifications.addNotification(payload);
|
await Notifications.addNotification(payload);
|
||||||
|
|
||||||
// (required) Called when a remote is received or opened, or local notification is opened
|
// (required) Called when a remote is received or opened, or local notification is opened
|
||||||
notification.finish(PushNotificationIOS.FetchResult.NoData);
|
notification.finish(PushNotificationIOS.FetchResult.NoData);
|
||||||
|
|
||||||
// if user is staring at the app when he receives the notification we process it instantly
|
// if user is staring at the app when he receives the notification we process it instantly
|
||||||
// so app refetches related wallet
|
// so app refetches related wallet
|
||||||
if (payload.foreground) props.onProcessNotifications();
|
if (payload.foreground) props.onProcessNotifications();
|
||||||
},
|
},
|
||||||
|
|
||||||
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
|
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
|
||||||
onAction: function (notification) {
|
onAction: function (notification) {
|
||||||
console.log('ACTION:', notification.action);
|
console.log('ACTION:', notification.action);
|
||||||
console.log('NOTIFICATION:', notification);
|
console.log('NOTIFICATION:', notification);
|
||||||
|
|
||||||
// process the action
|
// process the action
|
||||||
},
|
},
|
||||||
|
|
||||||
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
|
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
|
||||||
onRegistrationError: function (err) {
|
onRegistrationError: function (err) {
|
||||||
console.error(err.message, err);
|
console.error(err.message, err);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
// IOS ONLY (optional): default: all - Permissions to register.
|
// IOS ONLY (optional): default: all - Permissions to register.
|
||||||
permissions: {
|
permissions: {
|
||||||
alert: true,
|
alert: true,
|
||||||
badge: true,
|
badge: true,
|
||||||
sound: true,
|
sound: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Should the initial notification be popped automatically
|
// Should the initial notification be popped automatically
|
||||||
// default: true
|
// default: true
|
||||||
popInitialNotification: true,
|
popInitialNotification: true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (optional) default: true
|
* (optional) default: true
|
||||||
* - Specified if permissions (ios) and token (android and ios) will requested or not,
|
* - Specified if permissions (ios) and token (android and ios) will requested or not,
|
||||||
* - if not, you must call PushNotificationsHandler.requestPermissions() later
|
* - if not, you must call PushNotificationsHandler.requestPermissions() later
|
||||||
* - if you are not using remote notification or do not have Firebase installed, use this:
|
* - if you are not using remote notification or do not have Firebase installed, use this:
|
||||||
* requestPermissions: Platform.OS === 'ios'
|
* requestPermissions: Platform.OS === 'ios'
|
||||||
*/
|
*/
|
||||||
requestPermissions: true,
|
requestPermissions: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// …
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.cleanUserOptOutFlag = async function () {
|
Notifications.cleanUserOptOutFlag = async function () {
|
||||||
|
@ -227,8 +233,7 @@ function Notifications(props) {
|
||||||
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
||||||
|
|
||||||
const api = new Frisbee({ baseURI });
|
const api = new Frisbee({ baseURI });
|
||||||
|
const postCall = await api.post(
|
||||||
return await api.post(
|
|
||||||
'/unsubscribe',
|
'/unsubscribe',
|
||||||
Object.assign({}, _getHeaders(), {
|
Object.assign({}, _getHeaders(), {
|
||||||
body: {
|
body: {
|
||||||
|
@ -240,6 +245,8 @@ function Notifications(props) {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
Notifications.abandonPermissions();
|
||||||
|
return postCall;
|
||||||
};
|
};
|
||||||
|
|
||||||
Notifications.isNotificationsEnabled = async function () {
|
Notifications.isNotificationsEnabled = async function () {
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
<key>WidgetsExtension.xcscheme_^#shared#^_</key>
|
<key>WidgetsExtension.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>144</integer>
|
<integer>99</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -16,7 +16,7 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
|
||||||
workspace 'BlueWallet'
|
workspace 'BlueWallet'
|
||||||
platform :ios, '13.0'
|
platform :ios, '13.0'
|
||||||
prepare_react_native_project!
|
prepare_react_native_project!
|
||||||
setup_permissions(['Camera'])
|
setup_permissions(['Camera', 'Notifications'])
|
||||||
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
||||||
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
|
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
|
||||||
#
|
#
|
||||||
|
|
|
@ -780,7 +780,7 @@ SPEC CHECKSUMS:
|
||||||
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
||||||
RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c
|
RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c
|
||||||
RNLocalize: dbea38dcb344bf80ff18a1757b1becf11f70cae4
|
RNLocalize: dbea38dcb344bf80ff18a1757b1becf11f70cae4
|
||||||
RNPermissions: eae8b97d8ab0587f082966ee608f47c97b2a349b
|
RNPermissions: e9e703e08dfe50cf4d2ca45852eb45c4ac98f7b7
|
||||||
RNPrivacySnapshot: 71919dde3c6a29dd332115409c2aec564afee8f4
|
RNPrivacySnapshot: 71919dde3c6a29dd332115409c2aec564afee8f4
|
||||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||||
RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a
|
RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a
|
||||||
|
@ -793,6 +793,6 @@ SPEC CHECKSUMS:
|
||||||
RNWatch: fd30ca40a5b5ef58dcbc195638e68219bc455236
|
RNWatch: fd30ca40a5b5ef58dcbc195638e68219bc455236
|
||||||
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9
|
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9
|
||||||
|
|
||||||
PODFILE CHECKSUM: 23bb5c319ccbedd7e109c44fe4273afce6efb48a
|
PODFILE CHECKSUM: 27db07925dc3a89e9ecced7e377a50dba9deb58a
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.13.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { TextInput, FlatList, Linking, TouchableOpacity, StyleSheet, Text, View, Platform, PermissionsAndroid, Alert } from 'react-native';
|
import { TextInput, FlatList, Linking, TouchableOpacity, StyleSheet, Text, View, Platform, Alert } from 'react-native';
|
||||||
import Clipboard from '@react-native-clipboard/clipboard';
|
import Clipboard from '@react-native-clipboard/clipboard';
|
||||||
import { Icon } from 'react-native-elements';
|
import { Icon } from 'react-native-elements';
|
||||||
import Share from 'react-native-share';
|
import Share from 'react-native-share';
|
||||||
|
@ -15,6 +15,7 @@ import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||||
import { isDesktop } from '../../blue_modules/environment';
|
import { isDesktop } from '../../blue_modules/environment';
|
||||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||||
import alert from '../../components/Alert';
|
import alert from '../../components/Alert';
|
||||||
|
import { PERMISSIONS, RESULTS, request } from 'react-native-permissions';
|
||||||
const bitcoin = require('bitcoinjs-lib');
|
const bitcoin = require('bitcoinjs-lib');
|
||||||
const currency = require('../../blue_modules/currency');
|
const currency = require('../../blue_modules/currency');
|
||||||
|
|
||||||
|
@ -68,15 +69,8 @@ const SendCreate = () => {
|
||||||
RNFS.unlink(filePath);
|
RNFS.unlink(filePath);
|
||||||
});
|
});
|
||||||
} else if (Platform.OS === 'android') {
|
} else if (Platform.OS === 'android') {
|
||||||
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
const granted = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
|
||||||
title: loc.send.permission_storage_title,
|
if (granted === RESULTS.GRANTED) {
|
||||||
message: loc.send.permission_storage_message,
|
|
||||||
buttonNeutral: loc.send.permission_storage_later,
|
|
||||||
buttonNegative: loc._.cancel,
|
|
||||||
buttonPositive: loc._.ok,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (granted === PermissionsAndroid.RESULTS.GRANTED || Platform.Version >= 33) {
|
|
||||||
console.log('Storage Permission: Granted');
|
console.log('Storage Permission: Granted');
|
||||||
const filePath = RNFS.DownloadDirectoryPath + `/${fileName}`;
|
const filePath = RNFS.DownloadDirectoryPath + `/${fileName}`;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
PermissionsAndroid,
|
|
||||||
InteractionManager,
|
InteractionManager,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
|
@ -45,6 +44,7 @@ import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electr
|
||||||
import alert from '../../components/Alert';
|
import alert from '../../components/Alert';
|
||||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||||
import { writeFileAndExport } from '../../blue_modules/fs';
|
import { writeFileAndExport } from '../../blue_modules/fs';
|
||||||
|
import { PERMISSIONS, RESULTS, request } from 'react-native-permissions';
|
||||||
|
|
||||||
const prompt = require('../../helpers/prompt');
|
const prompt = require('../../helpers/prompt');
|
||||||
|
|
||||||
|
@ -364,15 +364,8 @@ const WalletDetails = () => {
|
||||||
RNFS.unlink(filePath);
|
RNFS.unlink(filePath);
|
||||||
});
|
});
|
||||||
} else if (Platform.OS === 'android') {
|
} else if (Platform.OS === 'android') {
|
||||||
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
const granted = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
|
||||||
title: loc.send.permission_storage_title,
|
if (granted === RESULTS.GRANTED) {
|
||||||
message: loc.send.permission_storage_message,
|
|
||||||
buttonNeutral: loc.send.permission_storage_later,
|
|
||||||
buttonNegative: loc._.cancel,
|
|
||||||
buttonPositive: loc._.ok,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (granted === PermissionsAndroid.RESULTS.GRANTED || Platform.Version >= 33) {
|
|
||||||
console.log('Storage Permission: Granted');
|
console.log('Storage Permission: Granted');
|
||||||
const filePath = RNFS.DownloadDirectoryPath + `/${fileName}`;
|
const filePath = RNFS.DownloadDirectoryPath + `/${fileName}`;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -50,6 +50,8 @@ jest.mock('@react-native-community/push-notification-ios', () => {
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('react-native-permissions', () => require('react-native-permissions/mock'));
|
||||||
|
|
||||||
jest.mock('react-native-device-info', () => {
|
jest.mock('react-native-device-info', () => {
|
||||||
return {
|
return {
|
||||||
getUniqueId: jest.fn().mockReturnValue('uniqueId'),
|
getUniqueId: jest.fn().mockReturnValue('uniqueId'),
|
||||||
|
|
Loading…
Add table
Reference in a new issue