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 config_plugins_1 = require("@expo/config-plugins"); 5const image_utils_1 = require("@expo/image-utils"); 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 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 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 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 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 var _a; 69 return ((_a = config.notification) === null || _a === void 0 ? void 0 : _a.icon) || null; 70} 71exports.getNotificationIcon = getNotificationIcon; 72function getNotificationColor(config) { 73 var _a; 74 return ((_a = config.notification) === null || _a === void 0 ? void 0 : _a.color) || null; 75} 76exports.getNotificationColor = getNotificationColor; 77function setNotificationIconColor(color, colors) { 78 return Colors.assignColorValue(colors, { 79 name: exports.NOTIFICATION_ICON_COLOR, 80 value: color, 81 }); 82} 83exports.setNotificationIconColor = setNotificationIconColor; 84/** 85 * Applies notification icon configuration for expo-notifications 86 */ 87async function setNotificationIconAsync(projectRoot, icon) { 88 if (icon) { 89 await writeNotificationIconImageFilesAsync(icon, projectRoot); 90 } 91 else { 92 removeNotificationIconImageFiles(projectRoot); 93 } 94} 95exports.setNotificationIconAsync = setNotificationIconAsync; 96function setNotificationConfig(props, manifest) { 97 const mainApplication = getMainApplicationOrThrow(manifest); 98 if (props.icon) { 99 addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource'); 100 } 101 else { 102 removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON); 103 } 104 if (props.color) { 105 addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource'); 106 } 107 else { 108 removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR); 109 } 110 return manifest; 111} 112async function writeNotificationIconImageFilesAsync(icon, projectRoot) { 113 await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName, scale }) => { 114 const drawableFolderName = folderName.replace('mipmap', 'drawable'); 115 const dpiFolderPath = path_1.resolve(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName); 116 if (!fs_1.existsSync(dpiFolderPath)) { 117 fs_1.mkdirSync(dpiFolderPath, { recursive: true }); 118 } 119 const iconSizePx = BASELINE_PIXEL_SIZE * scale; 120 try { 121 const resizedIcon = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-notification' }, { 122 src: icon, 123 width: iconSizePx, 124 height: iconSizePx, 125 resizeMode: 'cover', 126 backgroundColor: 'transparent', 127 })).source; 128 fs_1.writeFileSync(path_1.resolve(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'), resizedIcon); 129 } 130 catch (e) { 131 throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue resizing Android notification icon: ' + e); 132 } 133 })); 134} 135function removeNotificationIconImageFiles(projectRoot) { 136 Object.values(exports.dpiValues).forEach(async ({ folderName }) => { 137 const drawableFolderName = folderName.replace('mipmap', 'drawable'); 138 const dpiFolderPath = path_1.resolve(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName); 139 const iconFile = path_1.resolve(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'); 140 if (fs_1.existsSync(iconFile)) { 141 fs_1.unlinkSync(iconFile); 142 } 143 }); 144} 145/** 146 * Save sound files to `<project-root>/android/app/src/main/res/raw` 147 */ 148function setNotificationSounds(projectRoot, sounds) { 149 if (!Array.isArray(sounds)) { 150 throw new Error(ERROR_MSG_PREFIX + 151 `Must provide an array of sound files in your app config, found ${typeof sounds}.`); 152 } 153 for (const soundFileRelativePath of sounds) { 154 writeNotificationSoundFile(soundFileRelativePath, projectRoot); 155 } 156} 157exports.setNotificationSounds = setNotificationSounds; 158/** 159 * Copies the input file to the `<project-root>/android/app/src/main/res/raw` directory if 160 * there isn't already an existing file under that name. 161 */ 162function writeNotificationSoundFile(soundFileRelativePath, projectRoot) { 163 const rawResourcesPath = path_1.resolve(projectRoot, exports.ANDROID_RES_PATH, 'raw'); 164 const inputFilename = path_1.basename(soundFileRelativePath); 165 if (inputFilename) { 166 try { 167 const sourceFilepath = path_1.resolve(projectRoot, soundFileRelativePath); 168 const destinationFilepath = path_1.resolve(rawResourcesPath, inputFilename); 169 if (!fs_1.existsSync(rawResourcesPath)) { 170 fs_1.mkdirSync(rawResourcesPath, { recursive: true }); 171 } 172 fs_1.copyFileSync(sourceFilepath, destinationFilepath); 173 } 174 catch (e) { 175 throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue copying Android notification sounds: ' + e); 176 } 177 } 178} 179const withNotificationsAndroid = (config, { icon = null, color = null, sounds = [] }) => { 180 config = exports.withNotificationIconColor(config, { color }); 181 config = exports.withNotificationIcons(config, { icon }); 182 config = exports.withNotificationManifest(config, { icon, color }); 183 config = exports.withNotificationSounds(config, { sounds }); 184 return config; 185}; 186exports.withNotificationsAndroid = withNotificationsAndroid; 187