Skip to content

Commit

Permalink
Features/webp support for splashscreen (#1113)
Browse files Browse the repository at this point in the history
* - linting
- platform independent paths in testing
- addes some unittest
- remove duplication + add comments
- delete webp's if png's added, delete png's if webp' added.
- Update bin/templates/cordova/lib/prepare.js Co-authored-by: エリス <[email protected]>
- fix apache/cordova-plugin-splashscreen#257 webp support for android

* revert changes

* refactor: use source extension for target in getImageResourcePath

* fix(prepare): include more extensions in initial splash-screen resource map

* tests(prepare): quick-fix for tests

* backward slashes must be changed to forward slashes for fast-glob package.

Co-authored-by: Raphael von der Grün <[email protected]>
  • Loading branch information
PieterVanPoyer and raphinesse authored Dec 16, 2020
1 parent 55feadf commit 7428bd3
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 11 deletions.
44 changes: 33 additions & 11 deletions bin/templates/cordova/lib/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,12 @@ function default_versionCode (version) {
}

function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density : type), name);
return resourcePath;
// Use same extension as source with special case for 9-Patch files
const ext = sourceName.endsWith('.9.png')
? '.9.png' : path.extname(sourceName).toLowerCase();

const subDir = density ? `${type}-${density}` : type;
return path.join(resourcesDir, subDir, name + ext);
}

function getAdaptiveImageResourcePath (resourcesDir, type, density, name, sourceName) {
Expand All @@ -317,6 +318,15 @@ function getAdaptiveImageResourcePath (resourcesDir, type, density, name, source
return resourcePath;
}

function makeSplashCleanupMap (projectRoot, resourcesDir) {
// Build an initial resource map that deletes all existing splash screens
const existingSplashPaths = glob.sync(
`${resourcesDir.replace(/\\/g, '/')}/drawable-*/screen.{png,9.png,webp,jpg,jpeg}`,
{ cwd: projectRoot }
);
return makeCleanResourceMap(existingSplashPaths);
}

function updateSplashes (cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android');

Expand All @@ -326,7 +336,8 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
return;
}

var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'drawable', 'screen.png');
// Build an initial resource map that deletes all existing splash screens
const resourceMap = makeSplashCleanupMap(cordovaProject.root, platformResourcesDir);

var hadMdpi = false;
resources.forEach(function (resource) {
Expand All @@ -337,14 +348,14 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
hadMdpi = true;
}
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', resource.density, 'screen.png', path.basename(resource.src));
platformResourcesDir, 'drawable', resource.density, 'screen', path.basename(resource.src));
resourceMap[targetPath] = resource.src;
});

// There's no "default" drawable, so assume default == mdpi.
if (!hadMdpi && resources.defaultResource) {
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', 'mdpi', 'screen.png', path.basename(resources.defaultResource.src));
platformResourcesDir, 'drawable', 'mdpi', 'screen', path.basename(resources.defaultResource.src));
resourceMap[targetPath] = resources.defaultResource.src;
}

Expand All @@ -356,7 +367,8 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
const resourceMap = makeSplashCleanupMap(projectRoot, platformResourcesDir);

events.emit('verbose', 'Cleaning splash screens at ' + platformResourcesDir);

// No source paths are specified in the map, so updatePaths() will delete the target files.
Expand Down Expand Up @@ -546,13 +558,13 @@ function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResour
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
for (var density in android_icons) {
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher.png', path.basename(android_icons[density].src));
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher', path.basename(android_icons[density].src));
resourceMap[targetPath] = android_icons[density].src;
}

// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher.png', path.basename(default_icon.src));
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}

Expand Down Expand Up @@ -673,6 +685,16 @@ function mapImageResources (rootDir, subDir, type, resourceName) {
return pathMap;
}

/** Returns resource map that deletes all given paths */
function makeCleanResourceMap (resourcePaths) {
const pathMap = {};
resourcePaths.map(path.normalize)
.forEach(resourcePath => {
pathMap[resourcePath] = null;
});
return pathMap;
}

function updateFileResources (cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android');

Expand Down
137 changes: 137 additions & 0 deletions spec/unit/prepare.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ function mockGetIconItem (data) {
}, data);
}

/**
* Create a mock item from the getSplashScreen collection with the supplied updated data.
*
* @param {Object} data Changes to apply to the mock getSplashScreen item
*/
function mockGetSplashScreenItem (data) {
return Object.assign({}, {
src: undefined,
density: undefined
}, data);
}

describe('prepare', () => {
describe('updateIcons method', function () {
// Rewire
Expand Down Expand Up @@ -833,4 +845,129 @@ describe('prepare', () => {
).toBeResolved();
});
});

describe('updateSplashes method', function () {
// Rewire
let prepare;

// Spies
let emitSpy;
let updatePathsSpy;

// Mock Data
let cordovaProject;
let platformResourcesDir;

beforeEach(function () {
prepare = rewire('../../bin/templates/cordova/lib/prepare');

cordovaProject = {
root: '/mock',
projectConfig: {
path: '/mock/config.xml',
cdvNamespacePrefix: 'cdv'
},
locations: {
plugins: '/mock/plugins',
www: '/mock/www'
}
};
platformResourcesDir = PATH_RESOURCE;

emitSpy = jasmine.createSpy('emit');
prepare.__set__('events', {
emit: emitSpy
});

updatePathsSpy = jasmine.createSpy('updatePaths');
prepare.__set__('FileUpdater', {
updatePaths: updatePathsSpy
});

// mocking initial responses for mapImageResources
prepare.__set__('makeSplashCleanupMap', (rootDir, resourcesDir) => ({
[path.join(resourcesDir, 'drawable-mdpi/screen.png')]: null,
[path.join(resourcesDir, 'drawable-mdpi/screen.webp')]: null
}));
});

it('Test#001 : Should detect no defined splash screens.', function () {
const updateSplashes = prepare.__get__('updateSplashes');

// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [];
};

updateSplashes(cordovaProject, platformResourcesDir);

// The emit was called
expect(emitSpy).toHaveBeenCalled();

// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'This app does not have splash screens defined';
expect(actual).toEqual(expected);
});

it('Test#02 : Should update splash png icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');

// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [mockGetSplashScreenItem({
density: 'mdpi',
src: 'res/splash/android/mdpi-screen.png'
})];
};

updateSplashes(cordovaProject, platformResourcesDir);

// The emit was called
expect(emitSpy).toHaveBeenCalled();

// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'Updating splash screens at ' + PATH_RESOURCE;
expect(actual).toEqual(expected);

const actualResourceMap = updatePathsSpy.calls.argsFor(0)[0];
const expectedResourceMap = {};
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.png')] = 'res/splash/android/mdpi-screen.png';
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.webp')] = null;

expect(actualResourceMap).toEqual(expectedResourceMap);
});

it('Test#03 : Should update splash webp icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');

// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [mockGetSplashScreenItem({
density: 'mdpi',
src: 'res/splash/android/mdpi-screen.webp'
})];
};

// Creating Spies
updateSplashes(cordovaProject, platformResourcesDir);

// The emit was called
expect(emitSpy).toHaveBeenCalled();

// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'Updating splash screens at ' + PATH_RESOURCE;
expect(actual).toEqual(expected);

const actualResourceMap = updatePathsSpy.calls.argsFor(0)[0];

const expectedResourceMap = {};
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.webp')] = 'res/splash/android/mdpi-screen.webp';
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.png')] = null;

expect(actualResourceMap).toEqual(expectedResourceMap);
});
});
});

0 comments on commit 7428bd3

Please sign in to comment.