1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.ANDROID_RES_PATH = void 0;
7exports.configureAdaptiveIconAsync = configureAdaptiveIconAsync;
8exports.dpiValues = exports.createAdaptiveIconXmlString = void 0;
9exports.getAdaptiveIcon = getAdaptiveIcon;
10exports.getIcon = getIcon;
11exports.setIconAsync = setIconAsync;
12exports.setRoundIconManifest = setRoundIconManifest;
13exports.withAndroidIcons = void 0;
14function _configPlugins() {
15  const data = require("@expo/config-plugins");
16  _configPlugins = function () {
17    return data;
18  };
19  return data;
20}
21function _imageUtils() {
22  const data = require("@expo/image-utils");
23  _imageUtils = function () {
24    return data;
25  };
26  return data;
27}
28function _fsExtra() {
29  const data = _interopRequireDefault(require("fs-extra"));
30  _fsExtra = function () {
31    return data;
32  };
33  return data;
34}
35function _path() {
36  const data = _interopRequireDefault(require("path"));
37  _path = function () {
38    return data;
39  };
40  return data;
41}
42function _withAndroidManifestIcons() {
43  const data = require("./withAndroidManifestIcons");
44  _withAndroidManifestIcons = function () {
45    return data;
46  };
47  return data;
48}
49function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
50const {
51  Colors
52} = _configPlugins().AndroidConfig;
53const dpiValues = {
54  mdpi: {
55    folderName: 'mipmap-mdpi',
56    scale: 1
57  },
58  hdpi: {
59    folderName: 'mipmap-hdpi',
60    scale: 1.5
61  },
62  xhdpi: {
63    folderName: 'mipmap-xhdpi',
64    scale: 2
65  },
66  xxhdpi: {
67    folderName: 'mipmap-xxhdpi',
68    scale: 3
69  },
70  xxxhdpi: {
71    folderName: 'mipmap-xxxhdpi',
72    scale: 4
73  }
74};
75exports.dpiValues = dpiValues;
76const BASELINE_PIXEL_SIZE = 108;
77const ANDROID_RES_PATH = 'android/app/src/main/res/';
78exports.ANDROID_RES_PATH = ANDROID_RES_PATH;
79const MIPMAP_ANYDPI_V26 = 'mipmap-anydpi-v26';
80const ICON_BACKGROUND = 'iconBackground';
81const IC_LAUNCHER_PNG = 'ic_launcher.png';
82const IC_LAUNCHER_ROUND_PNG = 'ic_launcher_round.png';
83const IC_LAUNCHER_BACKGROUND_PNG = 'ic_launcher_background.png';
84const IC_LAUNCHER_FOREGROUND_PNG = 'ic_launcher_foreground.png';
85const IC_LAUNCHER_MONOCHROME_PNG = 'ic_launcher_monochrome.png';
86const IC_LAUNCHER_XML = 'ic_launcher.xml';
87const IC_LAUNCHER_ROUND_XML = 'ic_launcher_round.xml';
88const withAndroidIcons = config => {
89  const {
90    foregroundImage,
91    backgroundColor,
92    backgroundImage,
93    monochromeImage
94  } = getAdaptiveIcon(config);
95  const icon = foregroundImage !== null && foregroundImage !== void 0 ? foregroundImage : getIcon(config);
96  if (!icon) {
97    return config;
98  }
99  config = (0, _withAndroidManifestIcons().withAndroidManifestIcons)(config);
100  // Apply colors.xml changes
101  config = withAndroidAdaptiveIconColors(config, backgroundColor);
102  return (0, _configPlugins().withDangerousMod)(config, ['android', async config => {
103    var _config$android;
104    await setIconAsync(config.modRequest.projectRoot, {
105      icon,
106      backgroundColor,
107      backgroundImage,
108      monochromeImage,
109      isAdaptive: !!((_config$android = config.android) !== null && _config$android !== void 0 && _config$android.adaptiveIcon)
110    });
111    return config;
112  }]);
113};
114exports.withAndroidIcons = withAndroidIcons;
115function setRoundIconManifest(config, manifest) {
116  var _config$android2;
117  const isAdaptive = !!((_config$android2 = config.android) !== null && _config$android2 !== void 0 && _config$android2.adaptiveIcon);
118  const application = _configPlugins().AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
119  if (isAdaptive) {
120    application.$['android:roundIcon'] = '@mipmap/ic_launcher_round';
121  } else {
122    delete application.$['android:roundIcon'];
123  }
124  return manifest;
125}
126const withAndroidAdaptiveIconColors = (config, backgroundColor) => {
127  return (0, _configPlugins().withAndroidColors)(config, config => {
128    config.modResults = setBackgroundColor(backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : '#FFFFFF', config.modResults);
129    return config;
130  });
131};
132function getIcon(config) {
133  var _config$android3;
134  return ((_config$android3 = config.android) === null || _config$android3 === void 0 ? void 0 : _config$android3.icon) || config.icon || null;
135}
136function getAdaptiveIcon(config) {
137  var _config$android$adapt, _config$android4, _config$android4$adap, _config$android$adapt2, _config$android5, _config$android5$adap, _config$android$adapt3, _config$android6, _config$android6$adap, _config$android$adapt4, _config$android7, _config$android7$adap;
138  return {
139    foregroundImage: (_config$android$adapt = (_config$android4 = config.android) === null || _config$android4 === void 0 ? void 0 : (_config$android4$adap = _config$android4.adaptiveIcon) === null || _config$android4$adap === void 0 ? void 0 : _config$android4$adap.foregroundImage) !== null && _config$android$adapt !== void 0 ? _config$android$adapt : null,
140    backgroundColor: (_config$android$adapt2 = (_config$android5 = config.android) === null || _config$android5 === void 0 ? void 0 : (_config$android5$adap = _config$android5.adaptiveIcon) === null || _config$android5$adap === void 0 ? void 0 : _config$android5$adap.backgroundColor) !== null && _config$android$adapt2 !== void 0 ? _config$android$adapt2 : null,
141    backgroundImage: (_config$android$adapt3 = (_config$android6 = config.android) === null || _config$android6 === void 0 ? void 0 : (_config$android6$adap = _config$android6.adaptiveIcon) === null || _config$android6$adap === void 0 ? void 0 : _config$android6$adap.backgroundImage) !== null && _config$android$adapt3 !== void 0 ? _config$android$adapt3 : null,
142    monochromeImage: (_config$android$adapt4 = (_config$android7 = config.android) === null || _config$android7 === void 0 ? void 0 : (_config$android7$adap = _config$android7.adaptiveIcon) === null || _config$android7$adap === void 0 ? void 0 : _config$android7$adap.monochromeImage) !== null && _config$android$adapt4 !== void 0 ? _config$android$adapt4 : null
143  };
144}
145
146/**
147 * Resizes the user-provided icon to create a set of legacy icon files in
148 * their respective "mipmap" directories for <= Android 7, and creates a set of adaptive
149 * icon files for > Android 7 from the adaptive icon files (if provided).
150 */
151async function setIconAsync(projectRoot, {
152  icon,
153  backgroundColor,
154  backgroundImage,
155  monochromeImage,
156  isAdaptive
157}) {
158  if (!icon) {
159    return null;
160  }
161  await configureLegacyIconAsync(projectRoot, icon, backgroundImage, backgroundColor);
162  if (isAdaptive) {
163    await generateRoundIconAsync(projectRoot, icon, backgroundImage, backgroundColor);
164  } else {
165    await deleteIconNamedAsync(projectRoot, IC_LAUNCHER_ROUND_PNG);
166  }
167  await configureAdaptiveIconAsync(projectRoot, icon, backgroundImage, monochromeImage, isAdaptive);
168  return true;
169}
170
171/**
172 * Configures legacy icon files to be used on Android 7 and earlier. If adaptive icon configuration
173 * was provided, we create a pseudo-adaptive icon by layering the provided files (or background
174 * color if no backgroundImage is provided. If no backgroundImage and no backgroundColor are provided,
175 * the background is set to transparent.)
176 */
177async function configureLegacyIconAsync(projectRoot, icon, backgroundImage, backgroundColor) {
178  return generateMultiLayerImageAsync(projectRoot, {
179    icon,
180    backgroundImage,
181    backgroundColor,
182    outputImageFileName: IC_LAUNCHER_PNG,
183    imageCacheFolder: 'android-standard-square',
184    backgroundImageCacheFolder: 'android-standard-square-background'
185  });
186}
187async function generateRoundIconAsync(projectRoot, icon, backgroundImage, backgroundColor) {
188  return generateMultiLayerImageAsync(projectRoot, {
189    icon,
190    borderRadiusRatio: 0.5,
191    outputImageFileName: IC_LAUNCHER_ROUND_PNG,
192    backgroundImage,
193    backgroundColor,
194    imageCacheFolder: 'android-standard-circle',
195    backgroundImageCacheFolder: 'android-standard-round-background'
196  });
197}
198
199/**
200 * Configures adaptive icon files to be used on Android 8 and up. A foreground image must be provided,
201 * and will have a transparent background unless:
202 * - A backgroundImage is provided, or
203 * - A backgroundColor was specified
204 */
205async function configureAdaptiveIconAsync(projectRoot, foregroundImage, backgroundImage, monochromeImage, isAdaptive) {
206  if (monochromeImage) {
207    await generateMonochromeImageAsync(projectRoot, {
208      icon: monochromeImage,
209      imageCacheFolder: 'android-adaptive-monochrome',
210      outputImageFileName: IC_LAUNCHER_MONOCHROME_PNG
211    });
212  }
213  await generateMultiLayerImageAsync(projectRoot, {
214    backgroundColor: 'transparent',
215    backgroundImage,
216    backgroundImageCacheFolder: 'android-adaptive-background',
217    outputImageFileName: IC_LAUNCHER_FOREGROUND_PNG,
218    icon: foregroundImage,
219    imageCacheFolder: 'android-adaptive-foreground',
220    backgroundImageFileName: IC_LAUNCHER_BACKGROUND_PNG
221  });
222
223  // create ic_launcher.xml and ic_launcher_round.xml
224  const icLauncherXmlString = createAdaptiveIconXmlString(backgroundImage, monochromeImage);
225  await createAdaptiveIconXmlFiles(projectRoot, icLauncherXmlString,
226  // If the user only defined icon and not android.adaptiveIcon, then skip enabling the layering system
227  // this will scale the image down and present it uncropped.
228  isAdaptive);
229}
230function setBackgroundColor(backgroundColor, colors) {
231  return Colors.assignColorValue(colors, {
232    value: backgroundColor,
233    name: ICON_BACKGROUND
234  });
235}
236const createAdaptiveIconXmlString = (backgroundImage, monochromeImage) => {
237  const background = backgroundImage ? `@mipmap/ic_launcher_background` : `@color/iconBackground`;
238  const iconElements = [`<background android:drawable="${background}"/>`, '<foreground android:drawable="@mipmap/ic_launcher_foreground"/>'];
239  if (monochromeImage) {
240    iconElements.push('<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>');
241  }
242  return `<?xml version="1.0" encoding="utf-8"?>
243<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
244    ${iconElements.join('\n    ')}
245</adaptive-icon>`;
246};
247exports.createAdaptiveIconXmlString = createAdaptiveIconXmlString;
248async function createAdaptiveIconXmlFiles(projectRoot, icLauncherXmlString, add) {
249  const anyDpiV26Directory = _path().default.resolve(projectRoot, ANDROID_RES_PATH, MIPMAP_ANYDPI_V26);
250  await _fsExtra().default.ensureDir(anyDpiV26Directory);
251  const launcherPath = _path().default.resolve(anyDpiV26Directory, IC_LAUNCHER_XML);
252  const launcherRoundPath = _path().default.resolve(anyDpiV26Directory, IC_LAUNCHER_ROUND_XML);
253  if (add) {
254    await Promise.all([_fsExtra().default.writeFile(launcherPath, icLauncherXmlString), _fsExtra().default.writeFile(launcherRoundPath, icLauncherXmlString)]);
255  } else {
256    // Remove the xml if the icon switches from adaptive to standard.
257    await Promise.all([launcherPath, launcherRoundPath].map(async path => {
258      if (_fsExtra().default.existsSync(path)) {
259        return _fsExtra().default.remove(path);
260      }
261    }));
262  }
263}
264async function generateMultiLayerImageAsync(projectRoot, {
265  icon,
266  backgroundColor,
267  backgroundImage,
268  imageCacheFolder,
269  backgroundImageCacheFolder,
270  borderRadiusRatio,
271  outputImageFileName,
272  backgroundImageFileName
273}) {
274  await iterateDpiValues(projectRoot, async ({
275    dpiFolder,
276    scale
277  }) => {
278    let iconLayer = await generateIconAsync(projectRoot, {
279      cacheType: imageCacheFolder,
280      src: icon,
281      scale,
282      // backgroundImage overrides backgroundColor
283      backgroundColor: backgroundImage ? 'transparent' : backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : 'transparent',
284      borderRadiusRatio
285    });
286    if (backgroundImage) {
287      const backgroundLayer = await generateIconAsync(projectRoot, {
288        cacheType: backgroundImageCacheFolder,
289        src: backgroundImage,
290        scale,
291        backgroundColor: 'transparent',
292        borderRadiusRatio
293      });
294      if (backgroundImageFileName) {
295        await _fsExtra().default.writeFile(_path().default.resolve(dpiFolder, backgroundImageFileName), backgroundLayer);
296      } else {
297        iconLayer = await (0, _imageUtils().compositeImagesAsync)({
298          foreground: iconLayer,
299          background: backgroundLayer
300        });
301      }
302    } else if (backgroundImageFileName) {
303      // Remove any instances of ic_launcher_background.png that are there from previous icons
304      await deleteIconNamedAsync(projectRoot, backgroundImageFileName);
305    }
306    await _fsExtra().default.ensureDir(dpiFolder);
307    await _fsExtra().default.writeFile(_path().default.resolve(dpiFolder, outputImageFileName), iconLayer);
308  });
309}
310async function generateMonochromeImageAsync(projectRoot, {
311  icon,
312  imageCacheFolder,
313  outputImageFileName
314}) {
315  await iterateDpiValues(projectRoot, async ({
316    dpiFolder,
317    scale
318  }) => {
319    const monochromeIcon = await generateIconAsync(projectRoot, {
320      cacheType: imageCacheFolder,
321      src: icon,
322      scale,
323      backgroundColor: 'transparent'
324    });
325    await _fsExtra().default.ensureDir(dpiFolder);
326    await _fsExtra().default.writeFile(_path().default.resolve(dpiFolder, outputImageFileName), monochromeIcon);
327  });
328}
329function iterateDpiValues(projectRoot, callback) {
330  return Promise.all(Object.values(dpiValues).map(value => callback({
331    dpiFolder: _path().default.resolve(projectRoot, ANDROID_RES_PATH, value.folderName),
332    ...value
333  })));
334}
335async function deleteIconNamedAsync(projectRoot, name) {
336  return iterateDpiValues(projectRoot, ({
337    dpiFolder
338  }) => {
339    return _fsExtra().default.remove(_path().default.resolve(dpiFolder, name));
340  });
341}
342async function generateIconAsync(projectRoot, {
343  cacheType,
344  src,
345  scale,
346  backgroundColor,
347  borderRadiusRatio
348}) {
349  const iconSizePx = BASELINE_PIXEL_SIZE * scale;
350  return (await (0, _imageUtils().generateImageAsync)({
351    projectRoot,
352    cacheType
353  }, {
354    src,
355    width: iconSizePx,
356    height: iconSizePx,
357    resizeMode: 'cover',
358    backgroundColor,
359    borderRadius: borderRadiusRatio ? iconSizePx * borderRadiusRatio : undefined
360  })).source;
361}
362//# sourceMappingURL=withAndroidIcons.js.map