1"use strict"; 2Object.defineProperty(exports, "__esModule", { value: true }); 3exports.withNotificationsAndroid = exports.setNotificationSounds = exports.setNotificationIconAsync = exports.setNotificationIconColor = exports.getNotificationColor = exports.getNotificationIcon = exports.withNotificationSounds = exports.withNotificationManifest = exports.withNotificationIconColor = exports.withNotificationIcons = exports.NOTIFICATION_ICON_COLOR_RESOURCE = exports.NOTIFICATION_ICON_COLOR = exports.NOTIFICATION_ICON_RESOURCE = exports.NOTIFICATION_ICON = exports.META_DATA_NOTIFICATION_ICON_COLOR = exports.META_DATA_NOTIFICATION_ICON = exports.dpiValues = exports.ANDROID_RES_PATH = void 0; 4const image_utils_1 = require("@expo/image-utils"); 5const config_plugins_1 = require("expo/config-plugins"); 6const fs_1 = require("fs"); 7const path_1 = require("path"); 8const { Colors } = config_plugins_1.AndroidConfig; 9exports.ANDROID_RES_PATH = 'android/app/src/main/res/'; 10exports.dpiValues = { 11 mdpi: { folderName: 'mipmap-mdpi', scale: 1 }, 12 hdpi: { folderName: 'mipmap-hdpi', scale: 1.5 }, 13 xhdpi: { folderName: 'mipmap-xhdpi', scale: 2 }, 14 xxhdpi: { folderName: 'mipmap-xxhdpi', scale: 3 }, 15 xxxhdpi: { folderName: 'mipmap-xxxhdpi', scale: 4 }, 16}; 17const { addMetaDataItemToMainApplication, getMainApplicationOrThrow, removeMetaDataItemFromMainApplication, } = config_plugins_1.AndroidConfig.Manifest; 18const BASELINE_PIXEL_SIZE = 24; 19const ERROR_MSG_PREFIX = 'An error occurred while configuring Android notifications. '; 20exports.META_DATA_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon'; 21exports.META_DATA_NOTIFICATION_ICON_COLOR = 'expo.modules.notifications.default_notification_color'; 22exports.NOTIFICATION_ICON = 'notification_icon'; 23exports.NOTIFICATION_ICON_RESOURCE = `@drawable/${exports.NOTIFICATION_ICON}`; 24exports.NOTIFICATION_ICON_COLOR = 'notification_icon_color'; 25exports.NOTIFICATION_ICON_COLOR_RESOURCE = `@color/${exports.NOTIFICATION_ICON_COLOR}`; 26const withNotificationIcons = (config, { icon }) => { 27 // If no icon provided in the config plugin props, fallback to value from app.json 28 icon = icon || getNotificationIcon(config); 29 return (0, config_plugins_1.withDangerousMod)(config, [ 30 'android', 31 async (config) => { 32 await setNotificationIconAsync(config.modRequest.projectRoot, icon); 33 return config; 34 }, 35 ]); 36}; 37exports.withNotificationIcons = withNotificationIcons; 38const withNotificationIconColor = (config, { color }) => { 39 // If no color provided in the config plugin props, fallback to value from app.json 40 return (0, config_plugins_1.withAndroidColors)(config, (config) => { 41 color = color || getNotificationColor(config); 42 config.modResults = setNotificationIconColor(color, config.modResults); 43 return config; 44 }); 45}; 46exports.withNotificationIconColor = withNotificationIconColor; 47const withNotificationManifest = (config, { icon, color }) => { 48 // If no icon or color provided in the config plugin props, fallback to value from app.json 49 icon = icon || getNotificationIcon(config); 50 color = color || getNotificationColor(config); 51 return (0, config_plugins_1.withAndroidManifest)(config, (config) => { 52 config.modResults = setNotificationConfig({ icon, color }, config.modResults); 53 return config; 54 }); 55}; 56exports.withNotificationManifest = withNotificationManifest; 57const withNotificationSounds = (config, { sounds }) => { 58 return (0, config_plugins_1.withDangerousMod)(config, [ 59 'android', 60 (config) => { 61 setNotificationSounds(config.modRequest.projectRoot, sounds); 62 return config; 63 }, 64 ]); 65}; 66exports.withNotificationSounds = withNotificationSounds; 67function getNotificationIcon(config) { 68 return config.notification?.icon || null; 69} 70exports.getNotificationIcon = getNotificationIcon; 71function getNotificationColor(config) { 72 return config.notification?.color || null; 73} 74exports.getNotificationColor = getNotificationColor; 75function setNotificationIconColor(color, colors) { 76 return Colors.assignColorValue(colors, { 77 name: exports.NOTIFICATION_ICON_COLOR, 78 value: color, 79 }); 80} 81exports.setNotificationIconColor = setNotificationIconColor; 82/** 83 * Applies notification icon configuration for expo-notifications 84 */ 85async function setNotificationIconAsync(projectRoot, icon) { 86 if (icon) { 87 await writeNotificationIconImageFilesAsync(icon, projectRoot); 88 } 89 else { 90 removeNotificationIconImageFiles(projectRoot); 91 } 92} 93exports.setNotificationIconAsync = setNotificationIconAsync; 94function setNotificationConfig(props, manifest) { 95 const mainApplication = getMainApplicationOrThrow(manifest); 96 if (props.icon) { 97 addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource'); 98 } 99 else { 100 removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON); 101 } 102 if (props.color) { 103 addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource'); 104 } 105 else { 106 removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR); 107 } 108 return manifest; 109} 110async function writeNotificationIconImageFilesAsync(icon, projectRoot) { 111 await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName, scale }) => { 112 const drawableFolderName = folderName.replace('mipmap', 'drawable'); 113 const dpiFolderPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName); 114 if (!(0, fs_1.existsSync)(dpiFolderPath)) { 115 (0, fs_1.mkdirSync)(dpiFolderPath, { recursive: true }); 116 } 117 const iconSizePx = BASELINE_PIXEL_SIZE * scale; 118 try { 119 const resizedIcon = (await (0, image_utils_1.generateImageAsync)({ projectRoot, cacheType: 'android-notification' }, { 120 src: icon, 121 width: iconSizePx, 122 height: iconSizePx, 123 resizeMode: 'cover', 124 backgroundColor: 'transparent', 125 })).source; 126 (0, fs_1.writeFileSync)((0, path_1.resolve)(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'), resizedIcon); 127 } 128 catch (e) { 129 throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue resizing Android notification icon: ' + e); 130 } 131 })); 132} 133function removeNotificationIconImageFiles(projectRoot) { 134 Object.values(exports.dpiValues).forEach(async ({ folderName }) => { 135 const drawableFolderName = folderName.replace('mipmap', 'drawable'); 136 const dpiFolderPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName); 137 const iconFile = (0, path_1.resolve)(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'); 138 if ((0, fs_1.existsSync)(iconFile)) { 139 (0, fs_1.unlinkSync)(iconFile); 140 } 141 }); 142} 143/** 144 * Save sound files to `<project-root>/android/app/src/main/res/raw` 145 */ 146function setNotificationSounds(projectRoot, sounds) { 147 if (!Array.isArray(sounds)) { 148 throw new Error(ERROR_MSG_PREFIX + 149 `Must provide an array of sound files in your app config, found ${typeof sounds}.`); 150 } 151 for (const soundFileRelativePath of sounds) { 152 writeNotificationSoundFile(soundFileRelativePath, projectRoot); 153 } 154} 155exports.setNotificationSounds = setNotificationSounds; 156/** 157 * Copies the input file to the `<project-root>/android/app/src/main/res/raw` directory if 158 * there isn't already an existing file under that name. 159 */ 160function writeNotificationSoundFile(soundFileRelativePath, projectRoot) { 161 const rawResourcesPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, 'raw'); 162 const inputFilename = (0, path_1.basename)(soundFileRelativePath); 163 if (inputFilename) { 164 try { 165 const sourceFilepath = (0, path_1.resolve)(projectRoot, soundFileRelativePath); 166 const destinationFilepath = (0, path_1.resolve)(rawResourcesPath, inputFilename); 167 if (!(0, fs_1.existsSync)(rawResourcesPath)) { 168 (0, fs_1.mkdirSync)(rawResourcesPath, { recursive: true }); 169 } 170 (0, fs_1.copyFileSync)(sourceFilepath, destinationFilepath); 171 } 172 catch (e) { 173 throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue copying Android notification sounds: ' + e); 174 } 175 } 176} 177const withNotificationsAndroid = (config, { icon = null, color = null, sounds = [] }) => { 178 config = (0, exports.withNotificationIconColor)(config, { color }); 179 config = (0, exports.withNotificationIcons)(config, { icon }); 180 config = (0, exports.withNotificationManifest)(config, { icon, color }); 181 config = (0, exports.withNotificationSounds)(config, { sounds }); 182 return config; 183}; 184exports.withNotificationsAndroid = withNotificationsAndroid; 185