feat!: android 12 splash screen (#1441)
* chore!: remove old splashscreen logic * feat(splashscreen): add backwards compatibility * chore: remove unused method * chore: prefix splashscreen_background with cdv_ * feat: support android 12 splashscreen api configs * feat: improve & refactor the logic for android splashscreen api 12 * feat: splashscreen copy image resources * feat: splashscreen branding image & xml cleanup * fix: splashscreen cleanup & branding conditions * fix: splashscreen @color usage * feat: update default Apache Cordova splash screen * chore: add missing asf header * fix: splashscreen image size * chore: use Theme.SplashScreen.IconBackground as default parent to support windowSplashScreenIconBackgroundColor * fix: center default test image by correct pivot * fix: fs-extra copySync * feat: re-add AutoHideSplashScreen and SplashScreenDelay preference support * chore: move splashscreen into CordovaActivity * feat: support splashscreen.hide & centralize to SplashScreenPlugin * chore: cleanup SplashScreenPlugin * feat: support fade, default auto hide on onPageFinished, support delays, refactor * refactor: cleanup splash screen * refactor: cleanup remove unused import * chore: add show method as unsupported * test: create a spy on updateProjectSplashScreen * style: add ending new line * chore: improve logging to warn when image path is missing * chore: split windowSplashScreenAnimatedIcon and windowSplashScreenBrandingImage case and added branding warning * chore: improve when to display warning * fix: add splashscreen dependency to app as well * chore: move the core-splashscreen dep lower Co-authored-by: Niklas Merz <niklasmerz@linux.com>
3
cordova-js-src/platform.js
vendored
@ -36,6 +36,9 @@ module.exports = {
|
||||
// TODO: Extract this as a proper plugin.
|
||||
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
|
||||
|
||||
// Core Splash Screen
|
||||
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
|
||||
|
||||
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
|
||||
|
||||
// Inject a listener for the backbutton on the document.
|
||||
|
33
cordova-js-src/plugin/android/splashscreen.js
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
var splashscreen = {
|
||||
show: function () {
|
||||
console.log('"navigator.splashscreen.show()" is unsupported on Android.');
|
||||
},
|
||||
hide: function () {
|
||||
exec(null, null, 'CordovaSplashScreenPlugin', 'hide', []);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = splashscreen;
|
@ -80,6 +80,7 @@ android {
|
||||
dependencies {
|
||||
implementation "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
|
||||
implementation "androidx.webkit:webkit:${cordovaConfig.ANDROIDX_WEBKIT_VERSION}"
|
||||
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@
|
||||
"KOTLIN_VERSION": "1.5.21",
|
||||
"ANDROIDX_APP_COMPAT_VERSION": "1.4.2",
|
||||
"ANDROIDX_WEBKIT_VERSION": "1.4.0",
|
||||
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0-rc01",
|
||||
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.10",
|
||||
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
|
||||
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false
|
||||
|
@ -76,6 +76,14 @@ public class ConfigXmlParser {
|
||||
)
|
||||
);
|
||||
|
||||
pluginEntries.add(
|
||||
new PluginEntry(
|
||||
SplashScreenPlugin.PLUGIN_NAME,
|
||||
"org.apache.cordova.SplashScreenPlugin",
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
parse(action.getResources().getXml(id));
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
|
||||
/**
|
||||
* This class is the main Android activity that represents the Cordova
|
||||
@ -98,11 +99,16 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
protected ArrayList<PluginEntry> pluginEntries;
|
||||
protected CordovaInterfaceImpl cordovaInterface;
|
||||
|
||||
private SplashScreen splashScreen;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
// Handle the splash screen transition.
|
||||
splashScreen = SplashScreen.installSplashScreen(this);
|
||||
|
||||
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
|
||||
loadConfig();
|
||||
|
||||
@ -125,8 +131,6 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
// (as was the case in previous cordova versions)
|
||||
if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
|
||||
immersiveMode = true;
|
||||
// The splashscreen plugin needs the flags set before we're focused to prevent
|
||||
// the nav and title bars from flashing in.
|
||||
setImmersiveUiVisibility();
|
||||
} else {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
@ -153,6 +157,9 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
}
|
||||
cordovaInterface.onCordovaInit(appView.getPluginManager());
|
||||
|
||||
// Setup the splash screen based on preference settings
|
||||
cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen);
|
||||
|
||||
// Wire the hardware volume controls to control media if desired.
|
||||
String volumePref = preferences.getString("DefaultVolumeStream", "");
|
||||
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
|
||||
@ -526,5 +533,4 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
168
framework/src/org/apache/cordova/SplashScreenPlugin.java
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.core.splashscreen.SplashScreenViewProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
@SuppressLint("LongLogTag")
|
||||
public class SplashScreenPlugin extends CordovaPlugin {
|
||||
static final String PLUGIN_NAME = "CordovaSplashScreenPlugin";
|
||||
|
||||
// Default config preference values
|
||||
private static final boolean DEFAULT_AUTO_HIDE = true;
|
||||
private static final int DEFAULT_DELAY_TIME = -1;
|
||||
private static final boolean DEFAULT_FADE = true;
|
||||
private static final int DEFAULT_FADE_TIME = 500;
|
||||
|
||||
// Config preference values
|
||||
/**
|
||||
* @param boolean autoHide to auto splash screen (default=true)
|
||||
*/
|
||||
private boolean autoHide;
|
||||
/**
|
||||
* @param int delayTime in milliseconds (default=-1)
|
||||
*/
|
||||
private int delayTime;
|
||||
/**
|
||||
* @param int fade to fade out splash screen (default=true)
|
||||
*/
|
||||
private boolean isFadeEnabled;
|
||||
/**
|
||||
* @param int fadeDuration fade out duration in milliseconds (default=500)
|
||||
*/
|
||||
private int fadeDuration;
|
||||
|
||||
// Internal variables
|
||||
/**
|
||||
* @param boolean keepOnScreen flag to determine if the splash screen remains visible.
|
||||
*/
|
||||
private boolean keepOnScreen = true;
|
||||
|
||||
@Override
|
||||
protected void pluginInitialize() {
|
||||
// Auto Hide & Delay Settings
|
||||
autoHide = preferences.getBoolean("AutoHideSplashScreen", DEFAULT_AUTO_HIDE);
|
||||
delayTime = preferences.getInteger("SplashScreenDelay", DEFAULT_DELAY_TIME);
|
||||
LOG.d(PLUGIN_NAME, "Auto Hide: " + autoHide);
|
||||
if (delayTime != DEFAULT_DELAY_TIME) {
|
||||
LOG.d(PLUGIN_NAME, "Delay: " + delayTime + "ms");
|
||||
}
|
||||
|
||||
// Fade & Fade Duration
|
||||
isFadeEnabled = preferences.getBoolean("FadeSplashScreen", DEFAULT_FADE);
|
||||
fadeDuration = preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_TIME);
|
||||
LOG.d(PLUGIN_NAME, "Fade: " + isFadeEnabled);
|
||||
if (isFadeEnabled) {
|
||||
LOG.d(PLUGIN_NAME, "Fade Duration: " + fadeDuration + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(
|
||||
String action,
|
||||
JSONArray args,
|
||||
CallbackContext callbackContext
|
||||
) throws JSONException {
|
||||
if (action.equals("hide") && autoHide == false) {
|
||||
/*
|
||||
* The `.hide()` method can only be triggered if the `splashScreenAutoHide`
|
||||
* is set to `false`.
|
||||
*/
|
||||
keepOnScreen = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
switch (id) {
|
||||
case "setupSplashScreen":
|
||||
setupSplashScreen((SplashScreen) data);
|
||||
break;
|
||||
|
||||
case "onPageFinished":
|
||||
attemptCloseOnPageFinished();
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setupSplashScreen(SplashScreen splashScreen) {
|
||||
// Setup Splash Screen Delay
|
||||
splashScreen.setKeepOnScreenCondition(() -> keepOnScreen);
|
||||
|
||||
// auto hide splash screen when custom delay is defined.
|
||||
if (autoHide && delayTime != DEFAULT_DELAY_TIME) {
|
||||
Handler splashScreenDelayHandler = new Handler();
|
||||
splashScreenDelayHandler.postDelayed(() -> keepOnScreen = false, delayTime);
|
||||
}
|
||||
|
||||
// auto hide splash screen with default delay (-1) delay is controlled by the
|
||||
// `onPageFinished` message.
|
||||
|
||||
// If auto hide is disabled (false), the hiding of the splash screen must be determined &
|
||||
// triggered by the front-end code with the `navigator.splashscreen.hide()` method.
|
||||
|
||||
// Setup the fade
|
||||
splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
|
||||
@Override
|
||||
public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) {
|
||||
View splashScreenView = splashScreenViewProvider.getView();
|
||||
|
||||
splashScreenView
|
||||
.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(isFadeEnabled ? fadeDuration : 0)
|
||||
.setStartDelay(isFadeEnabled ? 0 : fadeDuration)
|
||||
.setInterpolator(new AccelerateInterpolator())
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
splashScreenViewProvider.remove();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void attemptCloseOnPageFinished() {
|
||||
if (autoHide && delayTime == DEFAULT_DELAY_TIME) {
|
||||
keepOnScreen = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -73,6 +73,8 @@ class Api {
|
||||
configXml: path.join(appRes, 'xml', 'config.xml'),
|
||||
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
|
||||
strings: path.join(appRes, 'values', 'strings.xml'),
|
||||
themes: path.join(appRes, 'values', 'themes.xml'),
|
||||
colors: path.join(appRes, 'values', 'colors.xml'),
|
||||
manifest: path.join(appMain, 'AndroidManifest.xml'),
|
||||
build: path.join(this.root, 'build'),
|
||||
javaSrc: path.join(appMain, 'java')
|
||||
|
364
lib/prepare.js
@ -62,16 +62,14 @@ module.exports.prepare = function (cordovaProject, options) {
|
||||
updateUserProjectGradlePropertiesConfig(this, args);
|
||||
|
||||
// Update own www dir with project's www assets and plugins' assets and js-files
|
||||
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
|
||||
// update project according to config.xml changes.
|
||||
return updateProjectAccordingTo(self._config, self.locations);
|
||||
}).then(function () {
|
||||
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
|
||||
}).then(function () {
|
||||
events.emit('verbose', 'Prepared android project successfully');
|
||||
});
|
||||
return Promise.resolve(updateWww(cordovaProject, this.locations))
|
||||
.then(() => updateProjectAccordingTo(self._config, self.locations))
|
||||
.then(function () {
|
||||
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
|
||||
}).then(function () {
|
||||
events.emit('verbose', 'Prepared android project successfully');
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {PlatformApi} project */
|
||||
@ -171,7 +169,6 @@ module.exports.clean = function (options) {
|
||||
return Promise.resolve().then(function () {
|
||||
cleanWww(projectRoot, self.locations);
|
||||
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
|
||||
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
|
||||
cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root));
|
||||
});
|
||||
};
|
||||
@ -267,19 +264,10 @@ function cleanWww (projectRoot, locations) {
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectAccordingTo (platformConfig, locations) {
|
||||
// Update app name by editing res/values/strings.xml
|
||||
const strings = xmlHelpers.parseElementtreeSync(locations.strings);
|
||||
updateProjectStrings(platformConfig, locations);
|
||||
updateProjectSplashScreen(platformConfig, locations);
|
||||
|
||||
const name = platformConfig.name();
|
||||
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
|
||||
|
||||
const shortName = platformConfig.shortName && platformConfig.shortName();
|
||||
if (shortName && shortName !== name) {
|
||||
strings.find('string[@name="launcher_name"]').text = shortName.replace(/'/g, '\\\'');
|
||||
}
|
||||
|
||||
fs.writeFileSync(locations.strings, strings.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
|
||||
|
||||
// Update app name for gradle project
|
||||
fs.writeFileSync(path.join(locations.root, 'cdv-gradle-name.gradle'),
|
||||
@ -342,6 +330,262 @@ function updateProjectAccordingTo (platformConfig, locations) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates project structure and AndroidManifest according to project's configuration.
|
||||
*
|
||||
* @param {ConfigParser} platformConfig A project's configuration that will
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectStrings (platformConfig, locations) {
|
||||
// Update app name by editing res/values/strings.xml
|
||||
const strings = xmlHelpers.parseElementtreeSync(locations.strings);
|
||||
|
||||
const name = platformConfig.name();
|
||||
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
|
||||
|
||||
const shortName = platformConfig.shortName && platformConfig.shortName();
|
||||
if (shortName && shortName !== name) {
|
||||
strings.find('string[@name="launcher_name"]').text = shortName.replace(/'/g, '\\\'');
|
||||
}
|
||||
|
||||
fs.writeFileSync(locations.strings, strings.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ConfigParser} platformConfig A project's configuration that will
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreen (platformConfig, locations) {
|
||||
// res/values/themes.xml
|
||||
const themes = xmlHelpers.parseElementtreeSync(locations.themes);
|
||||
const splashScreenTheme = themes.find('style[@name="Theme.App.SplashScreen"]');
|
||||
|
||||
[
|
||||
'windowSplashScreenAnimatedIcon',
|
||||
'windowSplashScreenAnimationDuration',
|
||||
'windowSplashScreenBackground',
|
||||
'windowSplashScreenBrandingImage',
|
||||
'windowSplashScreenIconBackgroundColor',
|
||||
'postSplashScreenTheme'
|
||||
].forEach(themeKey => {
|
||||
const cdvConfigPrefKey = 'Android' + themeKey.charAt(0).toUpperCase() + themeKey.slice(1);
|
||||
const cdvConfigPrefValue = platformConfig.getPreference(cdvConfigPrefKey, this.platform);
|
||||
let themeTargetNode = splashScreenTheme.find(`item[@name="${themeKey}"]`);
|
||||
|
||||
switch (themeKey) {
|
||||
case 'windowSplashScreenBackground':
|
||||
// use the user defined value for "colors.xml"
|
||||
updateProjectSplashScreenBackgroundColor(cdvConfigPrefValue, locations);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_background`
|
||||
themeTargetNode.text = '@color/cdv_splashscreen_background';
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenAnimatedIcon':
|
||||
// handle here the cases of "png" vs "xml" (drawable)
|
||||
// If "png":
|
||||
// - Clear out default or previous set "drawable/ic_cdv_splashscreen.xml" if exisiting.
|
||||
// - Copy png in correct mipmap dir with name "ic_cdv_splashscreen.png"
|
||||
// If "xml":
|
||||
// - Clear out "{mipmap}/ic_cdv_splashscreen.png" if exisiting.
|
||||
// - Copy xml into drawable dir with name "ic_cdv_splashscreen.xml"
|
||||
|
||||
// updateProjectSplashScreenIcon()
|
||||
// value should change depending on case:
|
||||
// If "png": "@mipmap/ic_cdv_splashscreen"
|
||||
// If "xml": "@drawable/ic_cdv_splashscreen"
|
||||
updateProjectSplashScreenImage(locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue);
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenBrandingImage':
|
||||
// display warning only when set.
|
||||
if (cdvConfigPrefValue) {
|
||||
events.emit('warn', `"${themeKey}" is currently not supported by the splash screen compatibility library. https://issuetracker.google.com/issues/194301890`);
|
||||
}
|
||||
|
||||
updateProjectSplashScreenImage(locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_icon_background`
|
||||
if (!cdvConfigPrefValue && themeTargetNode) {
|
||||
splashScreenTheme.remove(themeTargetNode);
|
||||
} else if (cdvConfigPrefValue) {
|
||||
// if there is no current node, create a new node.
|
||||
if (!themeTargetNode) {
|
||||
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey });
|
||||
splashScreenTheme.append(themeTargetNode);
|
||||
}
|
||||
|
||||
// set the user defined color.
|
||||
themeTargetNode.text = '@drawable/ic_cdv_splashscreen_branding';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenIconBackgroundColor':
|
||||
// use the user defined value for "colors.xml"
|
||||
updateProjectSplashScreenIconBackgroundColor(cdvConfigPrefValue, locations);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_icon_background`
|
||||
if (!cdvConfigPrefValue && themeTargetNode) {
|
||||
// currentItem.remove();
|
||||
splashScreenTheme.remove(themeTargetNode);
|
||||
} else if (cdvConfigPrefValue) {
|
||||
// if there is no current color, create a new node.
|
||||
if (!themeTargetNode) {
|
||||
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey });
|
||||
splashScreenTheme.append(themeTargetNode);
|
||||
}
|
||||
|
||||
// set the user defined color.
|
||||
themeTargetNode.text = '@color/cdv_splashscreen_icon_background';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenAnimationDuration':
|
||||
themeTargetNode.text = cdvConfigPrefValue || '200';
|
||||
break;
|
||||
|
||||
case 'postSplashScreenTheme':
|
||||
themeTargetNode.text = cdvConfigPrefValue || '@style/Theme.AppCompat.NoActionBar';
|
||||
break;
|
||||
|
||||
default:
|
||||
events.emit('warn', `The theme property "${themeKey}" does not exist`);
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFileSync(locations.themes, themes.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out Android application themes to ' + locations.themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} splashBackgroundColor SplashScreen Background Color Hex Code
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreenBackgroundColor (splashBackgroundColor, locations) {
|
||||
if (!splashBackgroundColor) { splashBackgroundColor = '#FFFFFF'; }
|
||||
|
||||
// res/values/colors.xml
|
||||
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
|
||||
colors.find('color[@name="cdv_splashscreen_background"]').text = splashBackgroundColor.replace(/'/g, '\\\'');
|
||||
|
||||
fs.writeFileSync(locations.colors, colors.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out Android application SplashScreen Color to ' + locations.colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} splashIconBackgroundColor SplashScreen Icon Background Color Hex Code
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreenIconBackgroundColor (splashIconBackgroundColor, locations) {
|
||||
// res/values/colors.xml
|
||||
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
|
||||
// node name
|
||||
const name = 'cdv_splashscreen_icon_background';
|
||||
|
||||
// get the current defined color
|
||||
let currentColor = colors.find(`color[@name="${name}"]`);
|
||||
|
||||
if (!splashIconBackgroundColor && currentColor) {
|
||||
colors.getroot().remove(currentColor);
|
||||
} else if (splashIconBackgroundColor) {
|
||||
// if there is no current color, create a new node.
|
||||
if (!currentColor) {
|
||||
currentColor = colors.getroot().makeelement('color', { name });
|
||||
colors.getroot().append(currentColor);
|
||||
}
|
||||
|
||||
// set the user defined color.
|
||||
currentColor.text = splashIconBackgroundColor.replace(/'/g, '\\\'');
|
||||
}
|
||||
|
||||
// write out the changes.
|
||||
fs.writeFileSync(locations.colors, colors.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out Android application SplashScreen Icon Color to ' + locations.colors);
|
||||
}
|
||||
|
||||
function cleanupAndSetProjectSplashScreenImage (srcFile, destFilePath, possiblePreviousDestFilePath, cleanupOnly = false) {
|
||||
if (fs.existsSync(possiblePreviousDestFilePath)) {
|
||||
fs.removeSync(possiblePreviousDestFilePath);
|
||||
}
|
||||
|
||||
if (cleanupOnly && fs.existsSync(destFilePath)) {
|
||||
// Also remove dest file path for cleanup even if previous was not use.
|
||||
fs.removeSync(destFilePath);
|
||||
}
|
||||
|
||||
if (!cleanupOnly && srcFile && fs.existsSync(srcFile)) {
|
||||
fs.copySync(srcFile, destFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue = '') {
|
||||
const SPLASH_SCREEN_IMAGE_BY_THEME_KEY = {
|
||||
windowSplashScreenAnimatedIcon: 'ic_cdv_splashscreen',
|
||||
windowSplashScreenBrandingImage: 'ic_cdv_splashscreen_branding'
|
||||
};
|
||||
|
||||
const destFileName = SPLASH_SCREEN_IMAGE_BY_THEME_KEY[themeKey] || null;
|
||||
if (!destFileName) throw new CordovaError(`${themeKey} is not valid for image detection.`);
|
||||
|
||||
// Default paths of where images are saved
|
||||
const destPngDir = path.join(locations.res, 'drawable-nodpi');
|
||||
const destXmlDir = path.join(locations.res, 'drawable');
|
||||
|
||||
// Dest File Name and Path
|
||||
const destFileNameExt = destFileName + '.xml';
|
||||
let destFilePath = path.join(destXmlDir, destFileNameExt);
|
||||
let possiblePreviousDestFilePath = path.join(destPngDir, destFileName + '.png');
|
||||
|
||||
// Default Drawable Source File
|
||||
const defaultSrcFilePath = themeKey !== 'windowSplashScreenBrandingImage'
|
||||
? require.resolve('cordova-android/templates/project/res/drawable/' + destFileNameExt)
|
||||
: null;
|
||||
|
||||
if (!cdvConfigPrefValue || !fs.existsSync(cdvConfigPrefValue)) {
|
||||
let emitType = 'verbose';
|
||||
let emmitMessage = `The "${cdvConfigPrefKey}" is undefined. Cordova's default will be used.`;
|
||||
|
||||
if (cdvConfigPrefValue && !fs.existsSync(cdvConfigPrefValue)) {
|
||||
emitType = 'warn';
|
||||
emmitMessage = `The "${cdvConfigPrefKey}" value does not exist. Cordova's default will be used.`;
|
||||
}
|
||||
|
||||
events.emit(emitType, emmitMessage);
|
||||
const cleanupOnly = themeKey === 'windowSplashScreenBrandingImage';
|
||||
cleanupAndSetProjectSplashScreenImage(defaultSrcFilePath, destFilePath, possiblePreviousDestFilePath, cleanupOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
const iconExtension = path.extname(cdvConfigPrefValue).toLowerCase();
|
||||
|
||||
if (iconExtension === '.png') {
|
||||
// Put the image at this location.
|
||||
destFilePath = path.join(destPngDir, destFileName + '.png');
|
||||
|
||||
// Check for this file and remove.
|
||||
possiblePreviousDestFilePath = path.join(destXmlDir, destFileName + '.xml');
|
||||
|
||||
// copy the png to correct mipmap folder with name of ic_cdv_splashscreen.png
|
||||
// delete ic_cdv_splashscreen.xml from drawable folder
|
||||
// update themes.xml windowSplashScreenAnimatedIcon value to @mipmap/ic_cdv_splashscreen
|
||||
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
|
||||
} else if (iconExtension === '.xml') {
|
||||
// copy the xml to drawable folder with name of ic_cdv_splashscreen.xml
|
||||
// delete ic_cdv_splashscreen.png from mipmap folder
|
||||
// update themes.xml windowSplashScreenAnimatedIcon value to @drawable/ic_cdv_splashscreen
|
||||
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
|
||||
} else {
|
||||
// use the default destFilePath & possiblePreviousDestFilePath, no update require.
|
||||
events.emit('warn', `The "${cdvConfigPrefKey}" had an unsupported extension. Cordova's default will be used.`);
|
||||
cleanupAndSetProjectSplashScreenImage(defaultSrcFilePath, destFilePath, possiblePreviousDestFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Consturct the default value for versionCode as
|
||||
// PATCH + MINOR * 100 + MAJOR * 10000
|
||||
// see http://developer.android.com/tools/publishing/versioning.html
|
||||
@ -380,68 +624,6 @@ 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) {
|
||||
const resources = cordovaProject.projectConfig.getSplashScreens('android');
|
||||
|
||||
// if there are no "splash" elements in config.xml
|
||||
if (resources.length === 0) {
|
||||
events.emit('verbose', 'This app does not have splash screens defined');
|
||||
// We must not return here!
|
||||
// If the user defines no splash screens, the cleanup map will cause any
|
||||
// existing splash screen images (e.g. the defaults that we copy into a
|
||||
// new app) to be removed from the app folder, which is what we want.
|
||||
}
|
||||
|
||||
// Build an initial resource map that deletes all existing splash screens
|
||||
const resourceMap = makeSplashCleanupMap(cordovaProject.root, platformResourcesDir);
|
||||
|
||||
let hadMdpi = false;
|
||||
resources.forEach(function (resource) {
|
||||
if (!resource.density) {
|
||||
return;
|
||||
}
|
||||
if (resource.density === 'mdpi') {
|
||||
hadMdpi = true;
|
||||
}
|
||||
const targetPath = getImageResourcePath(
|
||||
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) {
|
||||
const targetPath = getImageResourcePath(
|
||||
platformResourcesDir, 'drawable', 'mdpi', 'screen', path.basename(resources.defaultResource.src));
|
||||
resourceMap[targetPath] = resources.defaultResource.src;
|
||||
}
|
||||
|
||||
events.emit('verbose', 'Updating splash screens at ' + platformResourcesDir);
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
|
||||
const resources = projectConfig.getSplashScreens('android');
|
||||
if (resources.length > 0) {
|
||||
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.
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
|
||||
}
|
||||
}
|
||||
|
||||
function updateIcons (cordovaProject, platformResourcesDir) {
|
||||
const icons = cordovaProject.projectConfig.getIcons('android');
|
||||
|
||||
@ -521,7 +703,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
const android_icons = preparedIcons.android_icons;
|
||||
const default_icon = preparedIcons.default_icon;
|
||||
|
||||
// The source paths for icons and splashes are relative to
|
||||
// The source paths for icons are relative to
|
||||
// project's config.xml location, so we use it as base path.
|
||||
let background;
|
||||
let foreground;
|
||||
@ -620,7 +802,7 @@ function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResour
|
||||
const android_icons = preparedIcons.android_icons;
|
||||
const default_icon = preparedIcons.default_icon;
|
||||
|
||||
// The source paths for icons and splashes are relative to
|
||||
// The source paths for icons are relative to
|
||||
// project's config.xml location, so we use it as base path.
|
||||
for (const density in android_icons) {
|
||||
const targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher', path.basename(android_icons[density].src));
|
||||
@ -750,16 +932,6 @@ 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) {
|
||||
const files = cordovaProject.projectConfig.getFileResources('android');
|
||||
|
||||
|
@ -83,18 +83,6 @@ 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', () => {
|
||||
// Rewire
|
||||
let prepare;
|
||||
@ -779,7 +767,6 @@ describe('prepare', () => {
|
||||
prepare.__set__('updateProjectAccordingTo', jasmine.createSpy('updateProjectAccordingTo')
|
||||
.and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateIcons', jasmine.createSpy('updateIcons').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateSplashes', jasmine.createSpy('updateSplashes').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateFileResources', jasmine.createSpy('updateFileResources').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateConfigFilesFrom',
|
||||
jasmine.createSpy('updateConfigFilesFrom')
|
||||
@ -871,7 +858,7 @@ describe('prepare', () => {
|
||||
|
||||
prepare.__set__('updateWww', jasmine.createSpy('updateWww'));
|
||||
prepare.__set__('updateIcons', jasmine.createSpy('updateIcons').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateSplashes', jasmine.createSpy('updateSplashes').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateProjectSplashScreen', jasmine.createSpy('updateProjectSplashScreen'));
|
||||
prepare.__set__('updateFileResources', jasmine.createSpy('updateFileResources').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateConfigFilesFrom',
|
||||
jasmine.createSpy('updateConfigFilesFrom')
|
||||
@ -960,110 +947,4 @@ describe('prepare', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSplashes method', function () {
|
||||
// Mock Data
|
||||
let cordovaProject;
|
||||
let platformResourcesDir;
|
||||
|
||||
beforeEach(function () {
|
||||
cordovaProject = {
|
||||
root: '/mock',
|
||||
projectConfig: {
|
||||
path: '/mock/config.xml',
|
||||
cdvNamespacePrefix: 'cdv'
|
||||
},
|
||||
locations: {
|
||||
plugins: '/mock/plugins',
|
||||
www: '/mock/www'
|
||||
}
|
||||
};
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,7 @@
|
||||
package __ID__;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.apache.cordova.*;
|
||||
|
||||
public class __ACTIVITY__ extends CordovaActivity
|
||||
|
@ -35,7 +35,7 @@
|
||||
<activity android:name="__ACTIVITY__"
|
||||
android:label="@string/activity_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:theme="@style/Theme.App.SplashScreen"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
android:exported="true">
|
||||
|
@ -287,6 +287,7 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
implementation "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
|
||||
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
|
||||
|
||||
if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${cordovaConfig.KOTLIN_VERSION}"
|
||||
|
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 192 KiB |
34
templates/project/res/drawable/ic_cdv_splashscreen.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<group
|
||||
android:pivotX="243"
|
||||
android:pivotY="256"
|
||||
android:scaleX="0.5"
|
||||
android:scaleY="0.5">
|
||||
<path
|
||||
android:fillColor="#9D9D9C"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M425.22,448h-62.16l4.34,-54.86h-30.54L332.52,448H203.68l-4.34,-54.86H168.8l4.34,54.86h-62.16L76.1,210.22L163.38,64h209.44l87.28,146.22L425.22,448zM355.29,137.21h-56.02l3.79,27.43h-69.93l3.79,-27.43h-56.02l-35.06,73.02l17.53,146.22h209.44l17.53,-146.22L355.29,137.21zM324.75,308.02c-4.88,0 -8.67,-15.13 -8.67,-34.05s3.98,-34.05 8.67,-34.05c4.88,0 8.67,15.13 8.67,34.05S329.63,308.02 324.75,308.02zM214.7,310.86c-4.88,0 -8.67,-15.13 -8.67,-34.05s3.98,-34.05 8.67,-34.05c4.88,0 8.67,15.13 8.67,34.05S219.4,310.86 214.7,310.86z" />
|
||||
</group>
|
||||
</vector>
|
22
templates/project/res/values/colors.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<color name="cdv_splashscreen_background">#FFFFFFFF</color>
|
||||
</resources>
|
@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<resources>
|
||||
<!-- App label shown within list of installed apps, battery & network usage screens. -->
|
||||
<string name="app_name">__NAME__</string>
|
||||
|
34
templates/project/res/values/themes.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<resources>
|
||||
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen.IconBackground">
|
||||
<!-- Optional: Set the splash screen background. (Default: #FFFFFF) -->
|
||||
<item name="windowSplashScreenBackground">@color/cdv_splashscreen_background</item>
|
||||
|
||||
<!-- Required: Add either a drawable or an animated drawable -->
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_cdv_splashscreen</item>
|
||||
|
||||
<!-- Required: For animated icons -->
|
||||
<item name="windowSplashScreenAnimationDuration">200</item>
|
||||
|
||||
<!-- Required: Set the theme of the Activity that directly follows your splash screen. -->
|
||||
<item name="postSplashScreenTheme">@style/Theme.AppCompat.NoActionBar</item>
|
||||
</style>
|
||||
</resources>
|
@ -49,7 +49,6 @@
|
||||
|
||||
<preference name="loglevel" value="DEBUG" />
|
||||
<!--
|
||||
<preference name="splashscreen" value="splash" />
|
||||
<preference name="backgroundColor" value="0xFFF" />
|
||||
<preference name="loadUrlTimeoutValue" value="20000" />
|
||||
<preference name="InAppBrowserStorageEnabled" value="true" />
|
||||
|