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