fix: call media permissions in android 13+ (#5326)

* add android permissions

* create askAndroidMediaPermissions helper

* ask android permission

* fix i18n import

* remove useless const

* wip

* revert login changes
This commit is contained in:
Gleidson Daniel Silva 2023-11-20 13:07:33 -03:00 committed by GitHub
parent 0a75a6615c
commit 6a1205afcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 12 deletions

View File

@ -17,6 +17,11 @@
<!-- android 13 notifications -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- android 13 media permission -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<application
android:name="chat.rocket.reactnative.MainApplication"
android:allowBackup="false"

View File

@ -0,0 +1,54 @@
import { Permission, PermissionsAndroid, Platform, Rationale } from 'react-native';
import i18n from '../../../i18n';
// Define a type for the permissions map
type PermissionsMap = { [key: string]: string };
/**
* Rationale for requesting read permissions on Android.
*/
const readExternalStorageRationale: Rationale = {
title: i18n.t('Read_External_Permission'),
message: i18n.t('Read_External_Permission_Message'),
buttonPositive: i18n.t('Ok')
};
/**
* Checks if all requested permissions are granted.
*
* @param {PermissionsMap} permissionsStatus - The object containing the statuses of the permissions.
* @param {string[]} permissions - The list of permissions to check.
* @return {boolean} Whether all permissions are granted.
*/
const areAllPermissionsGranted = (permissionsStatus: PermissionsMap, permissions: string[]): boolean =>
permissions.every(permission => permissionsStatus[permission] === PermissionsAndroid.RESULTS.GRANTED);
/**
* Requests permission for reading media on Android.
*
* @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the permissions were granted.
*/
export const askAndroidMediaPermissions = async (): Promise<boolean> => {
if (Platform.OS !== 'android') return true;
// For Android versions that require the new permissions model (API Level >= 33)
if (Platform.constants.Version >= 33) {
const permissions = [
'android.permission.READ_MEDIA_IMAGES',
'android.permission.READ_MEDIA_VIDEO',
'android.permission.READ_MEDIA_AUDIO'
];
const permissionsStatus = await PermissionsAndroid.requestMultiple(permissions as Permission[]);
return areAllPermissionsGranted(permissionsStatus, permissions);
}
// For older Android versions
const result = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
readExternalStorageRationale
);
return result === PermissionsAndroid.RESULTS.GRANTED;
};

View File

@ -15,3 +15,4 @@ export * from './url';
export * from './isValidEmail';
export * from './random';
export * from './image';
export * from './askAndroidMediaPermissions';

View File

@ -1,6 +1,6 @@
import React from 'react';
import { StackNavigationProp } from '@react-navigation/stack';
import { BackHandler, FlatList, Keyboard, PermissionsAndroid, ScrollView, Text, View, Rationale } from 'react-native';
import { BackHandler, FlatList, Keyboard, ScrollView, Text, View } from 'react-native';
import ShareExtension from 'rn-extensions-share';
import * as FileSystem from 'expo-file-system';
import { connect } from 'react-redux';
@ -24,7 +24,7 @@ import styles from './styles';
import ShareListHeader from './Header';
import { TServerModel, TSubscriptionModel } from '../../definitions';
import { ShareInsideStackParamList } from '../../definitions/navigationTypes';
import { getRoomAvatar, isAndroid, isIOS } from '../../lib/methods/helpers';
import { getRoomAvatar, isAndroid, isIOS, askAndroidMediaPermissions } from '../../lib/methods/helpers';
interface IDataFromShare {
value: string;
@ -63,12 +63,6 @@ interface IShareListViewProps extends INavigationOption {
theme: TSupportedThemes;
}
const permission: Rationale = {
title: I18n.t('Read_External_Permission'),
message: I18n.t('Read_External_Permission_Message'),
buttonPositive: 'Ok'
};
const getItemLayout = (data: any, index: number) => ({ length: data.length, offset: ROW_HEIGHT * index, index });
const keyExtractor = (item: TSubscriptionModel) => item.rid;
@ -282,8 +276,8 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
askForPermission = async (data: IDataFromShare[]) => {
const mediaIndex = data.findIndex(item => item.type === 'media');
if (mediaIndex !== -1) {
const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE, permission);
if (result !== PermissionsAndroid.RESULTS.GRANTED) {
const result = await askAndroidMediaPermissions();
if (!result) {
this.setState({ needsPermission: true });
return Promise.reject();
}
@ -441,8 +435,8 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
style={{ backgroundColor: themes[theme].backgroundColor }}
contentContainerStyle={[styles.container, styles.centered, { backgroundColor: themes[theme].backgroundColor }]}
>
<Text style={[styles.permissionTitle, { color: themes[theme].titleText }]}>{permission.title}</Text>
<Text style={[styles.permissionMessage, { color: themes[theme].bodyText }]}>{permission.message}</Text>
<Text style={[styles.permissionTitle, { color: themes[theme].titleText }]}>{I18n.t('Read_External_Permission')}</Text>
<Text style={[styles.permissionMessage, { color: themes[theme].bodyText }]}>{I18n.t('Read_External_Permission_Message')}</Text>
</ScrollView>
</SafeAreaView>
);