From 03f979c21d0a18ca64f0c86ac35759f7ace656e6 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Mon, 30 Nov 2020 00:14:21 +0100 Subject: [PATCH 1/6] style: ending / for ignored directories --- .gitignore | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0824afb..40fe6fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,18 @@ node_modules/ artifacts/ test/ReactNativeIconTest/ios/ReactNativeIconTest/Images.xcassets/AppIcon.appiconset/ -test/ReactNativeIconTest/android/app/src/main/res -test/CordovaApp/platforms/ios/ionic_app/Images.xcassets/AppIcon.appiconset -test/CordovaApp/platforms/android/res -test/NativeApp/ios/native_app/Assets.xcassets/AppIcon.appiconset -test/NativeApp/android/native_app/src/main/res +test/ReactNativeIconTest/android/app/src/main/res/ +test/CordovaApp/platforms/ios/ionic_app/Images.xcassets/AppIcon.appiconset/ +test/CordovaApp/platforms/android/res/ +test/NativeApp/ios/native_app/Assets.xcassets/AppIcon.appiconset/ +test/NativeApp/android/native_app/src/main/res/ npm-debug.log src/init/test-images/*-output.png src/label/test-images/*-output.png src/resize/test-images/*output.png .nyc_output/ -.idea +.idea/ *.iml # Windows junk. From 27020ad5e4e0f4a25fd4f28d5e81983bb6e64594 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Wed, 16 Dec 2020 23:27:22 +0100 Subject: [PATCH 2/6] style: cleanup await in generate specs --- src/android/generate-manifest-adaptive-icons.specs.js | 4 ++-- src/android/generate-manifest-icons.specs.js | 3 ++- src/ios/generate-iconset-icons.specs.js | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/android/generate-manifest-adaptive-icons.specs.js b/src/android/generate-manifest-adaptive-icons.specs.js index 2ac740e..a9fd227 100644 --- a/src/android/generate-manifest-adaptive-icons.specs.js +++ b/src/android/generate-manifest-adaptive-icons.specs.js @@ -1,7 +1,7 @@ const { expect } = require('chai'); const path = require('path'); -const deleteFolderIfExists = require('../utils/delete-folder-if-exists'); const generateManifestAdaptiveIcons = require('./generate-manifest-adaptive-icons'); +const deleteFolderIfExists = require('../utils/delete-folder-if-exists'); const fileExists = require('../utils/file-exists'); const backgroundIcon = './test/icon.background.png'; @@ -59,7 +59,7 @@ describe('generate-manifest-adaptive-icons', () => { // Delete all of the folders we're expecting to create, then generate the icons. await Promise.all(resourceFolders.map(deleteFolderIfExists)); - await (generateManifestAdaptiveIcons(backgroundIcon, foregroundIcon, manifestPath)); + await generateManifestAdaptiveIcons(backgroundIcon, foregroundIcon, manifestPath); const filesDoExist = await Promise.all(expectedPaths.map(fileExists)); filesDoExist.forEach((exists, index) => { expect(exists, `${resourceFoldersFiles[index]} should be generated`).to.equal(true); diff --git a/src/android/generate-manifest-icons.specs.js b/src/android/generate-manifest-icons.specs.js index 5e5b157..fecfd76 100644 --- a/src/android/generate-manifest-icons.specs.js +++ b/src/android/generate-manifest-icons.specs.js @@ -21,6 +21,7 @@ const expectedFiles = [ './ic_launcher.png', './ic_launcher_round.png', ]; + // Create a test for each manifest. const testManifests = [{ projectName: 'React Native Manifest', @@ -47,7 +48,7 @@ describe('generate-manifest-icons', () => { // Delete all of the folders we're expecting to create, then generate the icons. await Promise.all(resourceFolders.map(deleteFolderIfExists)); - await (generateManifestIcons(sourceIcon, manifestPath)); + await generateManifestIcons(sourceIcon, manifestPath); const filesDoExist = await Promise.all(resourceFoldersFiles.map(fileExists)); filesDoExist.forEach((exists, index) => { expect(exists, `${resourceFoldersFiles[index]} should be generated`).to.equal(true); diff --git a/src/ios/generate-iconset-icons.specs.js b/src/ios/generate-iconset-icons.specs.js index 6f15056..73cd424 100644 --- a/src/ios/generate-iconset-icons.specs.js +++ b/src/ios/generate-iconset-icons.specs.js @@ -29,7 +29,7 @@ describe('generate-iconset-icons', () => { // Delete all of the files we're expecting to create, then generate them. await Promise.all(files.map(deleteIfExists)); - await (generateIconsetIcons(sourceIcon, 'test/ReactNativeIconTest/ios/ReactNativeIconTest/Images.xcassets/AppIcon.appiconset')); + await generateIconsetIcons(sourceIcon, 'test/ReactNativeIconTest/ios/ReactNativeIconTest/Images.xcassets/AppIcon.appiconset'); const filesDoExist = await Promise.all(files.map(fileExists)); filesDoExist.forEach((exists, index) => { expect(exists, `${files[index]} should be generated`).to.equal(true); @@ -59,7 +59,7 @@ describe('generate-iconset-icons', () => { // Delete all of the files we're expecting to create, then generate them. await Promise.all(files.map(deleteIfExists)); - await (generateIconsetIcons(sourceIcon, 'test/CordovaApp/platforms/ios/ionic_app/Images.xcassets/AppIcon.appiconset')); + await generateIconsetIcons(sourceIcon, 'test/CordovaApp/platforms/ios/ionic_app/Images.xcassets/AppIcon.appiconset'); const filesDoExist = await Promise.all(files.map(fileExists)); filesDoExist.forEach((exists, index) => { expect(exists, `${files[index]} should be generated`).to.equal(true); @@ -89,7 +89,7 @@ describe('generate-iconset-icons', () => { // Delete all of the files we're expecting to create, then generate them. await Promise.all(files.map(deleteIfExists)); - await (generateIconsetIcons(sourceIcon, 'test/NativeApp/ios/native_app/Assets.xcassets/AppIcon.appiconset')); + await generateIconsetIcons(sourceIcon, 'test/NativeApp/ios/native_app/Assets.xcassets/AppIcon.appiconset'); const filesDoExist = await Promise.all(files.map(fileExists)); filesDoExist.forEach((exists, index) => { expect(exists, `${files[index]} should be generated`).to.equal(true); From 696b4c8c11ca4308978b7de38ea0b41104d0b13f Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Wed, 16 Dec 2020 23:37:47 +0100 Subject: [PATCH 3/6] feat(android): add notification icons generate --- .../AndroidManifest.notification-icons.json | 9 +++ .../generate-manifest-notification-icons.js | 33 +++++++++++ ...erate-manifest-notification-icons.specs.js | 56 ++++++++++++++++++ .../drawable-hdpi/ic_stat_notification.png | Bin 0 -> 1123 bytes .../drawable-mdpi/ic_stat_notification.png | Bin 0 -> 1327 bytes .../drawable-xhdpi/ic_stat_notification.png | Bin 0 -> 1457 bytes .../drawable-xxhdpi/ic_stat_notification.png | Bin 0 -> 2094 bytes .../drawable-xxxhdpi/ic_stat_notification.png | Bin 0 -> 2787 bytes test/notification.png | Bin 0 -> 1439 bytes 9 files changed, 98 insertions(+) create mode 100644 src/android/AndroidManifest.notification-icons.json create mode 100644 src/android/generate-manifest-notification-icons.js create mode 100644 src/android/generate-manifest-notification-icons.specs.js create mode 100644 test/CordovaApp/platforms/android/src/main/res/drawable-hdpi/ic_stat_notification.png create mode 100644 test/CordovaApp/platforms/android/src/main/res/drawable-mdpi/ic_stat_notification.png create mode 100644 test/CordovaApp/platforms/android/src/main/res/drawable-xhdpi/ic_stat_notification.png create mode 100644 test/CordovaApp/platforms/android/src/main/res/drawable-xxhdpi/ic_stat_notification.png create mode 100644 test/CordovaApp/platforms/android/src/main/res/drawable-xxxhdpi/ic_stat_notification.png create mode 100644 test/notification.png diff --git a/src/android/AndroidManifest.notification-icons.json b/src/android/AndroidManifest.notification-icons.json new file mode 100644 index 0000000..06230cf --- /dev/null +++ b/src/android/AndroidManifest.notification-icons.json @@ -0,0 +1,9 @@ +{ + "notificationIcons": [ + { "path": "res/drawable-mdpi/ic_stat_" , "size": "24x24" }, + { "path": "res/drawable-hdpi/ic_stat_" , "size": "36x36" }, + { "path": "res/drawable-xhdpi/ic_stat_" , "size": "48x48" }, + { "path": "res/drawable-xxhdpi/ic_stat_" , "size": "72x72" }, + { "path": "res/drawable-xxxhdpi/ic_stat_" , "size": "96x96" } + ] +} diff --git a/src/android/generate-manifest-notification-icons.js b/src/android/generate-manifest-notification-icons.js new file mode 100644 index 0000000..43dccac --- /dev/null +++ b/src/android/generate-manifest-notification-icons.js @@ -0,0 +1,33 @@ +const path = require('path'); +const mkdirp = require('mkdirp'); + +const androidManifestNotificationIcons = require('./AndroidManifest.notification-icons.json'); +const resizeImage = require('../resize/resize-image'); + +// Generate Android Manifest icons given a manifest file. +module.exports = async function generateManifestIcons(notificationIcon, manifest) { + // Create the object we will return. + const results = { + icons: [], + }; + + // We've got the manifest file, get the parent folder. + const manifestFolder = path.dirname(manifest); + + // Generate each image in the full icon set, updating the contents. + await Promise.all(androidManifestNotificationIcons.notificationIcons.map(async (icon) => { + const iconPathWithFileName = icon.path.replace('', path.basename(notificationIcon)); + const targetPath = path.join(manifestFolder, iconPathWithFileName); + + // Each icon lives in its own folder, so we'd better make sure that folder + // exists. + await mkdirp(path.dirname(targetPath)); + results.icons.push(iconPathWithFileName); + + return resizeImage(notificationIcon, targetPath, icon.size); + })); + // Before writing the contents file, sort the contents (otherwise + // they could be in a different order each time). + results.icons.sort(); + return results; +}; diff --git a/src/android/generate-manifest-notification-icons.specs.js b/src/android/generate-manifest-notification-icons.specs.js new file mode 100644 index 0000000..9a1285f --- /dev/null +++ b/src/android/generate-manifest-notification-icons.specs.js @@ -0,0 +1,56 @@ +const { expect } = require('chai'); +const path = require('path'); +const generateManifestNotificationIcons = require('./generate-manifest-notification-icons'); +const deleteFolderIfExists = require('../utils/delete-folder-if-exists'); +const fileExists = require('../utils/file-exists'); + +const notificationIcon = './test/notification.png'; + +// The folders we expect to generate, relative to the manifest location. +const expectedFolders = [ + './res/drawable-hdpi', + './res/drawable-mdpi', + './res/drawable-xhdpi', + './res/drawable-xxhdpi', + './res/drawable-xxxhdpi', +]; + +// The files we expect in each of the folders above. +const expectedFiles = [ + './ic_stat_notification.png', +]; + +// Create a test for each manifest. +const testManifests = [{ + projectName: 'React Native Manifest', + manifestPath: './test/ReactNativeIconTest/android/app/src/main/AndroidManifest.xml', +}, { + projectName: 'Cordova Manifest', + manifestPath: './test/CordovaApp/platforms/android/src/main/AndroidManifest.xml', +}, { + projectName: 'Native Manifest', + manifestPath: './test/NativeApp/android/native_app/src/main/AndroidManifest.xml', +}]; + +describe('generate-manifest-notification-icons', () => { + // Run each test. + testManifests.forEach(({ projectName, manifestPath }) => { + it(`should be able to generate notification icons for the ${projectName} manifest`, async () => { + // Get the manifest folder, create an array of every icon we expect to see. + const manifestFolder = path.dirname(manifestPath); + const resourceFolders = expectedFolders.map((f) => path.join(manifestFolder, f)); + const resourceFoldersFiles = resourceFolders.reduce((allFiles, folder) => { + expectedFiles.forEach((ef) => allFiles.push(path.join(folder, ef))); + return allFiles; + }, []); + + // Delete all of the folders we're expecting to create, then generate the icons. + await Promise.all(resourceFolders.map(deleteFolderIfExists)); + await generateManifestNotificationIcons(notificationIcon, manifestPath); + const filesDoExist = await Promise.all(resourceFoldersFiles.map(fileExists)); + filesDoExist.forEach((exists, index) => { + expect(exists, `${resourceFoldersFiles[index]} should be generated`).to.equal(true); + }); + }); + }); +}); diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-hdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-hdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..7f7f27056391312d8f046b9e8930bd3c64a6345a GIT binary patch literal 1123 zcmV-p1f2VcP)365o%uMp`{kzD^bS8Z-c}YBQNyxkR+}}C(f6h(f zDQV`b*&7>an6EIop+O976w|_el70}=UtA?!T3}-RW?V9f^L?imnlRdn^ZfqZ8^n9F zn6IGF9uDZmFJ}Qbf9!c)Ix@FCwwgO4aU>$iTCIhfe>}YQ>+Bs#o6Inm&16Qe$ zHt%rY*bBJI%>&N)BVf-05eP%_BmlJyjSG7|I``QovtNKz**>woxMs5DdYF2Gy`Nkr zrmd%aW%49uyA95naYEwXqIG%6F{2^zj$ekEz~m~Ej{)UoC@kGa zeuCIqeg?7K#0BqoHx0VvU0Ifnr9pUSGg15`S9xw?ePai36_hKYTp)7^eqtT%g_~aP9NG>3g*?X?_n`=qvxm-_Xmp^YrHBa=xaig|%h?7PMlJ&*vcKq27F;irZ=E`T$KH+Ot>eGZUcVHu|p zf`kdMjA%rQ2nnJ@9N?%BD5^7pgkdXDj$%P%Kn(Qb$~crtmwD0p($@FLV%7#_XdI%D z={Uy0aK%8O1LgV`MJY==z1?CM8q~ zqkc9LP#QW%W~&4_s-*!iPzh0&j{AR-(m)&7sfl2wu^M9c?)_{W8x2?gJ<1?CD+If)r2M0?5Juo;vBerd2OS7$)Ly$)9Lumv> zp{STf_J(NROeFf&;Ck=e?jF728{kY^A1vGZY1FhLcSZ*4aqQB_TcOSC5(|0!8Q2eu1?|uU<&(DUr^S9}9SNq&gW5Al%Y0Z4N;CH^+v}9@z&ebxR_YN=V6-oO+ zQ=qW+IT6L6<-32Smw)^|z4*g9;4UEZmB;=88F;V;^~&_e(^lWQ8^y`|;JO0I?=%Xn zcDR1_@9^i(7lB#x)rX1!msHFB`_XygvH&qUTB`w-K>_r!6j;I~t*m^D63_?8S0S`O p!dE8>Pq>VkPe>Q<=LNjR{sXJyP31kv8vOtO002ovPDHLkV1lr=B`W{` literal 0 HcmV?d00001 diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-mdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-mdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..cffbbafb3e1d6c3819e9ffe48440f63cba27d18f GIT binary patch literal 1327 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|PfvG#dC&U%VwP=1}*6_rt^^JAg zJBz003Vyx5JN`2;G8tD~HE(*R5;0H2ri_t=&7|V0UEd!jc0OJqIm7a+yoxrAEbO`k zr!CsvTerXGS9Q_NKWWntS<8DqRcEcdlSa+&cvYM(!6tnjT`%68%Wx5D{vcyiwG=DZQff6E_xoLSC^H}*JR#2zLwLk^u7 zzW+ckuyA_JV`k%KWMXmn|DQW-pKs!mV_qH7%4+4>8+XthH8_XO6K%bg5zcFupZ_)PApzJCezpQTF z3Df2`rmgSI+CJ)+USZ>x(ab$=)cD4@`MpugN1c+(to$+tjjw^>V9@+QtN5~7_HpgH z*T4YLY51UCaG8})Mn3JhM)hl8$Y|AlP|3e6n|d4=IGS}Iq*IQoRJ;ZTk!tk^g`CS$ z$;TASUjqY5sp^AV_GK168S$iJvZb%&%HPVBe~`+&B$jwgs`wQ!+@wn1i>F^=;*}PT zKPFc23K(wU#qUK@E-~^*3&kB1D|#=Sa*2sYS~UL^Fl;4?-wP#Q;*UAXm-9*>?~Qoj zYhdsK!Tz-bA1-DcK$iFV}&!qRh-JYYs?o?K`t}`F{3ov$u;UKL7sh^s_=94%_J1 zcn?V?W|q*84F?$*7;8OU978nDCnqp4xv8azStO(;KTl|2@YDO>a6o~BgO8K<@w11m z9v?ooySunKI;NEPtZ7hianjJUQ#90+5)#V zYo_T(<>WFa-i}xwv6D-2V^#0kxqF!fZrrw97t21qAZmZ&<7H=~x8>a3m3sP_@9k~x z3);MPytz}F`+c5m^|yDdLc7<$k7k=(aJT+}6o-rrhf=xV8`}athO&iI UGn{=)TR=(6)78&qol`;+01Za^mjD0& literal 0 HcmV?d00001 diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-xhdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-xhdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7ed92311dc78df2cd076023b955a8657817fe2 GIT binary patch literal 1457 zcmV;i1y1^jP)7MSXy7!!Y=m}eC^W5~<7j#Y?XW`=ClFV%;zhDk&)TBFL zK+%PFAH9UoI(e29W+P?ODhFKs$fp!#!BzL}<=ER_|HgOE><*nDw_sJItHG-^JKr4o zIJ8cF*XIKWt}%y}oyiGElF|MC2#24)gM9AWdy>Zd_NqhPP1KDCMcDz^zZU@!iwAT$;ZmB1`>0{~+*@!4FdqF@dcH*8p1q{CfMn zV}Jk411H{k`moD3l@>hWk`28u)&rOC{~L=}Y@&H+?M_3M%s_+>QpaP?$$O=@^=V9> zrjo1LEna!jj2k4KzhU!bd-t7qi{o0Zdr%XXY$)%>o*<4__6w@ET@BP|Vz%r9ubXiO zD5iPLIOCPqFPbpnX+k38idBB;T;f8IKc1K9V}kL0O< z*};zIpBQ&*C>uc5H*gpwKh^`1G+-#k&1kNA*KHNX%JSk=$^)z{FVZTE(_Xq}GzZ60 zwZvCF$n4ycIg`4|&p_K$5Cb$)wlV?e6=$nznn>tSsK>OIN{tL4nBg!4C~KS`0K6LO z9_CW%lc`RjvIo>a6+*9!{={O_A}DC+8A;8?6`simK;N|%DO3?je^av=h0qWQSu4QJ zf(1k;SY-lgApI%YDGuG#;w03#*Y&JYYnF*a6Z(a*@7~}N{+@^4@Y{sdM8$}&>tr(F44xJ z2>nFvn=__BM3vEjb@G}n^8?s<-F;@A`2P_R`aj4q>T!s2aLCfV&v3A>h$Os)K`!7kM>Iz~K5XzX?l4kdLoe=yKshrx7hCQr=v~w)x{6I-z8VEtNs8 z^MU$1@fLdXe;JHUY1>b&aR3cFR^Hf2Sr(MaWKUVJ^2Scku;bJk2Rfx~FEuVaCrZbl z+}HN*_uFo6zt$}|K52n+0^4qGZ*O~dS5S6OvPU$5{s%cJ1AEHAo-xa9NbJAHljUt)vP?d*?gM4PFzhf`+{3uj-OS7IB}=EoAyuMg5RQ_?>=TaaW}C`e!~-~Y<}a?u|iC0p2uxW*VOoxit@SZ8P`Z0+06%a5LWvHb9_?|}Rp z=T2j0F3mUmN;#fDnqz()dfwXH*!`z#-No13NRlmLTF;%@z&g!UfArPEdgA?Kz*mTz za@j&@z8LwLwHg8Sh|4hFNq{+EGq8yX%kTzJpa408&;s#*VWXp{T0r+N0xsKxmTQ9H z5J@V_S4RL*m*%RzOV2emjKQuk-_upxY%T-m8B3B3Tzc+(`w098kAB9`qYwIb00000 LNkvXXu0mjf;Z({t literal 0 HcmV?d00001 diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-xxhdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-xxhdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..708c21a555f05783d28506e47e3310ee85286d95 GIT binary patch literal 2094 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 literal 0 HcmV?d00001 From 6df8fee6adc554335bd3dd57882c8d8ab742f5a0 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Wed, 16 Dec 2020 23:43:24 +0100 Subject: [PATCH 4/6] feat(imagemagick): implement and test color/monochrome --- .gitignore | 1 + src/color/monochrome-image.js | 5 ++++ src/color/monochrome-image.specs.js | 31 ++++++++++++++++++++ src/color/test-images/input.png | Bin 0 -> 1903 bytes src/color/test-images/output.png | Bin 0 -> 2698 bytes src/color/test-images/reference-default.png | Bin 0 -> 1363 bytes src/color/test-images/reference-white.png | Bin 0 -> 1393 bytes 7 files changed, 37 insertions(+) create mode 100644 src/color/monochrome-image.js create mode 100644 src/color/monochrome-image.specs.js create mode 100644 src/color/test-images/input.png create mode 100644 src/color/test-images/output.png create mode 100644 src/color/test-images/reference-default.png create mode 100644 src/color/test-images/reference-white.png diff --git a/.gitignore b/.gitignore index 40fe6fc..6190094 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ test/CordovaApp/platforms/android/res/ test/NativeApp/ios/native_app/Assets.xcassets/AppIcon.appiconset/ test/NativeApp/android/native_app/src/main/res/ npm-debug.log +src/color/test-images/*-output.png src/init/test-images/*-output.png src/label/test-images/*-output.png src/resize/test-images/*output.png diff --git a/src/color/monochrome-image.js b/src/color/monochrome-image.js new file mode 100644 index 0000000..50b3ee9 --- /dev/null +++ b/src/color/monochrome-image.js @@ -0,0 +1,5 @@ +const imagemagickCli = require('imagemagick-cli'); + +module.exports = async function monochromeImage(source, target, color = 'black') { + return imagemagickCli.exec(`convert "${source}" -fill ${color} -colorize 100 "${target}"`); +}; diff --git a/src/color/monochrome-image.specs.js b/src/color/monochrome-image.specs.js new file mode 100644 index 0000000..74054c9 --- /dev/null +++ b/src/color/monochrome-image.specs.js @@ -0,0 +1,31 @@ +const { expect } = require('chai'); +const monochromeImage = require('./monochrome-image'); +const compareImages = require('../testing/compare-images'); + +describe('monochrome-image', () => { + it('should be able to monochrome an image to given color', async () => { + const input = './src/color/test-images/input.png'; + const output = './src/color/test-images/output.png'; + const reference = './src/color/test-images/reference-white.png'; + await monochromeImage(input, output, 'white'); + const difference = await compareImages(output, reference); + expect(difference).to.be.below(1, 'Generated image is below accepted similarly threshold'); + }); + + it('should be able to monochrome an image with default color', async () => { + const input = './src/color/test-images/input.png'; + const output = './src/color/test-images/output.png'; + const reference = './src/color/test-images/reference-default.png'; + await monochromeImage(input, output); + const difference = await compareImages(output, reference); + expect(difference).to.be.below(1, 'Generated image is below accepted similarly threshold'); + }); + + it('should fail with a sensible error message if imagemagick returns an error', async () => { + try { + await monochromeImage('badinput', 'badoutput', 'badcolor'); + } catch (err) { + expect(err.message).to.match(/failed/); + } + }); +}); diff --git a/src/color/test-images/input.png b/src/color/test-images/input.png new file mode 100644 index 0000000000000000000000000000000000000000..1544fbfc500752efa4c2db4e8ce71b4435826cab GIT binary patch literal 1903 zcmZ8g3pmv28vp(?{~2S(WK7h=Dz_qXOm4H2HbcrKrg4`xCJ762&ctO7)B4OhYS(CCr+dYB-wrR z>pYx&t?D(tC`H0NXOCNhqV@L_*Gef}FF8h(yiJBdWPjijfl2Z_PTf{Fv;)@mB>2sU zNXiAx(r(NhjNwz``HEL(MF|)qSFo9xhMpQ zii$*#A_DV})5QMOoT&k&p*FAbXhGUFpX2)Jf!&?CSq*n%d~MBWT6e;n?TP{(-w(+I z0JC6eX5tX>erCYWnQSYI9o4gEJUyqcyGND;zf{dj%CrmdB7fZ4%IVOrG=zUXkunqG zF`U^Tyg^?Z`{3<*5H=xwS*bqd%a5#Yzc4j)YFxeTuM~fguAY6>X_9!l;@#EB<|?9c zS5NiX6qA>{`9hby`mVE`RhMt?bm_CKdb6a~9atxg^S4E{U823tad_=s;*N#aQNo34 zY>wxu_8oH9`c#GbgO8bKH$`UP*6OE0HE3h4t#cn9hG)PJJ(^Jm3cj{(Ad||97rA<> z8-9||bivsDv{G6=P)8;_x5_~J?}IP)>z!$Uoh7x)OfeLED!pi;_kCKaqURTI zFbMs>H~?4#h?@oe(Wu@5#6Sz!W_h-?nCTZl5)JuAZ1?z!d6Ccqj#^?6?kBJ;O9w}; z1MYIaIhh>^%Yew5uYjWIBj`XeS4i?f#uZ0f7k5z4V?0Mt2Z|XHQu)~Ab|3CQW)vZb zrc{^UF#_^WbtPbVJCinz0FlzVvV(yZ^^K3z&_Fn2NC7cYQlqq1K{e>;eOtGWm%y<^&_1Uik!O_ZtrGO->Jp8_RV+LM zKNS9Eu#-`0G8xp4?Kp4VKjHvvs8c0P$d7YO;W8G@J8I)P*O$>ZUdusDz~)fmVM6C! z;6l0IdVXGcb?&4A*L||J2uqO@(+101V7bPw>zW$8^1{Y-;b z<)|OP4@Rk)K~Zku6ZXc&y}$vSM4V|@9B-uwRfhJ78*hIp+%^8|%5@Zv$VTPi)dFwU z$7^Mp&e?T#ehW}I*v{GUC3*Z=yJ}YMb5IHYv$=_Bp#CP_wB(mCtA!A4041Et!`Ml; zxB2QgPugp|4XOWp(Z1T4V*JqfTM_+d42@KadQOCYz?C0YD9nAsvA5KwQ7nwbsCjK@ zY7-UL%yQ5x#PDOsUI(k_R4kD$<)j&`-lQ+Jrc{@5M$i#|<4RVAC$z6W<7nqemc>h+ zUUOUd9FDg*hO!1di4vP#xt}&CQ5UYD6?#kaIkjEz5Z5=~Z3VLt2~*iCQokLV?N_ZK z=GI}>+TikixwDBy?fr0up}(<;`%X`n>?>Q{|y97{sI61 literal 0 HcmV?d00001 diff --git a/src/color/test-images/output.png b/src/color/test-images/output.png new file mode 100644 index 0000000000000000000000000000000000000000..4111e5ba738dc76f131e02e3a3cee9fc2d02f42c GIT binary patch literal 2698 zcmds3X*d-68voCjF$~7egi|v@wwMOzlqQ47GH7gFQd!25?N}nD7&W4-6&DHPlBls3 zsg$u(wjxU@%h;D3hO#u~%!m7>5BKBwa-Zk-yuWvOe!us5zrDA}HfAD7IV1o;#KPRf z4gd%*g#ZBu%&#h%gIlQ^d5en2eg1!vM?*0KvN!CPogCpH}kv z{Lc+bcMxlI*Xt#Zjijbjk5Sc=Ng>APO(f+cRISx$bSrcPBCS>cNmuTcINqHz3i(W%r7&thA%@RyNnR*LR^i`Lb#ClPN7;&oSh z?tOi6$>7J>ZvMAqLjf#J%=p(3RiqsTPN>mP7|F##K6A+;ux%CnhawrP0{N*FqXRKb zG^IjuowoZ~ww5W^+WL)7IQSW6N*j2WRe!X=2zrRonHaNuGg9Ld!f86K)%{qnX2pUv zh|W1m{;R7XKn6>N6~a|P3!#fmc=rcihero7@?PWGkDIVOIW$S^3>A8V&?6cZ_nsa? zo8Zsd5*l!j{~Rc2B|R_t9V#{Nlbw}4p^$LHR6MnX(B}J?Q3YiJ`4pd>g}FD5=t7ve zB{%BAq)VJ)VjSygE@#XuQyR-qb0iY#+wl;o zDp`7gdrb2&HoLtiWWtqE;~duwN8|py7-y6}9e;k$Azr>c#6b8|#KY}~oT+>5EIx#xaK@N}G=4Q&QF2+7B0cWHriqQp*9{EyE`w zGAusl#Qhyw)LSLF2cvgj6st8s$JJRq}P))L?q8 zV;k(9g&X8(@6pqN(Vp@6#_4?bfYH!2wTeqLozDu-J9immAL>~@_3VXO>O;U`4j=97 z{Jq0bRfmGk5=lBvcDr%RAYe?)vVtGqC^O>o6Hj={`mAuLZ-cL!T=IdPh&301syGRe zJxb@;+t$Q@N+y~0?P6K2oI2al_A*pTPGm8hOB`QFSdlWnkXy_zymwBt$eTc$ zL-bv`;cxir)crxA0jVo!Z&)hk@^!QS?3>HYy8dt__G6ZEK2q#T+!aE(ULJqDqM;W; zvWOsV9ZeBu4gi4b+w8|GwJK5tXt3bae-bvD(5vG5pz#@+~wY8HBd&r$*ToAIG)!Sq#kdaLI- z4$%Oz&M>^y6{*F&FHA$xg}f$YHOV5sb)GD46^xN`-Jvi5@R|*EL|9tkWhpx>1`za` zh#0JY@kqnymiXtB!tHv5F`|alyUy;% zF4MN)<|C5YvloAbK9Mdjewt|$tFzc9x9O;8B7Ul|Dg%=YiMM{hlq^D}DW!*l$xs#hc6FSVv$`Mg; zrVh8CrS`q>AMdno_$aq~yzH4ud|~N&*```KyuQb0$a?Ig=4j=WDjBzw=jFNe3H25| zI~hYz&1!U;F7DLgnESo}jg6?(j%R7`+8~ru{CSuMULWO8-$;3`A zFlR0&AE{X9w4a&3IH4c(RG8u7iWm_j2VK)Tl^MJ4Zxh^HSLT8i%Kxq+)Xnm}-YGU|x&`@fdgYMPZR?22sgtEWYQ#fi=UP$i2gJ9dM?#=_MA z<1dX8=Mp62X_Fk;r^TCJ%#y0VyQTeHH8>gDA}~dXBb99oo9_^>eno>&WhQ43gA-tc zCZ$Ctw-{9lG~3R}HO~1RyRB!Y>DM{Rw<9L+EtE^y;<2-7=O)LilsPNwLH*pqzWTKu zQj;S2KO)IGAbbLtqC)RVAbKb@Q}&Ms?aC5n2ZJu{r0m2Vg}l^AUmg$IZ;!s4FDhyis|BBb zXy^jPbSN_T7N!xTSTv+g-B4EWgV}V#M&90rE60D3X}~FjUp!RrjF{gFsNy@$&5Ta0 z9b|dG{T#7pJ#fI1`VFa+_S`T1^rzRazx?PKIABBV?dv{GN*}^h4yxAKHR^4b1c?bA z0%1@1(!X)G2kCk9e#5UzlDu1x{jX8ay5|t9K;+ z`vtkb<3f5r$yyKX*W$f&Ksn6RDa_M7%uAmV>ct~~$Ki2@kKhj<`P~7J*Vj3!kJr<{ m;q-C14<0u)eh~x)dHQ-s{ck}L(IAT_02Zb;CKbkR*Z&4d`F0lo literal 0 HcmV?d00001 diff --git a/src/color/test-images/reference-default.png b/src/color/test-images/reference-default.png new file mode 100644 index 0000000000000000000000000000000000000000..cfc7c492a73d80b583398bfed29f6f77dd98d540 GIT binary patch literal 1363 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5FnKh;1l8sq>%tf5p{vF9_I<5 ziSi{ue!&c^yxtP+#u_#XF4eIOVdg8I&-G8gr9CPC;^~ba_D$hXe^SrDz|!pL;uunK z>&?~fqS*!lt%*yH9da`>WRv`$TJ?YX8jZ_=!RnJ!I>Nm^&1b(*VSjENw-bkAivWn2 zkQ!K|S=3$SzHe>QFNb}KO+i)3tIU>6^Sm9FwzyP{CGpp)*X!4PEXusI_5G^F*Q_iX zgJy4&ukGsjTc&iulXJ?ts^`H+zrJnen<(BffB)`ZQzUn*sCImgetvn`OZlhX0?*@X ze|)M{XWY8+<@5QhYD|R;ORb+5dnBLsWnx^qYV~>LfNdTPYIQ%aqJ0XT*&mMe4Ea@X_wx8TOYDu%G!pYt3NLB@4Wxz;saKu>yi&u19l0^ z{Lf|DJ&)xH!xeAo4fbMAmiIW`L@V!N@tV2%!JmBYp1FcRZBxB1{;zF#eeDoK?&N}h z(T!2MayA@@QJ?!iEMe=kH3xJL1azcV@C9%(=gb#hpwl9(Ex_ob$-ZFwN-lxq zc$N#B7&bC|()|9P>CY?%Gb4sq^$gJ}owaNR@dxF3fSOe9H)OIKF>Y{O^af9QSg?Z= zk1Q>v96p}IZ+uNDgT08S(T^Agq0j?N6Sy5_$x77NF?ebv6f<3Dt@xs+%;4E*QSZm_ zDLmmrC8I~U!+RrU0kI3$p0g<^3p_v1f8cgdhr{%1&p8x=n|^*y=X$W#^4?7c`QEQT znObscFqv%(zo!;aydz@s{y2J0)X9<>Gb!UuBy+wCZ| z7kVHNZ&TygaQYF`m5)cOGFkN=F|7T4gh^+;L-JC_Gn(2Bg%j8;zC|+5vCeE@pDlVI z_;0o@g8=K7Ps#;ZkDK4VH1=~~xKPA!+-~vygT>~|KCBZMIjlFt#x?qDA6PYw{X&1Y zcEPQ`iW%k0pR#tC2k<;ltl55)rS`<5bT?~J2Mz@}#@JiOo=;Hw!}{kgL#MQa_J@ic zKfdgg*~K8ht-!HirI7%~=ku%r>>Ny~r5<}(&)WPCY1qK%#ITVmHPwB-(c|D5@jDm< z6NDnlpO>g+oZHs-*;`CO&oT2>s8Z9jJ+B`9N#Yh@e5R^#cGp(_=Nl*PX`h~EV}J9) zzX@6M>f;zJ8+NcPHGI8F_KlZO*^K4$-_O@{6;}vT;QEs{oi9v+qfvrq^2#l*e%lvF z2AxcNFUkFRWA!w9tLJq)KYqLVzMA1YtHr@@eH&)RUtP~?!Wh2cLFW7TeZS^TXzEu` z_qg27URUw=-oNKe$%V|)SJyABv(8}3OW3+8?kDTtDwfP8PmP0IGWW|JG^}~V@cVSb zjrsH6`Ulj>KHyMi2!E2@)NoaHyZ(ki~8*y|K!4}BYzp1!KvHR)z4*}Q$iB} D=hNM* literal 0 HcmV?d00001 diff --git a/src/color/test-images/reference-white.png b/src/color/test-images/reference-white.png new file mode 100644 index 0000000000000000000000000000000000000000..861fce124adf741ce3242b1d275af9700830115c GIT binary patch literal 1393 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5FnKt;1l8sr2o?x%yQ*j0JKJ- zB*-tAftA-=qTN`-M!_Yvp*qaGa>et>>E~`~&r|)CzwyJYeHv#!2r@9R%=L6}45_&F z=FZ!^TLvNx4{u0va$1}fpWWHu+WgAH_eX4j2-}Ulu?@jWbK)8KCp;};s1na&ta-McAxi9k zenZXjXXOoF*aA*4tgvVF5mNcgmQcyM-@rkWN8k@rp1A;LgD2Bgc3|}301Z#{P-GrR zGL(KwKoP>jkr#b%`hPnI!^vh)@}w1Th+$Y*=eKU#Mh2EBMrH>chQz!D+ZbG!6H1ik z-E(JA(0uT`pYy=E&VtRo+zwT&U+jz+5*c%Iau|%5>i+#;SYyd4_+s)#LnaU1#+3bS$@i^pm2TZ? z;$Y^&YH;QfL%W_@7G{pVq;92=U!Rk-H`T@oa%LZrhi}Mc_ z?`OCp=D>E~?t#3&Tz|H+-U#``@N4PpYg}*Y1(y7t`q@8^@sG3y+lRGMZ*H}nGhOq~ zt2UZJk>Nkbi#uD)+HS4$s*i0rBK;!#!PAP34|mH{vp*1b5Llq0EKu-SpId?N0n4XP z62G`^rT)9kw1ZoK>4Wx!>1i`^b}U<1{Fz(7fqe&O&hO$Ksu|}?pFW=+!%)vs8J2r4 zw?lWvtgTe~DWM4fAo)_` literal 0 HcmV?d00001 From 35f9ab265e06a2b4011ab0f62d61e6982f0074d8 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Wed, 16 Dec 2020 23:48:32 +0100 Subject: [PATCH 5/6] feat(android): generate notification in white monochrome --- .../generate-manifest-notification-icons.js | 4 +++- .../drawable-hdpi/ic_stat_notification.png | Bin 1123 -> 601 bytes .../drawable-mdpi/ic_stat_notification.png | Bin 1327 -> 491 bytes .../drawable-xhdpi/ic_stat_notification.png | Bin 1457 -> 705 bytes .../drawable-xxhdpi/ic_stat_notification.png | Bin 2094 -> 949 bytes .../drawable-xxxhdpi/ic_stat_notification.png | Bin 2787 -> 1266 bytes 6 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/android/generate-manifest-notification-icons.js b/src/android/generate-manifest-notification-icons.js index 43dccac..95a6464 100644 --- a/src/android/generate-manifest-notification-icons.js +++ b/src/android/generate-manifest-notification-icons.js @@ -3,6 +3,7 @@ const mkdirp = require('mkdirp'); const androidManifestNotificationIcons = require('./AndroidManifest.notification-icons.json'); const resizeImage = require('../resize/resize-image'); +const monochromeImage = require('../color/monochrome-image'); // Generate Android Manifest icons given a manifest file. module.exports = async function generateManifestIcons(notificationIcon, manifest) { @@ -24,7 +25,8 @@ module.exports = async function generateManifestIcons(notificationIcon, manifest await mkdirp(path.dirname(targetPath)); results.icons.push(iconPathWithFileName); - return resizeImage(notificationIcon, targetPath, icon.size); + return resizeImage(notificationIcon, targetPath, icon.size) + .then(() => monochromeImage(targetPath, targetPath, 'white')); })); // Before writing the contents file, sort the contents (otherwise // they could be in a different order each time). diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-hdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-hdpi/ic_stat_notification.png index 7f7f27056391312d8f046b9e8930bd3c64a6345a..ace915f8e36b1edf326f59dd643d54badc5ad141 100644 GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>g1N2R zl9uOpn(V1YeBBl&V};jV5!oaCK=Zc~# zo1lf~+zX;bQVJp!yf3}Fe2{;6+On@-elyv>y7Z)P=c!6y_^Xzzopr0DAfAZ2$lO delta 1113 zcmV-f1g87h1mg&h8Gi-<007|tn3w>Wv)%rKYaW-50BSE-RU?{MJQ3%JV71J3y)V9x>( z2t)ED0JRN`3x9h)I``QovtNKz**>woxMs5DdYF2Gy`Nkrrmd%aW%49uyA95naYEwX zqIG%6F{2^zj$ekEz~m~Ej{)UoC@kGaeuCIqeg?7K#0BqoHx0Vv zU0Ifnr9pUSGg15`S9xw?ePai36_hKYTp)7^eqtT%g@0R#qvkPAJ#70+Pu5!C5>6l5 zSPJIVukT?oa--=l)hap#dM~t>rT`D*0=ZmIW|#*75{hEJ>H^E{SQ-Z4^r4L`wIh>B zB#L=~Q0%+L6+Msutw15*%;BeoIxc`Shc|b8b$t$yUtt-i5rTvXu#9L#iwFs#L>%C# z5GblMf`5czD^ZSOL1aJ-^yA7nluDO*(fZQX_sC+_24!d*qLAr0#=&sKK%oQW`WHnh zOFO;YVi@Ddq%5*Aw6ogG%C+~w8X8MI!`r|ri?Nd=UrPSPzR;t7HWE-8I!9)!1Uagu z0WeSrQJ0SUf0EKb8`!CdV5YGeV;Y4SQp}ET6n~(QX~-nT>q1$@c5muYfwW49fY}&K zD;IQj6^#V6(ilUBYsl|qkNFZ1!{FlRTlU=x5ym^U*eQ1_qr>7V%YeEZOdTt&nxr zfPaG6x2?gJ<1?CD+If)r2M0?5Juo;vBerd2OS7$)Ly$)9Lumv>p{STf_J(NROeFf& z;Ck=e?jF728{kY^A1vGZY1FhLcSZ*4aqQB_TcOSC5(|0!8Q2S&e2Ws$2gp|;v_QgFCkjuvjG0eJ f7w_iNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^> z;_2(k{)9(BOxHR#Np&$$=(DGbV~EA+w^wYvm;(h4e7t}AuFl5IksFL!#RJ6j7tU=} zh={6W&VRuA#>T-xAZP8KrloVsCW;6-`mmJen*A2s?zwjTEOX=kKP%4c35a^HqHDQH zS=j2^0)vlhUNHB?R+-0g&Xn9$uv$y?it5sLOV0UPS$ajUtFbbTK0Cpq*Y>YgRoa^) z$AmhpEntS<8DqRcEcdlSa+&cvYM(!6tnjT`%68%Wx5D{vcyiwG=DZQff6E_xoLSC^H}*JR#2zLwLk^u7 zzW+ckuyA_JV`k%KWMXmn|DQW-pKs!mV_qH7%4+4>8+XthH8_XO6K%bg5zcFupZ_)PApzJCezpQTF z3Df2`rmgSI+CJ)+USZ>x(ab$=)cD4@`MpugN1c+(to$+tjjw^>V9@+QtN5~7_HpgH z*T4YLY51UCaG8})Mn3JhM)hl8$Y|AlP|3e6n|d4=IGS}Iq*IQoRJ;ZTk!tk^g`CS$ z$;TASUjqY5sp^AV_GK168S$iJvZb%&%HPVBe~`+&B$jwgs`wQ!+@wn1i>F^=;*}PT zKPFc23K(wU#qUK@E-~^*3&kB1D|#=Sa*2sYS~UL^Fl;4?-wP#Q;*UAXm-9*>?~Qoj zYhdsK!Tz-bA1-DcK$iFV}&!qRh-JYYs?o?K`t}`F{3ov$u;UKL7sh^s_=94%_J1 zcn?V?W|q*84F?$*7;8OU978nDCnqp4xv8azStO(;KTl|2@YDO>a6o~BgO8K<@w11m z9v?ooySunKI;NEPtZ7hianjJUQ#90+5)#V zYo_T(<>WFa-i}xwv6D-2V^#0kxqF!fZrrw97t21qAZmZ&<7H=~x8>a3m3sP_@9k~x z3);MPytz}F`+c5m^|yDdLc7<$k7k=(aJT+}6o-rrhf=xV8`}athO&iI UGn{=)TR=(6)78&qol`;+01Za^mjD0& diff --git a/test/CordovaApp/platforms/android/src/main/res/drawable-xhdpi/ic_stat_notification.png b/test/CordovaApp/platforms/android/src/main/res/drawable-xhdpi/ic_stat_notification.png index 0b7ed92311dc78df2cd076023b955a8657817fe2..4f2c6ea5a1f6a0d87d60e9c6ce2b047a988ba3a1 100644 GIT binary patch literal 705 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 WZ@X_gB}sb#0000fJFu;B50}w2 z?v)QkU%40_aAsJ1AcN1qvHSVW)5@*4#BZ%BntH#Zqw}lc83vnq4{iH4EKm6Dy!Gcq zfkMM|Sx+ZQo*i}v_~Md_mh7)Pr>=9^xLcBc z;&Jya747CTz4qNa-+d>c;gZvZH+J*hOrL)_?cIqn@)VvP$K* zGt1ODna8*3tiG5eUTB$p@6+i__Jn9)C;|i1lHsauFZPCq5GqU!a37+#@d)iO!>tew(mL1iW zNsZsewP{)6LA!g;bMrRM6JBs~@|#m}j%5rDv6XtUJ=^~H%vsO+FS<+g9OHW9>1zHF zECO@qEMnhsXm<^J%!A~4rdpX3PAm#n~Jl^*_KXY&~3AET><`Iv* zI=Sz0u({UqA5(enhPeC{%P5{_s;t~ClRPW4Z(~NtJMp?j=NmY-R(4#IXJ*#>Wa`el z=wF;)fuzNWp2okXdY`Pjsx?fez1zjm^nqRelSDV$TC>B_Y}eYqoiFUvE5E65>80C0 z_FbXX6?dLlDO%qQbBYkDy7NFMzG$|o)gR}0b*ai*A5SSSIHO`hq=&2zlb-@#$7mE*&DZUIA&L(9YTHd-DKSmeg5?UftDnm{ Hr-UW|XM-B$ literal 2787 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 Date: Thu, 17 Dec 2020 00:19:30 +0100 Subject: [PATCH 6/6] feat(android): new usage param to generate notification --- README.md | 16 ++++++++++++++++ bin/app-icon.js | 12 +++++++++++- src/generate.js | 12 ++++++++++++ src/generate.specs.js | 17 +++++++++++++++++ src/validate-parameters.js | 7 +++++++ src/validate-parameters.specs.js | 16 ++++++++++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0df666f..5891420 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Icon management for Mobile Apps. Create icons, generate all required sizes, labe * [Generating Icons](#generating-icons) * [Labelling Icons](#labelling-icons) * [Adaptive Icons](#adaptive-icons) + * [Notification Icons](#notification-icons) * [Developer Guide](#developer-guide) * [Initial Setup](#initial-setup) * [Running Tests](#running-tests) @@ -180,6 +181,21 @@ To test how adaptive icons will look when animated, swiped, etc, the [Adaptive I Note that Adaptive Icons of *all* supported sizes are generated. However, we also generate the `res/mipmap-anydpi-v26/` adaptive icon. This is a large size icon which Android from v26 onwards will automatically rescale as needed to all other sizes. This technically makes the density specific icons redundant. The reason we generate both is to ensure that after `generate` is run, *all* icons in the project will be consistent. +### Notification Icons + +Support for [Notification Icons for Android](https://developer.android.com/guide/topics/ui/notifiers/notifications) is added. + +The current goals are: + +1. Notification Icons are 'opt in', they are not generated by default +2. Creating or generating notification icons is done via the `--notification-icons` flag + +Another guide about Android Notifications can be found [here](https://material.io/design/platform-guidance/android-notifications.html). + +To help you develop notification icons, the [Notification icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html) website by [Roman Nurik](https://twitter.com/romannurik) is very useful! + +Note that Notification Icons of *all* supported sizes are generated. + ## Developer Guide The only dependencies are Node 10 (or above) and Yarn. diff --git a/bin/app-icon.js b/bin/app-icon.js index aad3511..c85c629 100755 --- a/bin/app-icon.js +++ b/bin/app-icon.js @@ -45,15 +45,19 @@ program .option('-p, --platforms [optional]', "The platforms to generate icons for. Defaults to 'android,ios'", 'android,ios') .option('--background-icon [optional]', "The background icon path. Defaults to 'icon.background.png'") .option('--foreground-icon [optional]', "The foreground icon path. Defaults to 'icon.foreground.png'") - .option('--adaptive-icons [optional]', "Additionally, generate Android Adaptive Icon templates. Defaults to 'false'") + .option('--adaptive-icons [optional]', "Additionally, generate Android adaptive icons. Defaults to 'false'") + .option('--notification-icon [optional]', "The notification icon path. Defaults to 'notification.png'") + .option('--notification-icons [optional]', "Additionally, generate Android notification icons. Defaults to 'false'") .action(async (parameters) => { const { icon, backgroundIcon, foregroundIcon, + notificationIcon, search, platforms, adaptiveIcons, + notificationIcons, } = parameters; await imageMagickCheck(); @@ -67,14 +71,20 @@ program const checkPath = foregroundIcon || 'icon.foreground.png'; await errorIfMissing(checkPath, `Foreground icon file '${checkPath}' does not exist. Add the file or specify foreground icon with the '--foreground-icon' parameter.`); } + if (notificationIcons) { + const checkPath = notificationIcon || 'notification.png'; + await errorIfMissing(checkPath, `Notification icon file '${checkPath}' does not exist. Add the file or specify notification icon with the '--notification-icon' parameter.`); + } try { await generate({ sourceIcon: icon, backgroundIcon, foregroundIcon, + notificationIcon, searchRoot: search, platforms, adaptiveIcons, + notificationIcons, }); } catch (err) { console.error(chalk.red(`An error occurred generating the icons: ${err.message}`)); diff --git a/src/generate.js b/src/generate.js index 49c81ce..b4cd178 100644 --- a/src/generate.js +++ b/src/generate.js @@ -4,6 +4,7 @@ const generateIconsetIcons = require('./ios/generate-iconset-icons'); const findAndroidManifests = require('./android/find-android-manifests'); const generateManifestIcons = require('./android/generate-manifest-icons'); const generateManifestAdaptiveIcons = require('./android/generate-manifest-adaptive-icons'); +const generateManifestNotificationIcons = require('./android/generate-manifest-notification-icons'); const validateParameters = require('./validate-parameters'); module.exports = async function generate(parameters) { @@ -12,9 +13,11 @@ module.exports = async function generate(parameters) { sourceIcon, backgroundIcon, foregroundIcon, + notificationIcon, searchRoot, platforms, adaptiveIcons, + notificationIcons, } = validateParameters(parameters || {}); // Set up the results object. @@ -22,6 +25,7 @@ module.exports = async function generate(parameters) { iconsets: [], manifests: [], adaptiveIconManifests: [], + notificationIconManifests: [], }; const iconSets = await findIconsetFolders(searchRoot); @@ -57,6 +61,14 @@ module.exports = async function generate(parameters) { }); } + if (notificationIcons) { + const notifRes = await generateManifestNotificationIcons(notificationIcon, manifest); + results.notificationIconManifests.push({ manifest, icons: notifRes.icons }); + notifRes.icons.forEach((icon) => { + console.log(` ${chalk.green('✓')} Generated notification icon ${icon}`); + }); + } + return null; })); return results; diff --git a/src/generate.specs.js b/src/generate.specs.js index ae899df..f291d08 100644 --- a/src/generate.specs.js +++ b/src/generate.specs.js @@ -33,4 +33,21 @@ describe('generate', () => { expect(results.manifests.length).to.equal(3); expect(results.adaptiveIconManifests.length).to.equal(3); }); + + it('should be able to generate test app icons with notification icons included', async () => { + const parameters = { + sourceIcon: './test/icon.png', + notificationIcon: './test/notification.png', + searchPath: './', + notificationIcons: true, + }; + + // Delete all of the files we're expecting to create, then generate them. + const results = await generate(parameters); + // TODO: Check we found the manifests etc etc + expect(results).to.not.equal(null); + expect(results.iconsets.length).to.equal(3); + expect(results.manifests.length).to.equal(3); + expect(results.notificationIconManifests.length).to.equal(3); + }); }); diff --git a/src/validate-parameters.js b/src/validate-parameters.js index b8abc68..c817fb5 100644 --- a/src/validate-parameters.js +++ b/src/validate-parameters.js @@ -21,12 +21,19 @@ module.exports = function validateParameters(parameters) { const backgroundIcon = parameters.backgroundIcon || 'icon.background.png'; const foregroundIcon = parameters.foregroundIcon || 'icon.foreground.png'; + // Validate or assign the notification icons flag. + // Set default value for the notification icon path. + const notificationIcons = !!parameters.notificationIcons; + const notificationIcon = parameters.notificationIcon || 'notification.png'; + return { sourceIcon, backgroundIcon, foregroundIcon, + notificationIcon, searchRoot, platforms, adaptiveIcons, + notificationIcons, }; }; diff --git a/src/validate-parameters.specs.js b/src/validate-parameters.specs.js index 5ee2c26..3d9cc69 100644 --- a/src/validate-parameters.specs.js +++ b/src/validate-parameters.specs.js @@ -7,9 +7,11 @@ describe('validateParameters', () => { sourceIcon: 'icon.png', backgroundIcon: 'icon.background.png', foregroundIcon: 'icon.foreground.png', + notificationIcon: 'notification.png', searchRoot: './', platforms: 'android,ios', adaptiveIcons: false, + notificationIcons: false, }; } @@ -34,6 +36,13 @@ describe('validateParameters', () => { assert.strictEqual(parameters.foregroundIcon, 'icon.foreground.png'); }); + it('should provide a default notification icon', () => { + const params = validParameters(); + delete params.notificationIcon; + const parameters = validateParameters(params); + assert.strictEqual(parameters.notificationIcon, 'notification.png'); + }); + it('should provide a default search root', () => { const params = validParameters(); delete params.searchRoot; @@ -48,6 +57,13 @@ describe('validateParameters', () => { assert.strictEqual(parameters.adaptiveIcons, false); }); + it('should provide a default notification icons option', () => { + const params = validParameters(); + delete params.notificationIcons; + const parameters = validateParameters(params); + assert.strictEqual(parameters.notificationIcons, false); + }); + it('should provide a default set of platforms', () => { const params = validParameters(); delete params.platforms;