Browse Source

Projucer: allow to specify custom content of AndroidManifest.xml

tags/2021-05-28
Lukasz Kozakiewicz 8 years ago
parent
commit
df1b43b704
2 changed files with 151 additions and 35 deletions
  1. +52
    -0
      BREAKING-CHANGES.txt
  2. +99
    -35
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h

+ 52
- 0
BREAKING-CHANGES.txt View File

@@ -45,6 +45,58 @@ rather than using a dedicated scrollbar. The scrollbar is still available though
needed.
Change
------
The previous setting of Android exporter "Custom manifest xml elements" creating
child nodes of <application> element has been replaced by "Custom manifest XML content"
setting that allows to specify the content of the entire manifest instead.
Any previously values of the old setting will be used in the new setting by default, and
they will need changing as mentioned in Workaround. The custom content will be merged
with the content auto-generated by Projucer. Any custom elements or custom attributes
will override the ones set by Projucer. Projucer will also automatically add any
missing and required elements and attributes.
Possible Issues
---------------
If a Projucer project used "Custom manifest xml elements" field, the value will no
longer be compatible with the project generated in the latest Projucer version. The solution
is very simple and quick though, as mentioned in the Workaround section.
Workaround
----------
For any elements previously used, simply embed them explicitly in <manifest><application>
elements,for example instead of:
<meta-data android:name="paramId1" android:value="paramValue1"/>
<meta-data android:name="paramId2" android:value="paramValue2"/>
simply write:
<manifest>
<application>
<meta-data android:name="paramId1" android:value="paramValue1"/>
<meta-data android:name="paramId2" android:value="paramValue2"/>
</application>
</manifest>
Rationale
---------
To maintain the high level of flexibility of generated Android projects and to avoid
creating fields in Projucer for every possible future parameter, it is simpler to allow to
set up the required parameters manually. This way it is not only possible to add any custom
elements but it is also possible to override the default attributes assigned by Projucer for
the required elements. For instance, if the default value of <supports-screens> element is
not satisfactory because you want a support for x-large screens only, simply set
"Custom manifest XML content" to:
<manifest>
<supports-screens android:xlargeScreens="true"/>
</manifest>
Version 5.1.2
=============


+ 99
- 35
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h View File

@@ -841,8 +841,10 @@ private:
props.add (new TextPropertyComponent (androidOtherPermissions.getPropertyAsValue(), "Custom permissions", 2048, false),
"A space-separated list of other permission flags that should be added to the manifest.");
props.add (new TextPropertyComponent (androidManifestCustomXmlElements.getPropertyAsValue(), "Custom manifest xml elements", 8192, true),
"You can specify custom XML elements that will be added to AndroidManifest.xml as children of <application> element.");
props.add (new TextPropertyComponent (androidManifestCustomXmlElements.getPropertyAsValue(), "Custom manifest XML content", 8192, true),
"You can specify custom AndroidManifest.xml content overriding the default one generated by Projucer. "
"Projucer will automatically create any missing and required XML elements and attributes "
"and merge them into your custom content.");
}
//==============================================================================
@@ -1366,28 +1368,39 @@ private:
//==============================================================================
XmlElement* createManifestXML() const
{
XmlElement* manifest = new XmlElement ("manifest");
XmlElement* manifest = XmlDocument::parse (androidManifestCustomXmlElements.get());
manifest->setAttribute ("xmlns:android", "http://schemas.android.com/apk/res/android");
manifest->setAttribute ("android:versionCode", androidVersionCode.get());
manifest->setAttribute ("android:versionName", project.getVersionString());
manifest->setAttribute ("package", getActivityClassPackage());
if (manifest == nullptr)
manifest = new XmlElement ("manifest");
setAttributeIfNotPresent (*manifest, "xmlns:android", "http://schemas.android.com/apk/res/android");
setAttributeIfNotPresent (*manifest, "android:versionCode", androidVersionCode.get());
setAttributeIfNotPresent (*manifest, "android:versionName", project.getVersionString());
setAttributeIfNotPresent (*manifest, "package", getActivityClassPackage());
if (! isLibrary())
{
XmlElement* screens = manifest->createNewChildElement ("supports-screens");
screens->setAttribute ("android:smallScreens", "true");
screens->setAttribute ("android:normalScreens", "true");
screens->setAttribute ("android:largeScreens", "true");
screens->setAttribute ("android:anyDensity", "true");
if (manifest->getChildByName ("supports-screens") == nullptr)
{
XmlElement* screens = manifest->createNewChildElement ("supports-screens");
screens->setAttribute ("android:smallScreens", "true");
screens->setAttribute ("android:normalScreens", "true");
screens->setAttribute ("android:largeScreens", "true");
screens->setAttribute ("android:anyDensity", "true");
}
}
XmlElement* sdk = manifest->createNewChildElement ("uses-sdk");
sdk->setAttribute ("android:minSdkVersion", androidMinimumSDK.get());
sdk->setAttribute ("android:targetSdkVersion", androidMinimumSDK.get());
auto* sdk = getOrCreateChildWithName (*manifest, "uses-sdk");
setAttributeIfNotPresent (*sdk, "android:minSdkVersion", androidMinimumSDK.get());
setAttributeIfNotPresent (*sdk, "android:targetSdkVersion", androidMinimumSDK.get());
{
const StringArray permissions (getPermissionsRequired());
StringArray permissions (getPermissionsRequired());
forEachXmlChildElementWithTagName (*manifest, child, "uses-permission")
{
permissions.removeString (child->getStringAttribute ("android:name"), false);
}
for (int i = permissions.size(); --i >= 0;)
manifest->createNewChildElement ("uses-permission")->setAttribute ("android:name", permissions[i]);
@@ -1395,19 +1408,33 @@ private:
if (project.getModules().isModuleEnabled ("juce_opengl"))
{
XmlElement* feature = manifest->createNewChildElement ("uses-feature");
feature->setAttribute ("android:glEsVersion", (androidMinimumSDK.get().getIntValue() >= 18 ? "0x00030000" : "0x00020000"));
feature->setAttribute ("android:required", "true");
XmlElement* glVersion = nullptr;
forEachXmlChildElementWithTagName (*manifest, child, "uses-feature")
{
if (child->getStringAttribute ("android:glEsVersion").isNotEmpty())
{
glVersion = child;
break;
}
}
if (glVersion == nullptr)
glVersion = manifest->createNewChildElement ("uses-feature");
setAttributeIfNotPresent (*glVersion, "android:glEsVersion", (androidMinimumSDK.get().getIntValue() >= 18 ? "0x00030000" : "0x00020000"));
setAttributeIfNotPresent (*glVersion, "android:required", "true");
}
if (! isLibrary())
{
XmlElement* app = manifest->createNewChildElement ("application");
app->setAttribute ("android:label", "@string/app_name");
auto* app = getOrCreateChildWithName (*manifest, "application");
setAttributeIfNotPresent (*app, "android:label", "@string/app_name");
if (androidTheme.get().isNotEmpty())
app->setAttribute ("android:theme", androidTheme.get());
setAttributeIfNotPresent (*app, "android:theme", androidTheme.get());
if (! app->hasAttribute ("android:icon"))
{
ScopedPointer<Drawable> bigIcon (getBigIcon()), smallIcon (getSmallIcon());
@@ -1417,29 +1444,66 @@ private:
if (androidMinimumSDK.get().getIntValue() >= 11)
app->setAttribute ("android:hardwareAccelerated", "false"); // (using the 2D acceleration slows down openGL)
else
app->removeAttribute ("android:hardwareAccelerated");
auto* act = getOrCreateChildWithName (*app, "activity");
setAttributeIfNotPresent (*act, "android:name", getActivitySubClassName());
setAttributeIfNotPresent (*act, "android:label", "@string/app_name");
if (! act->hasAttribute ("android:configChanges"))
{
String configChanges ("keyboardHidden|orientation");
if (androidMinimumSDK.get().getIntValue() >= 13)
configChanges += "|screenSize";
act->setAttribute ("android:configChanges", configChanges);
}
else
{
auto configChanges = act->getStringAttribute ("android:configChanges");
XmlElement* act = app->createNewChildElement ("activity");
act->setAttribute ("android:name", getActivitySubClassName());
act->setAttribute ("android:label", "@string/app_name");
if (androidMinimumSDK.get().getIntValue() < 13 && configChanges.contains ("screenSize"))
{
configChanges = configChanges.replace ("|screenSize", "")
.replace ("screenSize|", "")
.replace ("screenSize", "");
act->setAttribute ("android:configChanges", configChanges);
}
}
String configChanges ("keyboardHidden|orientation");
if (androidMinimumSDK.get().getIntValue() >= 13)
configChanges += "|screenSize";
setAttributeIfNotPresent (*act, "android:screenOrientation", androidScreenOrientation.get());
act->setAttribute ("android:configChanges", configChanges);
act->setAttribute ("android:screenOrientation", androidScreenOrientation.get());
auto* intent = getOrCreateChildWithName (*act, "intent-filter");
XmlElement* intent = act->createNewChildElement ("intent-filter");
intent->createNewChildElement ("action")->setAttribute ("android:name", "android.intent.action.MAIN");
intent->createNewChildElement ("category")->setAttribute ("android:name", "android.intent.category.LAUNCHER");
auto* action = getOrCreateChildWithName (*intent, "action");
setAttributeIfNotPresent (*action, "android:name", "android.intent.action.MAIN");
for (XmlElement* e = XmlDocument::parse (androidManifestCustomXmlElements.get()); e != nullptr; e = e->getNextElement())
app->addChildElement (e);
auto* category = getOrCreateChildWithName (*intent, "category");
setAttributeIfNotPresent (*category, "android:name", "android.intent.category.LAUNCHER");
}
return manifest;
}
static XmlElement* getOrCreateChildWithName (XmlElement& element, const String& name)
{
auto* child = element.getChildByName (name);
if (child == nullptr)
child = element.createNewChildElement (name);
return child;
}
static void setAttributeIfNotPresent (XmlElement& element, const Identifier& attribute, const String& value)
{
if (! element.hasAttribute (attribute.toString()))
element.setAttribute (attribute, value);
}
StringArray getPermissionsRequired() const
{
StringArray s;


Loading…
Cancel
Save