-
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support adaptive icons for 'generate' (#86)
- Loading branch information
Showing
101 changed files
with
553 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
{ | ||
"adaptiveIcons": [ | ||
{ | ||
"folder": "res/mipmap-ldpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "36x36" | ||
}, | ||
{ | ||
"folder": "res/mipmap-mdpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "48x48" | ||
}, | ||
{ | ||
"folder": "res/mipmap-hdpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "72x72" | ||
}, | ||
{ | ||
"folder": "res/mipmap-xhdpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "96x96" | ||
}, | ||
{ | ||
"folder": "res/mipmap-xxhdpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "144x144" | ||
}, | ||
{ | ||
"folder": "res/mipmap-xxxhdpi-v26", | ||
"backgroundIcon": "ic_launcher_background.png", | ||
"foregroundIcon": "ic_launcher_foreground.png", | ||
"size": "192x192" | ||
}, | ||
{ | ||
"comment": "Note that the anydpi folder will *not* contain icons - it contains a reference to the mipmaps for specific sizes", | ||
"folder": "res/mipmap-anydpi-v26" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const { EOL } = require('os'); | ||
const mkdirp = require('mkdirp'); | ||
|
||
const androidManifestAdaptiveIcons = require('./AndroidManifest.adaptive-icons.json'); | ||
const resizeImage = require('../resize/resize-image'); | ||
|
||
// The XML for the ic launcher manifest. | ||
// eslint-disable-next-line | ||
const icLauncherManifestXml = | ||
`<?xml version="1.0" encoding="utf-8"?>${EOL}` | ||
+ `<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">${EOL}` | ||
+ ` <background android:drawable="@mipmap/ic_launcher_background" />${EOL}` | ||
+ ` <foreground android:drawable="@mipmap/ic_launcher_foreground" />${EOL}` | ||
+ `</adaptive-icon>${EOL}`; | ||
|
||
// Generate Android Manifest icons given a manifest file. | ||
module.exports = function generateManifestIcons(backgroundIcon, foregroundIcon, 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. | ||
return Promise.all(androidManifestAdaptiveIcons.adaptiveIcons.map((icon) => { | ||
// Each icon lives in its own folder, so we'd better make sure that folder | ||
// exists. | ||
return new Promise((resolve, reject) => { | ||
const resourceFolder = path.join(manifestFolder, icon.folder); | ||
mkdirp(resourceFolder, (err) => { | ||
if (err) return reject(err); | ||
|
||
// Create the manifests, for the normal icons and round icons. | ||
fs.writeFileSync(path.join(resourceFolder, 'ic_launcher.xml'), icLauncherManifestXml, 'utf8'); | ||
fs.writeFileSync(path.join(resourceFolder, 'ic_launcher_round.xml'), icLauncherManifestXml, 'utf8'); | ||
|
||
const operations = []; | ||
// If the manifest requires us to generate icons for the folder, do so. | ||
// Not *every* folder has icons - for example the 'anydpi' folder will | ||
// not contain icons. | ||
if (icon.backgroundIcon) { | ||
const backgroundOutput = path.join(resourceFolder, icon.backgroundIcon); | ||
operations.push(resizeImage(backgroundIcon, backgroundOutput, icon.size)); | ||
results.icons.push(backgroundOutput); | ||
} | ||
if (icon.foregroundIcon) { | ||
const foregroundOutput = path.join(resourceFolder, icon.foregroundIcon); | ||
operations.push(resizeImage(foregroundIcon, foregroundOutput, icon.size)); | ||
results.icons.push(foregroundOutput); | ||
} | ||
return resolve(Promise.all(operations)); | ||
}); | ||
}); | ||
})).then(() => { | ||
// Before writing the contents file, sort the contents (otherwise | ||
// they could be in a different order each time). | ||
results.icons.sort(); | ||
return results; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
const { expect } = require('chai'); | ||
const path = require('path'); | ||
const deleteFolderIfExists = require('../utils/delete-folder-if-exists'); | ||
const generateManifestAdaptiveIcons = require('./generate-manifest-adaptive-icons'); | ||
const fileExists = require('../utils/file-exists'); | ||
|
||
const backgroundIcon = './test/icon.background.png'; | ||
const foregroundIcon = './test/icon.foreground.png'; | ||
|
||
// The folders we expect to generate, relative to the manifest location. | ||
const expectedFolders = [ | ||
'./res/mipmap-ldpi-v26', | ||
'./res/mipmap-hdpi-v26', | ||
'./res/mipmap-mdpi-v26', | ||
'./res/mipmap-xhdpi-v26', | ||
'./res/mipmap-xxhdpi-v26', | ||
'./res/mipmap-xxxhdpi-v26', | ||
'./res/mipmap-anydpi-v26', | ||
]; | ||
|
||
// The files we expect in each of the folders above. | ||
const expectedFiles = [ | ||
'./ic_launcher.xml', | ||
'./ic_launcher_round.xml', | ||
'./ic_launcher_background.png', | ||
'./ic_launcher_foreground.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-adaptive-icons', () => { | ||
// Run each test. | ||
testManifests.forEach(({ projectName, manifestPath }) => { | ||
it(`should be able to generate adaptive icons for the ${projectName} manifest`, () => { | ||
// 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; | ||
}, []); | ||
|
||
// A bit of a hack here - the 'anydpi' folder should not contain any images, | ||
// it just references the other mipmaps. So remove the anydpi folder images | ||
// from the expected set of files. | ||
const expectedPaths = resourceFoldersFiles.filter(f => !(/anydpi.*png$/.test(f))); | ||
console.log(`Len: ${resourceFoldersFiles.length}`); | ||
expectedPaths.forEach(f => console.log(`Expecting: ${f}`)); | ||
|
||
// Delete all of the folders we're expecting to create, then generate the icons. | ||
return Promise.all(resourceFolders.map(deleteFolderIfExists)) | ||
.then(() => ( | ||
generateManifestAdaptiveIcons(backgroundIcon, foregroundIcon, manifestPath) | ||
)) | ||
.then(() => Promise.all(expectedPaths.map(fileExists))) | ||
.then((filesDoExist) => { | ||
filesDoExist.forEach((exists, index) => { | ||
expect(exists, `${resourceFoldersFiles[index]} should be generated`).to.equal(true); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.