react-native-app-data-sharing
Version:
This React Native package facilitates seamless and secure data sharing between applications on the same device.
190 lines (165 loc) • 5.78 kB
text/typescript
import {
createRunOncePlugin,
withAndroidManifest,
type AndroidManifest,
type ConfigPlugin,
} from '@expo/config-plugins';
import { type ManifestApplication } from '@expo/config-plugins/build/android/Manifest';
/**
* Interface for the plugin config
*/
interface ConfigProps {
appsBundleIds: string[];
}
/**
* Interface for the manifest
*/
type NewManifestApplication = ManifestApplication & {
provider?: any;
};
/**
* Interface for the plugin
*/
type DataShareConfig = ConfigPlugin<ConfigProps>;
/**
* Adds custom `<queries>` entries to the AndroidManifest.xml.
* This function ensures that a `<package>` array exists within `<queries>`.
* It adds specified package names to the `<queries>` section, avoiding duplicates.
*
* @param {AndroidManifest} androidManifest - The AndroidManifest.xml as a JavaScript object.
* @param {string[]} packages - Array of package names to add to the `<queries>` section.
* @returns {AndroidManifest} - The modified AndroidManifest.xml.
*/
const addCustomQueries = (
androidManifest: AndroidManifest,
packages: string[]
) => {
if (!androidManifest.manifest.queries) {
androidManifest.manifest.queries = [];
}
// Ensure `package` array exists inside `queries`
if (!androidManifest.manifest.queries.some((q) => q.package)) {
androidManifest.manifest.queries.push({ package: [] });
}
const defaultPackages = ['${applicationId}'];
const allPackages = [...new Set([...defaultPackages, ...packages])];
const packageQuery = androidManifest.manifest.queries.find((q) => q.package);
const existingPackages = new Set(
packageQuery?.package?.map((pkg) => pkg.$?.['android:name'])
);
const newPackages = allPackages
.filter((pkg) => !existingPackages.has(pkg))
.map((pkg) => ({ $: { 'android:name': pkg } }));
packageQuery?.package?.push(...newPackages);
return androidManifest;
};
/**
* Helper function to add permissions dynamically.
* @param {object} androidManifest - The AndroidManifest.xml as a JavaScript object.
* @param {string[]} permissions - Array of permissions to add.
*/
const addUsesPermissions = (
androidManifest: AndroidManifest,
permissions: string[]
) => {
if (!androidManifest.manifest['uses-permission']) {
androidManifest.manifest['uses-permission'] = [];
}
// Check if the permission already exists
const existingUsesPermissions = androidManifest.manifest[
'uses-permission'
].map((p) => {
return p.$['android:name'];
});
// Add the permission if it doesn't exist
permissions.forEach((permission) => {
if (
!existingUsesPermissions.includes(
`${permission}.permission.READ_SHARED_PREFS`
)
) {
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': `${permission}.permission.READ_SHARED_PREFS`,
},
});
}
});
return androidManifest;
};
/**
* Helper function to add custom `<permission>` and `<provider>` entries.
* @param {object} androidManifest - The AndroidManifest.xml as a JavaScript object.
*/
const addCustomEntriesToManifest = (androidManifest: AndroidManifest) => {
const appIdPlaceholder = '${applicationId}';
const manifestApplication: NewManifestApplication[] | undefined =
androidManifest.manifest.application;
// Define the custom permission
const permission = {
$: {
'android:name': `${appIdPlaceholder}.permission.READ_SHARED_PREFS`,
'android:protectionLevel': 'normal',
},
};
// Add the permission to <manifest>
if (!androidManifest.manifest.permission) {
androidManifest.manifest.permission = [];
}
// Check if the permission already exists
const existingPermissions = androidManifest.manifest.permission.map(
(p) => p.$['android:name']
);
// Add the permission if it doesn't exist
if (!existingPermissions.includes(permission.$['android:name'])) {
androidManifest.manifest.permission.push(permission);
}
// Add the provider entry to <application>
const provider = {
$: {
'android:name': 'com.data.SharedPreferencesProvider',
'android:authorities': `${appIdPlaceholder}.sharedpreferencesprovider`,
'android:permission': `${appIdPlaceholder}.permission.READ_SHARED_PREFS`,
'android:enabled': 'true',
'android:exported': 'true',
},
};
// Add the provider to <application>
if (!manifestApplication?.[0]?.provider) {
//@ts-ignore
manifestApplication[0].provider = [];
}
// Check if the provider already exists
const existingProviders = manifestApplication?.[0]?.provider.map(
(p: any) => p.$['android:name']
);
// Add the provider if it doesn't exist
if (!existingProviders.includes(provider.$['android:name'])) {
manifestApplication?.[0]?.provider.push(provider);
}
return androidManifest;
};
/**
* Main Expo plugin function to modify the AndroidManifest.xml.
* @param {object} config - The Expo app configuration.
* @param {object} options - Plugin options (e.g., permissions array).
*/
const withCustomManifest: DataShareConfig = (config, props) => {
const { appsBundleIds = [] } = props;
return withAndroidManifest(config, (ownConfig) => {
ownConfig.modResults = addUsesPermissions(ownConfig.modResults, [
...new Set(appsBundleIds),
]);
ownConfig.modResults = addCustomEntriesToManifest(ownConfig.modResults);
ownConfig.modResults = addCustomQueries(ownConfig.modResults, [
...new Set(appsBundleIds),
]);
return ownConfig;
});
};
const withDataSharing: DataShareConfig = (config, props) => {
config = withCustomManifest(config, props);
return config;
};
const pak = require('react-native-app-data-sharing/package.json');
export default createRunOncePlugin(withDataSharing, pak.name, pak.version);